@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.
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -21
- package/README.md +51 -51
- package/astro.mjs +162 -162
- package/bin/morpheme.mjs +38 -38
- package/client.d.ts +10 -10
- package/defaults/blockTexts.ts +33 -33
- package/defaults/collections.ts +774 -774
- package/defaults/colors.ts +125 -125
- package/defaults/company.ts +20 -20
- package/defaults/editor.ts +178 -178
- package/defaults/internalLinks.ts +30 -30
- package/defaults/opens.ts +18 -18
- package/defaults/popup.tsx +93 -93
- package/defaults/privacy.ts +79 -79
- package/defaults/rules.ts +450 -450
- package/defaults/schema.ts +16 -16
- package/defaults/sitedata.ts +129 -129
- package/defaults/standard.ts +106 -106
- package/defaults/standardPages.ts +87 -87
- package/defaults/texts.ts +481 -481
- package/demo/README.md +26 -26
- package/demo/src/content/blog/build-a-page-from-blocks.md +19 -19
- package/demo/src/content/blog/colors-everyone-can-read.md +19 -19
- package/demo/src/content/blog/edit-your-website-from-your-phone.md +21 -21
- package/demo/src/content/blog/every-save-is-a-version.md +21 -21
- package/demo/src/content/blog/schedule-a-post.md +17 -17
- package/demo/src/content/blog/secure-from-day-one.md +17 -17
- package/demo/src/content/blog/seo-without-the-guesswork.md +23 -23
- package/demo/src/content/blog/switch-themes-without-losing-anything.md +23 -23
- package/demo/src/content/blog/the-app-store.md +21 -21
- package/demo/src/content/pages/about.md +22 -22
- package/demo/src/content/pages/app-store.md +30 -30
- package/demo/src/content/pages/blocks.md +98 -98
- package/demo/src/content/pages/blog-page.md +11 -11
- package/demo/src/content/pages/contact.md +16 -16
- package/demo/src/content/pages/get-started.md +20 -20
- package/demo/src/content/pages/home.md +26 -26
- package/demo/src/content/pages/page-not-found.md +11 -11
- package/demo/src/content/pages/privacy-policy.md +24 -24
- package/demo/src/data/blocks.json +25 -25
- package/demo/src/data/booking.json +6 -6
- package/demo/src/data/categories/apps.json +6 -6
- package/demo/src/data/categories/design.json +6 -6
- package/demo/src/data/categories/editing.json +6 -6
- package/demo/src/data/categories/security.json +6 -6
- package/demo/src/data/categories/seo.json +6 -6
- package/demo/src/data/colors.json +75 -75
- package/demo/src/data/contact.json +8 -8
- package/demo/src/data/cookies.json +21 -21
- package/demo/src/data/footer.json +27 -27
- package/demo/src/data/menu.json +35 -35
- package/demo/src/data/mobile-menu.json +25 -25
- package/demo/src/data/popups/booking-bar.json +13 -13
- package/demo/src/data/popups/try-the-editor.json +16 -16
- package/demo/src/data/standard-pages.json +8 -8
- package/demo/src/data/testimonials/sample-bakery.json +10 -10
- package/demo/src/data/testimonials/sample-clinic.json +10 -10
- package/demo/src/data/testimonials/sample-studio.json +10 -10
- package/demo/src/site/colors.ts +11 -11
- package/demo/src/site/config.ts +19 -19
- package/icons/lucide/LICENSE +43 -43
- package/icons/lucide/address-book.svg +1 -1
- package/icons/lucide/app-window.svg +1 -1
- package/icons/lucide/arrow-counter-clockwise.svg +1 -1
- package/icons/lucide/arrow-down.svg +1 -1
- package/icons/lucide/arrow-left.svg +1 -1
- package/icons/lucide/arrow-right.svg +1 -1
- package/icons/lucide/arrow-square-out.svg +1 -1
- package/icons/lucide/arrow-u-up-left.svg +1 -1
- package/icons/lucide/arrow-u-up-right.svg +1 -1
- package/icons/lucide/arrow-up-right.svg +1 -1
- package/icons/lucide/arrow-up.svg +1 -1
- package/icons/lucide/arrows-down-up.svg +1 -1
- package/icons/lucide/arrows-in-line-horizontal.svg +1 -1
- package/icons/lucide/arrows-out-line-horizontal.svg +1 -1
- package/icons/lucide/article.svg +1 -1
- package/icons/lucide/bank.svg +1 -1
- package/icons/lucide/bell.svg +1 -1
- package/icons/lucide/bicycle.svg +1 -1
- package/icons/lucide/binoculars.svg +1 -1
- package/icons/lucide/book-open-text.svg +1 -1
- package/icons/lucide/book-open.svg +1 -1
- package/icons/lucide/browser.svg +1 -1
- package/icons/lucide/buildings.svg +1 -1
- package/icons/lucide/bus.svg +1 -1
- package/icons/lucide/calendar-blank.svg +1 -1
- package/icons/lucide/calendar-check.svg +1 -1
- package/icons/lucide/car.svg +1 -1
- package/icons/lucide/cards.svg +1 -1
- package/icons/lucide/caret-down.svg +1 -1
- package/icons/lucide/caret-left.svg +1 -1
- package/icons/lucide/caret-right.svg +1 -1
- package/icons/lucide/caret-up.svg +1 -1
- package/icons/lucide/certificate.svg +1 -1
- package/icons/lucide/chart-line-up.svg +1 -1
- package/icons/lucide/chats.svg +1 -1
- package/icons/lucide/check-circle.svg +1 -1
- package/icons/lucide/check.svg +1 -1
- package/icons/lucide/circle-half.svg +1 -1
- package/icons/lucide/circle.svg +1 -1
- package/icons/lucide/clock-counter-clockwise.svg +1 -1
- package/icons/lucide/clock.svg +1 -1
- package/icons/lucide/code.svg +1 -1
- package/icons/lucide/coffee.svg +1 -1
- package/icons/lucide/columns.svg +1 -1
- package/icons/lucide/cookie.svg +1 -1
- package/icons/lucide/copy.svg +1 -1
- package/icons/lucide/credit-card.svg +1 -1
- package/icons/lucide/currency-circle-dollar.svg +1 -1
- package/icons/lucide/cursor-click.svg +1 -1
- package/icons/lucide/database.svg +1 -1
- package/icons/lucide/desktop.svg +1 -1
- package/icons/lucide/device-mobile.svg +1 -1
- package/icons/lucide/door-open.svg +1 -1
- package/icons/lucide/download-simple.svg +1 -1
- package/icons/lucide/envelope-simple.svg +1 -1
- package/icons/lucide/eye-slash.svg +1 -1
- package/icons/lucide/eye.svg +1 -1
- package/icons/lucide/file-pdf.svg +1 -1
- package/icons/lucide/file-text.svg +1 -1
- package/icons/lucide/file-video.svg +1 -1
- package/icons/lucide/file-zip.svg +1 -1
- package/icons/lucide/flower-lotus.svg +1 -1
- package/icons/lucide/folder-simple.svg +1 -1
- package/icons/lucide/funnel.svg +1 -1
- package/icons/lucide/gauge.svg +1 -1
- package/icons/lucide/gear.svg +1 -1
- package/icons/lucide/github-logo.svg +1 -1
- package/icons/lucide/globe.svg +1 -1
- package/icons/lucide/google-logo.svg +1 -1
- package/icons/lucide/graduation-cap.svg +1 -1
- package/icons/lucide/hammer.svg +1 -1
- package/icons/lucide/hand-heart.svg +1 -1
- package/icons/lucide/handshake.svg +1 -1
- package/icons/lucide/hard-drives.svg +1 -1
- package/icons/lucide/heart.svg +1 -1
- package/icons/lucide/house.svg +1 -1
- package/icons/lucide/image-square.svg +1 -1
- package/icons/lucide/image.svg +1 -1
- package/icons/lucide/info.svg +1 -1
- package/icons/lucide/keyboard.svg +1 -1
- package/icons/lucide/leaf.svg +1 -1
- package/icons/lucide/lifebuoy.svg +1 -1
- package/icons/lucide/lightbulb.svg +1 -1
- package/icons/lucide/link-break.svg +1 -1
- package/icons/lucide/link-simple.svg +1 -1
- package/icons/lucide/list-bullets.svg +1 -1
- package/icons/lucide/list-checks.svg +1 -1
- package/icons/lucide/list-numbers.svg +1 -1
- package/icons/lucide/list.svg +1 -1
- package/icons/lucide/lock-simple-open.svg +1 -1
- package/icons/lucide/lock-simple.svg +1 -1
- package/icons/lucide/magnifying-glass.svg +1 -1
- package/icons/lucide/map-pin.svg +1 -1
- package/icons/lucide/map-trifold.svg +1 -1
- package/icons/lucide/medal.svg +1 -1
- package/icons/lucide/megaphone.svg +1 -1
- package/icons/lucide/minus.svg +1 -1
- package/icons/lucide/moon.svg +1 -1
- package/icons/lucide/newspaper.svg +1 -1
- package/icons/lucide/pack.json +23 -23
- package/icons/lucide/paint-brush-broad.svg +1 -1
- package/icons/lucide/palette.svg +1 -1
- package/icons/lucide/paper-plane-tilt.svg +1 -1
- package/icons/lucide/pencil-simple.svg +1 -1
- package/icons/lucide/phone.svg +1 -1
- package/icons/lucide/play-fill.svg +1 -1
- package/icons/lucide/plus.svg +1 -1
- package/icons/lucide/prohibit.svg +1 -1
- package/icons/lucide/puzzle-piece.svg +1 -1
- package/icons/lucide/question.svg +1 -1
- package/icons/lucide/quotes.svg +1 -1
- package/icons/lucide/rows.svg +1 -1
- package/icons/lucide/ruler.svg +1 -1
- package/icons/lucide/scan.svg +1 -1
- package/icons/lucide/shapes.svg +1 -1
- package/icons/lucide/share-network.svg +1 -1
- package/icons/lucide/shield-check.svg +1 -1
- package/icons/lucide/sidebar-simple.svg +1 -1
- package/icons/lucide/sign-out.svg +1 -1
- package/icons/lucide/signpost.svg +1 -1
- package/icons/lucide/sliders-horizontal.svg +1 -1
- package/icons/lucide/smiley.svg +1 -1
- package/icons/lucide/sparkle.svg +1 -1
- package/icons/lucide/square-half-bottom.svg +1 -1
- package/icons/lucide/square.svg +1 -1
- package/icons/lucide/squares-four.svg +1 -1
- package/icons/lucide/sticker.svg +1 -1
- package/icons/lucide/storefront.svg +1 -1
- package/icons/lucide/sun.svg +1 -1
- package/icons/lucide/swatches.svg +1 -1
- package/icons/lucide/tag.svg +1 -1
- package/icons/lucide/text-aa.svg +1 -1
- package/icons/lucide/text-align-left.svg +1 -1
- package/icons/lucide/text-b.svg +1 -1
- package/icons/lucide/text-h-one.svg +1 -1
- package/icons/lucide/text-h-three.svg +1 -1
- package/icons/lucide/text-h-two.svg +1 -1
- package/icons/lucide/text-italic.svg +1 -1
- package/icons/lucide/text-t.svg +1 -1
- package/icons/lucide/ticket.svg +1 -1
- package/icons/lucide/toggle-right.svg +1 -1
- package/icons/lucide/train.svg +1 -1
- package/icons/lucide/trash.svg +1 -1
- package/icons/lucide/upload-simple.svg +1 -1
- package/icons/lucide/user-circle.svg +1 -1
- package/icons/lucide/user.svg +1 -1
- package/icons/lucide/users-three.svg +1 -1
- package/icons/lucide/users.svg +1 -1
- package/icons/lucide/video-camera.svg +1 -1
- package/icons/lucide/warning-circle.svg +1 -1
- package/icons/lucide/warning.svg +1 -1
- package/icons/lucide/wave-sine.svg +1 -1
- package/icons/lucide/wheelchair.svg +1 -1
- package/icons/lucide/wrench.svg +1 -1
- package/icons/lucide/x.svg +1 -1
- package/icons/material-symbols/LICENSE +201 -201
- package/icons/material-symbols/address-book.svg +1 -1
- package/icons/material-symbols/app-window.svg +1 -1
- package/icons/material-symbols/arrow-counter-clockwise.svg +1 -1
- package/icons/material-symbols/arrow-down.svg +1 -1
- package/icons/material-symbols/arrow-left.svg +1 -1
- package/icons/material-symbols/arrow-right.svg +1 -1
- package/icons/material-symbols/arrow-square-out.svg +1 -1
- package/icons/material-symbols/arrow-u-up-left.svg +1 -1
- package/icons/material-symbols/arrow-u-up-right.svg +1 -1
- package/icons/material-symbols/arrow-up-right.svg +1 -1
- package/icons/material-symbols/arrow-up.svg +1 -1
- package/icons/material-symbols/arrows-down-up.svg +1 -1
- package/icons/material-symbols/arrows-in-line-horizontal.svg +1 -1
- package/icons/material-symbols/arrows-out-line-horizontal.svg +1 -1
- package/icons/material-symbols/article.svg +1 -1
- package/icons/material-symbols/bank.svg +1 -1
- package/icons/material-symbols/bell.svg +1 -1
- package/icons/material-symbols/bicycle.svg +1 -1
- package/icons/material-symbols/binoculars.svg +1 -1
- package/icons/material-symbols/book-open-text.svg +1 -1
- package/icons/material-symbols/book-open.svg +1 -1
- package/icons/material-symbols/browser.svg +1 -1
- package/icons/material-symbols/buildings.svg +1 -1
- package/icons/material-symbols/bus.svg +1 -1
- package/icons/material-symbols/calendar-blank.svg +1 -1
- package/icons/material-symbols/calendar-check.svg +1 -1
- package/icons/material-symbols/car.svg +1 -1
- package/icons/material-symbols/cards.svg +1 -1
- package/icons/material-symbols/caret-down.svg +1 -1
- package/icons/material-symbols/caret-left.svg +1 -1
- package/icons/material-symbols/caret-right.svg +1 -1
- package/icons/material-symbols/caret-up.svg +1 -1
- package/icons/material-symbols/certificate.svg +1 -1
- package/icons/material-symbols/chart-line-up.svg +1 -1
- package/icons/material-symbols/chats.svg +1 -1
- package/icons/material-symbols/check-circle.svg +1 -1
- package/icons/material-symbols/check.svg +1 -1
- package/icons/material-symbols/circle-half.svg +1 -1
- package/icons/material-symbols/circle.svg +1 -1
- package/icons/material-symbols/clock-counter-clockwise.svg +1 -1
- package/icons/material-symbols/clock.svg +1 -1
- package/icons/material-symbols/code.svg +1 -1
- package/icons/material-symbols/coffee.svg +1 -1
- package/icons/material-symbols/columns.svg +1 -1
- package/icons/material-symbols/cookie.svg +1 -1
- package/icons/material-symbols/copy.svg +1 -1
- package/icons/material-symbols/credit-card.svg +1 -1
- package/icons/material-symbols/currency-circle-dollar.svg +1 -1
- package/icons/material-symbols/cursor-click.svg +1 -1
- package/icons/material-symbols/database.svg +1 -1
- package/icons/material-symbols/desktop.svg +1 -1
- package/icons/material-symbols/device-mobile.svg +1 -1
- package/icons/material-symbols/door-open.svg +1 -1
- package/icons/material-symbols/download-simple.svg +1 -1
- package/icons/material-symbols/envelope-simple.svg +1 -1
- package/icons/material-symbols/eye-slash.svg +1 -1
- package/icons/material-symbols/eye.svg +1 -1
- package/icons/material-symbols/file-pdf.svg +1 -1
- package/icons/material-symbols/file-text.svg +1 -1
- package/icons/material-symbols/file-video.svg +1 -1
- package/icons/material-symbols/file-zip.svg +1 -1
- package/icons/material-symbols/flower-lotus.svg +1 -1
- package/icons/material-symbols/folder-simple.svg +1 -1
- package/icons/material-symbols/funnel.svg +1 -1
- package/icons/material-symbols/gauge.svg +1 -1
- package/icons/material-symbols/gear.svg +1 -1
- package/icons/material-symbols/github-logo.svg +1 -1
- package/icons/material-symbols/globe.svg +1 -1
- package/icons/material-symbols/google-logo.svg +1 -1
- package/icons/material-symbols/graduation-cap.svg +1 -1
- package/icons/material-symbols/hammer.svg +1 -1
- package/icons/material-symbols/hand-heart.svg +1 -1
- package/icons/material-symbols/handshake.svg +1 -1
- package/icons/material-symbols/hard-drives.svg +1 -1
- package/icons/material-symbols/heart.svg +1 -1
- package/icons/material-symbols/house.svg +1 -1
- package/icons/material-symbols/image-square.svg +1 -1
- package/icons/material-symbols/image.svg +1 -1
- package/icons/material-symbols/info.svg +1 -1
- package/icons/material-symbols/keyboard.svg +1 -1
- package/icons/material-symbols/leaf.svg +1 -1
- package/icons/material-symbols/lifebuoy.svg +1 -1
- package/icons/material-symbols/lightbulb.svg +1 -1
- package/icons/material-symbols/link-break.svg +1 -1
- package/icons/material-symbols/link-simple.svg +1 -1
- package/icons/material-symbols/list-bullets.svg +1 -1
- package/icons/material-symbols/list-checks.svg +1 -1
- package/icons/material-symbols/list-numbers.svg +1 -1
- package/icons/material-symbols/list.svg +1 -1
- package/icons/material-symbols/lock-simple-open.svg +1 -1
- package/icons/material-symbols/lock-simple.svg +1 -1
- package/icons/material-symbols/magnifying-glass.svg +1 -1
- package/icons/material-symbols/map-pin.svg +1 -1
- package/icons/material-symbols/map-trifold.svg +1 -1
- package/icons/material-symbols/medal.svg +1 -1
- package/icons/material-symbols/megaphone.svg +1 -1
- package/icons/material-symbols/minus.svg +1 -1
- package/icons/material-symbols/moon.svg +1 -1
- package/icons/material-symbols/newspaper.svg +1 -1
- package/icons/material-symbols/pack.json +29 -29
- package/icons/material-symbols/paint-brush-broad.svg +1 -1
- package/icons/material-symbols/palette.svg +1 -1
- package/icons/material-symbols/paper-plane-tilt.svg +1 -1
- package/icons/material-symbols/pencil-simple.svg +1 -1
- package/icons/material-symbols/phone.svg +1 -1
- package/icons/material-symbols/play-fill.svg +1 -1
- package/icons/material-symbols/plus.svg +1 -1
- package/icons/material-symbols/prohibit.svg +1 -1
- package/icons/material-symbols/puzzle-piece.svg +1 -1
- package/icons/material-symbols/question.svg +1 -1
- package/icons/material-symbols/quotes.svg +1 -1
- package/icons/material-symbols/rows.svg +1 -1
- package/icons/material-symbols/ruler.svg +1 -1
- package/icons/material-symbols/scan.svg +1 -1
- package/icons/material-symbols/shapes.svg +1 -1
- package/icons/material-symbols/share-network.svg +1 -1
- package/icons/material-symbols/shield-check.svg +1 -1
- package/icons/material-symbols/sidebar-simple.svg +1 -1
- package/icons/material-symbols/sign-out.svg +1 -1
- package/icons/material-symbols/signpost.svg +1 -1
- package/icons/material-symbols/sliders-horizontal.svg +1 -1
- package/icons/material-symbols/smiley.svg +1 -1
- package/icons/material-symbols/sparkle.svg +1 -1
- package/icons/material-symbols/square-half-bottom.svg +1 -1
- package/icons/material-symbols/square.svg +1 -1
- package/icons/material-symbols/squares-four.svg +1 -1
- package/icons/material-symbols/sticker.svg +1 -1
- package/icons/material-symbols/storefront.svg +1 -1
- package/icons/material-symbols/sun.svg +1 -1
- package/icons/material-symbols/swatches.svg +1 -1
- package/icons/material-symbols/tag.svg +1 -1
- package/icons/material-symbols/text-aa.svg +1 -1
- package/icons/material-symbols/text-align-left.svg +1 -1
- package/icons/material-symbols/text-b.svg +1 -1
- package/icons/material-symbols/text-h-one.svg +1 -1
- package/icons/material-symbols/text-h-three.svg +1 -1
- package/icons/material-symbols/text-h-two.svg +1 -1
- package/icons/material-symbols/text-italic.svg +1 -1
- package/icons/material-symbols/text-t.svg +1 -1
- package/icons/material-symbols/ticket.svg +1 -1
- package/icons/material-symbols/toggle-right.svg +1 -1
- package/icons/material-symbols/train.svg +1 -1
- package/icons/material-symbols/trash.svg +1 -1
- package/icons/material-symbols/upload-simple.svg +1 -1
- package/icons/material-symbols/user-circle.svg +1 -1
- package/icons/material-symbols/user.svg +1 -1
- package/icons/material-symbols/users-three.svg +1 -1
- package/icons/material-symbols/users.svg +1 -1
- package/icons/material-symbols/video-camera.svg +1 -1
- package/icons/material-symbols/warning-circle.svg +1 -1
- package/icons/material-symbols/warning.svg +1 -1
- package/icons/material-symbols/wave-sine.svg +1 -1
- package/icons/material-symbols/wheelchair.svg +1 -1
- package/icons/material-symbols/wrench.svg +1 -1
- package/icons/material-symbols/x.svg +1 -1
- package/icons/phosphor/pack.json +8 -8
- package/lib/apps.mjs +142 -139
- package/lib/apps.test.d.mts +1 -0
- package/lib/apps.test.mjs +50 -0
- package/lib/blocks.mjs +90 -90
- package/lib/caseCollisions.d.mts +4 -0
- package/lib/caseCollisions.mjs +62 -0
- package/lib/caseCollisions.test.d.mts +1 -0
- package/lib/caseCollisions.test.mjs +74 -0
- package/lib/colors.ts +86 -86
- package/lib/contactBar.ts +18 -18
- package/lib/contracts.d.mts +15 -0
- package/lib/contracts.mjs +17 -0
- package/lib/cookieCategories.mjs +28 -28
- package/lib/crop.mjs +83 -83
- package/lib/csp.mjs +130 -130
- package/lib/cspSources.mjs +60 -60
- package/lib/dynamic-colors.ts +426 -426
- package/lib/git.mjs +64 -64
- package/lib/hooks.ts +51 -51
- package/lib/inline.ts +20 -20
- package/lib/license-texts/Apache-2.0.txt +190 -190
- package/lib/license-texts/BSD-2-Clause.txt +24 -24
- package/lib/license-texts/BSD-3-Clause.txt +28 -28
- package/lib/license-texts/ISC.txt +15 -15
- package/lib/license-texts/MIT.txt +21 -21
- package/lib/licenses.d.mts +1 -1
- package/lib/licenses.mjs +683 -683
- package/lib/markdown.ts +42 -42
- package/lib/motion-schemes.mjs +5 -5
- package/lib/motion.ts +131 -131
- package/lib/notice.mjs +34 -34
- package/lib/scheduled.mjs +37 -37
- package/lib/snackbar.ts +149 -149
- package/lib/status.mjs +18 -18
- package/lib/storeEntries.d.mts +18 -0
- package/lib/storeEntries.mjs +64 -0
- package/lib/storeEntries.test.d.mts +1 -0
- package/lib/storeEntries.test.mjs +71 -0
- package/lib/svg.mjs +231 -231
- package/lib/text-colors.mjs +43 -43
- package/lib/video-id.mjs +72 -72
- package/lib/video.mjs +91 -91
- package/package.json +136 -131
- package/pages/404.astro +10 -10
- package/pages/[...slug].astro +51 -51
- package/pages/_headers.txt +31 -31
- package/pages/blog/index.astro +10 -10
- package/pages/blog/kategori/[category].astro +18 -18
- package/pages/editor/code-images.json.js +25 -25
- package/pages/editor/index.astro +75 -75
- package/pages/editor/manifest.webmanifest.js +23 -23
- package/pages/editor/sw.js +33 -33
- package/pages/editor/sw.js.js +4 -4
- package/pages/editor/thumbnails.json.js +21 -21
- package/pages/editor/version.json.js +22 -22
- package/pages/headers.js +4 -4
- package/pages/index.astro +10 -10
- package/pages/ny.astro +6 -6
- package/pages/robots.txt.js +3 -3
- package/pages/rss.xml.js +20 -20
- package/scripts/app-screenshots.mjs +164 -164
- package/scripts/app-workers.mjs +6 -6
- package/scripts/bin.mjs +12 -12
- package/scripts/built-site.mjs +134 -99
- package/scripts/check-consent.mjs +113 -113
- package/scripts/check-content.mjs +151 -149
- package/scripts/check-demo.mjs +24 -24
- package/scripts/check-store.mjs +160 -0
- package/scripts/check-themes.mjs +45 -45
- package/scripts/check-worker.mjs +569 -569
- package/scripts/compare-html.mjs +81 -81
- package/scripts/declarations.mjs +10 -0
- package/scripts/declarations.test.mjs +32 -0
- package/scripts/demo-site.mjs +42 -42
- package/scripts/editor-icons.mjs +56 -56
- package/scripts/editor-roundtrip.mjs +134 -134
- package/scripts/editor-screens.mjs +101 -101
- package/scripts/git-env.mjs +9 -9
- package/scripts/icon-packs.mjs +151 -151
- package/scripts/link-counts.mjs +22 -22
- package/scripts/page-colors.mjs +42 -42
- package/scripts/releases.mjs +80 -80
- package/scripts/responsive-images.mjs +94 -94
- package/scripts/scheduled-list.mjs +15 -15
- package/scripts/theme-screenshots.mjs +88 -88
- package/scripts/types.mjs +17 -21
- package/scripts/video-thumbnails.mjs +89 -89
- package/scripts/worker-modules.mjs +34 -34
- package/site/appCookies.mjs +74 -74
- package/site/apps.ts +21 -21
- package/site/autolinks.mjs +200 -200
- package/site/blockScripts.ts +31 -31
- package/site/categories.ts +16 -16
- package/site/consent.ts +26 -26
- package/site/content.config.ts +84 -84
- package/site/content.ts +104 -104
- package/site/cookieList.ts +17 -17
- package/site/defineSite.d.mts +2 -14
- package/site/defineSite.mjs +131 -136
- package/site/directives/interact.mjs +31 -31
- package/site/editor-version.mjs +26 -26
- package/site/fill.ts +13 -13
- package/site/format.ts +11 -11
- package/site/home.ts +66 -66
- package/site/iconPacks.mjs +43 -43
- package/site/icons.mjs +605 -605
- package/site/images.mjs +157 -157
- package/site/motion.ts +13 -13
- package/site/noindex.mjs +21 -21
- package/site/pageColors.ts +110 -110
- package/site/popups.ts +98 -98
- package/site/schema.ts +189 -189
- package/site/sections.ts +3 -3
- package/site/seo.ts +28 -28
- package/site/site.ts +76 -76
- package/site/siteConfig.mjs +8 -8
- package/site/siteCsp.mjs +75 -75
- package/site/siteModules.mjs +27 -27
- package/site/standardPages.ts +59 -59
- package/site/storeTheme.d.mts +6 -0
- package/site/storeTheme.mjs +29 -0
- package/site/storeTheme.test.d.mts +1 -0
- package/site/storeTheme.test.mjs +53 -0
- package/site/testimonials.ts +48 -48
- package/site/text.ts +43 -43
- package/site/theme.mjs +483 -472
- package/site/tracking.client.ts +225 -225
- package/site/tracking.ts +9 -9
- package/src/ActionParts.tsx +205 -205
- package/src/Advanced.tsx +152 -152
- package/src/App.tsx +901 -904
- package/src/AppList.tsx +1362 -1345
- package/src/BlockDialog.tsx +360 -360
- package/src/BlockParts.tsx +652 -652
- package/src/BlockSettings.tsx +533 -533
- package/src/BrandForm.tsx +164 -164
- package/src/Colors.tsx +476 -476
- package/src/CommunicationParts.tsx +199 -199
- package/src/CompanyForm.tsx +209 -209
- package/src/Consents.tsx +175 -175
- package/src/ContainmentParts.tsx +185 -185
- package/src/Crop.tsx +152 -152
- package/src/DataForm.tsx +434 -434
- package/src/Deleted.tsx +177 -177
- package/src/EntryForm.tsx +740 -740
- package/src/FileView.tsx +30 -30
- package/src/FormFrame.tsx +237 -237
- package/src/Keywords.tsx +225 -225
- package/src/Library.tsx +1563 -1563
- package/src/Licenses.tsx +336 -336
- package/src/LinkSheet.tsx +138 -138
- package/src/MarkdownEditor.tsx +1075 -1075
- package/src/Media.tsx +677 -677
- package/src/NavigationParts.tsx +350 -350
- package/src/NavigationRestParts.tsx +247 -247
- package/src/Notifications.tsx +59 -59
- package/src/PagePicker.tsx +136 -136
- package/src/Parts.tsx +991 -991
- package/src/PartsShown.tsx +168 -168
- package/src/PickerParts.tsx +183 -183
- package/src/Redirects.tsx +450 -450
- package/src/Screen.tsx +184 -184
- package/src/SectionsField.tsx +65 -65
- package/src/SelectionParts.tsx +134 -134
- package/src/SeoScores.tsx +184 -184
- package/src/Sheet.tsx +35 -35
- package/src/ThemeStore.tsx +376 -376
- package/src/Tokens.tsx +346 -346
- package/src/Versions.tsx +214 -214
- package/src/addresses.ts +75 -75
- package/src/allIcons.ts +7 -7
- package/src/appFiles.ts +29 -29
- package/src/apps.ts +281 -281
- package/src/ask.tsx +123 -123
- package/src/blocks.ts +251 -251
- package/src/brand.ts +153 -153
- package/src/cache.ts +42 -42
- package/src/check.ts +285 -285
- package/src/checkSite.ts +256 -256
- package/src/{consents.ts → consentData.ts} +26 -26
- package/src/cookieList.ts +70 -70
- package/src/countries.ts +59 -59
- package/src/deploy.ts +155 -155
- package/src/editor.css +602 -602
- package/src/examples.ts +85 -85
- package/src/fields.tsx +942 -942
- package/src/frontmatter.mjs +42 -42
- package/src/github.ts +376 -376
- package/src/i18n/da-DK/advanced.ts +28 -28
- package/src/i18n/da-DK/app.ts +71 -71
- package/src/i18n/da-DK/apps.ts +90 -90
- package/src/i18n/da-DK/blockSettings.ts +38 -38
- package/src/i18n/da-DK/blocks.ts +408 -408
- package/src/i18n/da-DK/check.ts +25 -25
- package/src/i18n/da-DK/colors.ts +64 -64
- package/src/i18n/da-DK/common.ts +118 -118
- package/src/i18n/da-DK/company.ts +24 -24
- package/src/i18n/da-DK/consents.ts +23 -23
- package/src/i18n/da-DK/forms.ts +116 -116
- package/src/i18n/da-DK/history.ts +35 -35
- package/src/i18n/da-DK/index.ts +49 -49
- package/src/i18n/da-DK/keywords.ts +19 -19
- package/src/i18n/da-DK/library.ts +135 -135
- package/src/i18n/da-DK/licenses.ts +25 -25
- package/src/i18n/da-DK/media.ts +79 -79
- package/src/i18n/da-DK/parts.ts +696 -696
- package/src/i18n/da-DK/preview.ts +13 -13
- package/src/i18n/da-DK/redirects.ts +51 -51
- package/src/i18n/da-DK/seo.ts +20 -20
- package/src/i18n/da-DK/text.ts +35 -35
- package/src/i18n/da-DK/theme.ts +41 -41
- package/src/i18n/en-US/advanced.ts +32 -32
- package/src/i18n/en-US/app.ts +84 -84
- package/src/i18n/en-US/apps.ts +134 -134
- package/src/i18n/en-US/blockSettings.ts +48 -48
- package/src/i18n/en-US/blocks.ts +441 -441
- package/src/i18n/en-US/check.ts +31 -31
- package/src/i18n/en-US/colors.ts +82 -82
- package/src/i18n/en-US/common.ts +144 -144
- package/src/i18n/en-US/company.ts +25 -25
- package/src/i18n/en-US/consents.ts +25 -25
- package/src/i18n/en-US/forms.ts +145 -145
- package/src/i18n/en-US/history.ts +43 -43
- package/src/i18n/en-US/index.ts +51 -51
- package/src/i18n/en-US/keywords.ts +24 -24
- package/src/i18n/en-US/library.ts +176 -176
- package/src/i18n/en-US/licenses.ts +36 -36
- package/src/i18n/en-US/media.ts +80 -80
- package/src/i18n/en-US/parts.ts +718 -718
- package/src/i18n/en-US/preview.ts +15 -15
- package/src/i18n/en-US/redirects.ts +66 -66
- package/src/i18n/en-US/seo.ts +26 -26
- package/src/i18n/en-US/text.ts +41 -41
- package/src/i18n/en-US/theme.ts +41 -41
- package/src/i18n/index.ts +124 -124
- package/src/icon.tsx +160 -160
- package/src/iconPacks.ts +89 -89
- package/src/images.ts +163 -163
- package/src/{licenses.ts → licenseData.ts} +79 -79
- package/src/markdown.mjs +204 -204
- package/src/nav.ts +68 -68
- package/src/notFound.ts +28 -28
- package/src/{notifications.ts → notificationData.ts} +223 -223
- package/src/pagePreview.tsx +204 -204
- package/src/r2Caching.ts +55 -55
- package/src/{redirects.ts → redirectData.ts} +149 -149
- package/src/registry.tsx +188 -188
- package/src/schema.ts +417 -417
- package/src/sections.mjs +303 -303
- package/src/seo.ts +333 -333
- package/src/shared.ts +44 -44
- package/src/sidebar.ts +31 -31
- package/src/site.ts +317 -317
- package/src/standard/Block.astro +19 -19
- package/src/standard/BlockSite.astro +18 -18
- package/src/standard/accordion/index.tsx +47 -47
- package/src/standard/accordion/load.ts +23 -23
- package/src/standard/accordion/shared.ts +15 -15
- package/src/standard/accordion/view.ts +32 -32
- package/src/standard/buttons/fields.ts +98 -98
- package/src/standard/buttons/index.tsx +23 -23
- package/src/standard/buttons/load.ts +34 -34
- package/src/standard/buttons/shared.ts +12 -12
- package/src/standard/buttons/view.ts +30 -30
- package/src/standard/cards/index.tsx +71 -71
- package/src/standard/cards/load.ts +17 -17
- package/src/standard/cards/shared.ts +27 -27
- package/src/standard/cards/view.ts +29 -29
- package/src/standard/checklist/index.tsx +27 -27
- package/src/standard/checklist/load.ts +5 -5
- package/src/standard/checklist/view.ts +13 -13
- package/src/standard/cookies/index.tsx +17 -17
- package/src/standard/cookies/list.ts +56 -56
- package/src/standard/cookies/load.ts +11 -11
- package/src/standard/cookies/shared.ts +20 -20
- package/src/standard/cookies/view.ts +28 -28
- package/src/standard/faq/index.tsx +29 -29
- package/src/standard/faq/load.ts +14 -14
- package/src/standard/faq/view.ts +21 -21
- package/src/standard/head.ts +23 -23
- package/src/standard/hero/fields.ts +48 -48
- package/src/standard/hero/index.tsx +57 -57
- package/src/standard/hero/load.ts +23 -23
- package/src/standard/hero/view.ts +38 -38
- package/src/standard/links/index.tsx +41 -41
- package/src/standard/links/load.ts +17 -17
- package/src/standard/links/view.ts +25 -25
- package/src/standard/list/index.tsx +50 -50
- package/src/standard/list/load.ts +5 -5
- package/src/standard/list/view.ts +18 -18
- package/src/standard/load.ts +7 -7
- package/src/standard/notice/index.tsx +30 -30
- package/src/standard/notice/load.ts +16 -16
- package/src/standard/notice/view.ts +10 -10
- package/src/standard/points/index.tsx +42 -42
- package/src/standard/points/load.ts +5 -5
- package/src/standard/points/view.ts +12 -12
- package/src/standard/posts/index.tsx +132 -132
- package/src/standard/posts/load.ts +36 -36
- package/src/standard/posts/shared.ts +21 -21
- package/src/standard/posts/view.ts +63 -63
- package/src/standard/pricecards/index.tsx +57 -57
- package/src/standard/pricecards/load.ts +11 -11
- package/src/standard/pricecards/view.ts +50 -50
- package/src/standard/sharedTexts.ts +6 -6
- package/src/standard/site.ts +124 -124
- package/src/standard/siteLooks.ts +19 -19
- package/src/standard/sites.ts +13 -13
- package/src/standard/steps/index.tsx +28 -28
- package/src/standard/steps/load.ts +5 -5
- package/src/standard/steps/view.ts +11 -11
- package/src/standard/testimonials/index.tsx +87 -87
- package/src/standard/testimonials/list.ts +43 -43
- package/src/standard/testimonials/load.ts +58 -58
- package/src/standard/testimonials/shared.ts +13 -13
- package/src/standard/testimonials/view.ts +76 -76
- package/src/standard/themeBlocks.ts +13 -13
- package/src/standard/video/index.tsx +23 -23
- package/src/standard/video/load.ts +10 -10
- package/src/standard/video/view.ts +42 -42
- package/src/standard/view.tsx +105 -105
- package/src/standard/views.ts +37 -37
- package/src/store.ts +479 -499
- package/src/theme.ts +90 -90
- package/src/themeUi.ts +97 -97
- package/src/themes.ts +91 -91
- package/src/ui.tsx +3402 -3402
- package/src/virtual.d.ts +5 -5
- package/src/when.ts +22 -22
- package/starter/.githooks/pre-push +4 -4
- package/starter/.nvmrc +1 -1
- package/starter/.prettierignore +11 -11
- package/starter/.prettierrc.json +7 -7
- package/starter/README.md +27 -26
- package/starter/astro.config.mjs +5 -5
- package/starter/package.json +67 -67
- package/starter/public/_redirects +1 -1
- package/starter/public/favicon.svg +1 -1
- package/starter/public/logo.svg +1 -1
- package/starter/src/content/pages/blog.md +8 -8
- package/starter/src/content/pages/forside.md +9 -9
- package/starter/src/content/pages/kontakt.md +7 -7
- package/starter/src/content/pages/privatlivspolitik.md +9 -9
- package/starter/src/content/pages/siden-findes-ikke.md +7 -7
- package/starter/src/content.config.ts +2 -2
- package/starter/src/data/apps.json +1 -1
- package/starter/src/data/block-settings.json +3 -3
- package/starter/src/data/blocks.json +1 -1
- package/starter/src/data/booking.json +6 -6
- package/starter/src/data/colors.json +70 -70
- package/starter/src/data/contact.json +8 -8
- package/starter/src/data/cookies.json +21 -21
- package/starter/src/data/csp.json +1 -1
- package/starter/src/data/footer.json +15 -15
- package/starter/src/data/internal-links.json +4 -4
- package/starter/src/data/menu.json +4 -4
- package/starter/src/data/mobile-menu.json +4 -4
- package/starter/src/data/old-addresses.json +1 -1
- package/starter/src/data/redirects.json +1 -1
- package/starter/src/data/standard-pages.json +8 -8
- package/starter/src/data/theme.json +1 -1
- package/starter/src/env.d.ts +2 -2
- package/starter/src/site/config.ts +13 -13
- package/starter/src/site/worker.js +14 -14
- package/starter/tsconfig.json +17 -17
- package/starter/worker/apps.js +6 -6
- package/starter/wrangler.jsonc +39 -39
- package/theme/LICENSE +21 -21
- package/theme/blocks/ArrowLink.tsx +14 -14
- package/theme/blocks/BlockHead.tsx +26 -26
- package/theme/blocks/Panel.tsx +32 -32
- package/theme/blocks/Person.tsx +26 -26
- package/theme/blocks/accordion/PageRow.tsx +25 -25
- package/theme/blocks/accordion/View.tsx +17 -17
- package/theme/blocks/buttons/Buttons.tsx +28 -28
- package/theme/blocks/buttons/View.tsx +12 -12
- package/theme/blocks/cards/LinkCards.tsx +72 -72
- package/theme/blocks/cards/View.tsx +20 -20
- package/theme/blocks/checklist/Checks.tsx +15 -15
- package/theme/blocks/checklist/View.tsx +14 -14
- package/theme/blocks/cookies/View.tsx +36 -36
- package/theme/blocks/faq/Accordion.tsx +34 -34
- package/theme/blocks/faq/View.tsx +16 -16
- package/theme/blocks/faq/script.ts +56 -56
- package/theme/blocks/hero/View.tsx +44 -44
- package/theme/blocks/links/LinkGroups.tsx +24 -24
- package/theme/blocks/links/View.tsx +17 -17
- package/theme/blocks/list/Rows.tsx +24 -24
- package/theme/blocks/list/View.tsx +14 -14
- package/theme/blocks/notice/View.tsx +13 -13
- package/theme/blocks/points/PointList.tsx +34 -34
- package/theme/blocks/points/View.tsx +14 -14
- package/theme/blocks/posts/View.tsx +26 -26
- package/theme/blocks/pricecards/View.tsx +40 -40
- package/theme/blocks/steps/StepList.tsx +23 -23
- package/theme/blocks/steps/View.tsx +14 -14
- package/theme/blocks/testimonials/List.tsx +46 -46
- package/theme/blocks/testimonials/Slider.tsx +44 -44
- package/theme/blocks/testimonials/Testimonial.tsx +124 -124
- package/theme/blocks/testimonials/View.tsx +21 -21
- package/theme/blocks/testimonials/script.ts +52 -52
- package/theme/blocks/video/View.tsx +41 -41
- package/theme/components/ActionBar.astro +63 -63
- package/theme/components/AppSlot.astro +21 -21
- package/theme/components/ArrowLink.tsx +21 -21
- package/theme/components/BlogIndex.astro +48 -48
- package/theme/components/BlogPosts.astro +18 -18
- package/theme/components/BlogPosts.tsx +45 -45
- package/theme/components/BookButton.tsx +22 -22
- package/theme/components/Container.astro +11 -11
- package/theme/components/CookieConsent.astro +121 -121
- package/theme/components/EndBlock.astro +21 -21
- package/theme/components/FontPreload.astro +6 -6
- package/theme/components/Footer.astro +85 -85
- package/theme/components/Header.astro +186 -186
- package/theme/components/Icon.astro +16 -16
- package/theme/components/Img.astro +29 -29
- package/theme/components/Img.tsx +33 -33
- package/theme/components/Inline.astro +11 -11
- package/theme/components/Logo.astro +23 -23
- package/theme/components/LogoMark.astro +10 -10
- package/theme/components/Markdown.astro +17 -17
- package/theme/components/MobileMenu.tsx +224 -224
- package/theme/components/MobileNav.astro +33 -33
- package/theme/components/PageHeader.astro +22 -22
- package/theme/components/PageSection.astro +24 -24
- package/theme/components/PhoneButton.tsx +11 -11
- package/theme/components/Popup.astro +145 -145
- package/theme/components/PopupButton.tsx +24 -24
- package/theme/components/PopupView.tsx +48 -48
- package/theme/components/PostList.tsx +58 -58
- package/theme/components/PostMeta.tsx +17 -17
- package/theme/components/RichText.astro +28 -28
- package/theme/components/Section.astro +29 -29
- package/theme/components/SectionHeading.tsx +8 -8
- package/theme/components/TextSection.astro +16 -16
- package/theme/components/home/PageBlock.astro +19 -19
- package/theme/components/home/Text.astro +13 -13
- package/theme/components/loadPopup.ts +22 -22
- package/theme/components/loadPosts.ts +16 -16
- package/theme/components/posts.ts +15 -15
- package/theme/editor/looks.mjs +7 -7
- package/theme/editor/previews.tsx +56 -56
- package/theme/layouts/Base.astro +273 -273
- package/theme/layouts/PageLayout.astro +126 -126
- package/theme/layouts/PostLayout.astro +137 -137
- package/theme/scripts/motion.ts +29 -29
- package/theme/styles/base.css +320 -320
- package/theme/styles/blocks.css +1808 -1808
- package/theme/styles/components.css +206 -206
- package/theme/styles/global.css +1302 -1302
- package/theme/styles/ui.css +6544 -6544
- package/theme/templates/Blog.astro +38 -38
- package/theme/templates/Category.astro +19 -19
- package/theme/templates/Home.astro +36 -36
- package/theme/templates/New.astro +68 -68
- package/theme/templates/NotFound.astro +117 -117
- package/theme/theme.json +28 -28
- package/theme/ui/Badge.tsx +45 -45
- package/theme/ui/BottomSheet.tsx +148 -148
- package/theme/ui/Button.tsx +79 -79
- package/theme/ui/ButtonGroup.tsx +31 -31
- package/theme/ui/Card.tsx +45 -45
- package/theme/ui/Carousel.tsx +124 -124
- package/theme/ui/Checkbox.tsx +59 -59
- package/theme/ui/Chip.tsx +101 -101
- package/theme/ui/ChipSet.tsx +27 -27
- package/theme/ui/DatePicker.tsx +149 -149
- package/theme/ui/DatePickerContainer.tsx +316 -316
- package/theme/ui/Dialog.tsx +100 -100
- package/theme/ui/Divider.tsx +27 -27
- package/theme/ui/ExposedDropdownMenu.tsx +77 -77
- package/theme/ui/Fab.tsx +88 -88
- package/theme/ui/FabMenu.tsx +67 -67
- package/theme/ui/IconButton.tsx +78 -78
- package/theme/ui/List.tsx +287 -287
- package/theme/ui/LoadingIndicator.tsx +29 -29
- package/theme/ui/Menu.tsx +278 -278
- package/theme/ui/NavigationBar.tsx +62 -62
- package/theme/ui/NavigationDrawer.tsx +80 -80
- package/theme/ui/NavigationRail.tsx +154 -154
- package/theme/ui/ProgressIndicator.tsx +162 -162
- package/theme/ui/RadioButton.tsx +50 -50
- package/theme/ui/Search.tsx +129 -129
- package/theme/ui/SegmentedButton.tsx +49 -49
- package/theme/ui/SideSheet.tsx +54 -54
- package/theme/ui/Slider.tsx +155 -155
- package/theme/ui/Snackbar.tsx +67 -67
- package/theme/ui/SplitButton.tsx +67 -67
- package/theme/ui/Switch.tsx +60 -60
- package/theme/ui/Tabs.tsx +114 -114
- package/theme/ui/TextField.tsx +278 -278
- package/theme/ui/TimePicker.tsx +128 -128
- package/theme/ui/TimePickerContainer.tsx +234 -234
- package/theme/ui/Toolbar.tsx +67 -67
- package/theme/ui/Tooltip.tsx +58 -58
- package/theme/ui/TopAppBar.tsx +77 -77
- package/theme/ui/bottomSheet.script.ts +8 -8
- package/theme/ui/{bottomSheet.ts → bottomSheetControl.ts} +211 -211
- package/theme/ui/carousel.script.ts +28 -28
- package/theme/ui/carouselLayout.ts +229 -229
- package/theme/ui/checkbox.script.ts +8 -8
- package/theme/ui/datePicker.script.ts +302 -302
- package/theme/ui/dates.ts +185 -185
- package/theme/ui/{dialog.ts → dialogControl.ts} +48 -48
- package/theme/ui/dropdownMenu.script.ts +166 -166
- package/theme/ui/fabMenu.script.ts +57 -57
- package/theme/ui/navigationRail.script.ts +47 -47
- package/theme/ui/ripple.script.ts +159 -159
- package/theme/ui/search.script.ts +92 -92
- package/theme/ui/{sideSheet.ts → sideSheetControl.ts} +48 -48
- package/theme/ui/slider.script.ts +12 -12
- package/theme/ui/sliderUpdate.ts +52 -52
- package/theme/ui/snackbar.script.ts +16 -16
- package/theme/ui/splitButton.script.ts +63 -63
- package/theme/ui/tabs.script.ts +48 -48
- package/theme/ui/textField.script.ts +17 -17
- package/theme/ui/textFieldUpdate.ts +44 -44
- package/theme/ui/timePicker.script.ts +248 -248
- package/theme/ui/toolbar.script.ts +43 -43
- package/theme/ui/tooltip.script.ts +68 -68
- package/worker/consent.js +141 -141
- package/worker/editor-github.js +167 -167
- package/worker/editor-host.js +74 -74
- package/worker/files.js +401 -401
- package/worker/github.js +36 -36
- package/worker/index.js +188 -184
- package/worker/not-found.js +97 -97
- package/worker/oauth.js +95 -95
- package/worker/publish.js +52 -52
- package/worker/push.js +270 -270
- package/worker/r2-status.js +50 -50
- package/worker/session.js +58 -58
- package/worker/store.js +46 -0
package/src/iconPacks.ts
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
// The site's icon packs, for the editor ("Indstillinger" › "Udseende" › "Ikonpakker", IconPacks in Library.tsx): the
|
|
2
|
-
// system's own (packages/editor/icons/<pack>/: Material Symbols, Phosphor and Lucide) and those the themes installed bring
|
|
3
|
-
// (src/themes/<theme>/icon-packs/<pack>/), each by its manifest (pack.json) with a few of its icons to show; and the one
|
|
4
|
-
// the site and this editor are built with (packages/editor/site/iconPacks.mjs: the one chosen, src/data/icons.json, else the theme's,
|
|
5
|
-
// else the standard theme's). The packs' icons are read as files: the editor draws them as they are, beside the icons it
|
|
6
|
-
// draws itself, which are the pack in use's (themePlugin in packages/editor/site/theme.mjs).
|
|
7
|
-
import { packInUse, packTheme, themePackId } from '../site/iconPacks.mjs';
|
|
8
|
-
import { STANDARD_THEME } from './store';
|
|
9
|
-
import { theme, themes } from './themes';
|
|
10
|
-
|
|
11
|
-
export type IconPack = {
|
|
12
|
-
/** Its id: a system pack's folder (`lucide`), a theme's `<theme>/<folder>` (packages/editor/site/iconPacks.mjs). */
|
|
13
|
-
id: string;
|
|
14
|
-
name: string;
|
|
15
|
-
/** Who made it. */
|
|
16
|
-
author: string;
|
|
17
|
-
/** Its licence, as an SPDX id (`Apache-2.0`), in its folder as LICENSE. */
|
|
18
|
-
license: string;
|
|
19
|
-
version?: string;
|
|
20
|
-
url?: string;
|
|
21
|
-
/** The theme it came with; none for the system's own. */
|
|
22
|
-
theme?: string;
|
|
23
|
-
/** The sets it has icons from besides its own (Material Design Icons' logos), each with its licence in the folder. */
|
|
24
|
-
includes?: { name: string; author: string; license: string; licenseFile: string; icons: string[] }[];
|
|
25
|
-
/** A few of its icons' files, to show what it looks like (SAMPLES). */
|
|
26
|
-
samples: string[];
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/** The icons each pack shows of itself, by the names the site draws them by. */
|
|
30
|
-
const SAMPLES = ['house', 'magnifying-glass', 'calendar-blank', 'envelope-simple', 'heart'];
|
|
31
|
-
|
|
32
|
-
type Manifest = Omit<IconPack, 'id' | 'theme' | 'samples'>;
|
|
33
|
-
const manifests = import.meta.glob<Manifest>(['@icons/*/pack.json', '/src/themes/*/icon-packs/*/pack.json'], {
|
|
34
|
-
eager: true,
|
|
35
|
-
import: 'default',
|
|
36
|
-
});
|
|
37
|
-
const files = import.meta.glob<string>(
|
|
38
|
-
[
|
|
39
|
-
'@icons/*/{house,magnifying-glass,calendar-blank,envelope-simple,heart}.svg',
|
|
40
|
-
'/src/themes/*/icon-packs/*/{house,magnifying-glass,calendar-blank,envelope-simple,heart}.svg',
|
|
41
|
-
],
|
|
42
|
-
{ eager: true, query: '?raw', import: 'default' },
|
|
43
|
-
);
|
|
44
|
-
const chosen = import.meta.glob<{ pack?: string }>('/src/data/icons.json', { eager: true, import: 'default' })['/src/data/icons.json'];
|
|
45
|
-
|
|
46
|
-
/** A pack's id from the path of a file in it: a theme's (src/themes/<theme>/icon-packs/<pack>/) or the system's (the
|
|
47
|
-
* CMS's icons/<pack>/, @icons). */
|
|
48
|
-
const idOf = (path: string) => {
|
|
49
|
-
const own = path.match(/\/themes\/([^/]+)\/icon-packs\/([^/]+)\//);
|
|
50
|
-
return own ? themePackId(own[1], own[2]) : path.split('/').at(-2)!;
|
|
51
|
-
};
|
|
52
|
-
const folderOf = (path: string) => path.slice(0, path.lastIndexOf('/') + 1);
|
|
53
|
-
|
|
54
|
-
/** The icon packs installed, by name. */
|
|
55
|
-
export const iconPacks: IconPack[] = Object.entries(manifests)
|
|
56
|
-
.map(([path, m]) => {
|
|
57
|
-
const id = idOf(path);
|
|
58
|
-
const folder = folderOf(path);
|
|
59
|
-
const pack: IconPack = {
|
|
60
|
-
id,
|
|
61
|
-
name: m.name,
|
|
62
|
-
author: m.author,
|
|
63
|
-
license: m.license,
|
|
64
|
-
samples: SAMPLES.map((name) => files[`${folder}${name}.svg`]).filter(Boolean),
|
|
65
|
-
};
|
|
66
|
-
if (m.version) pack.version = m.version;
|
|
67
|
-
if (m.url) pack.url = m.url;
|
|
68
|
-
if (m.includes?.length) pack.includes = m.includes;
|
|
69
|
-
const from = packTheme(id);
|
|
70
|
-
if (from) pack.theme = from;
|
|
71
|
-
return pack;
|
|
72
|
-
})
|
|
73
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
74
|
-
|
|
75
|
-
const installed = iconPacks.map((p) => p.id);
|
|
76
|
-
const standard = themes.find((x) => x.id === STANDARD_THEME);
|
|
77
|
-
|
|
78
|
-
/** The pack a theme uses while none is chosen, by its id. */
|
|
79
|
-
export const packOfTheme = (id: string): string =>
|
|
80
|
-
packInUse({ chosen: undefined, theme: id, icons: themes.find((x) => x.id === id)?.icons, standardIcons: standard?.icons, installed });
|
|
81
|
-
|
|
82
|
-
/** The pack the site and this editor are built with, by its id. */
|
|
83
|
-
export const iconPack: string = packInUse({
|
|
84
|
-
chosen: chosen?.pack,
|
|
85
|
-
theme: theme.id,
|
|
86
|
-
icons: theme.icons,
|
|
87
|
-
standardIcons: standard?.icons,
|
|
88
|
-
installed,
|
|
89
|
-
});
|
|
1
|
+
// The site's icon packs, for the editor ("Indstillinger" › "Udseende" › "Ikonpakker", IconPacks in Library.tsx): the
|
|
2
|
+
// system's own (packages/editor/icons/<pack>/: Material Symbols, Phosphor and Lucide) and those the themes installed bring
|
|
3
|
+
// (src/themes/<theme>/icon-packs/<pack>/), each by its manifest (pack.json) with a few of its icons to show; and the one
|
|
4
|
+
// the site and this editor are built with (packages/editor/site/iconPacks.mjs: the one chosen, src/data/icons.json, else the theme's,
|
|
5
|
+
// else the standard theme's). The packs' icons are read as files: the editor draws them as they are, beside the icons it
|
|
6
|
+
// draws itself, which are the pack in use's (themePlugin in packages/editor/site/theme.mjs).
|
|
7
|
+
import { packInUse, packTheme, themePackId } from '../site/iconPacks.mjs';
|
|
8
|
+
import { STANDARD_THEME } from './store';
|
|
9
|
+
import { theme, themes } from './themes';
|
|
10
|
+
|
|
11
|
+
export type IconPack = {
|
|
12
|
+
/** Its id: a system pack's folder (`lucide`), a theme's `<theme>/<folder>` (packages/editor/site/iconPacks.mjs). */
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
/** Who made it. */
|
|
16
|
+
author: string;
|
|
17
|
+
/** Its licence, as an SPDX id (`Apache-2.0`), in its folder as LICENSE. */
|
|
18
|
+
license: string;
|
|
19
|
+
version?: string;
|
|
20
|
+
url?: string;
|
|
21
|
+
/** The theme it came with; none for the system's own. */
|
|
22
|
+
theme?: string;
|
|
23
|
+
/** The sets it has icons from besides its own (Material Design Icons' logos), each with its licence in the folder. */
|
|
24
|
+
includes?: { name: string; author: string; license: string; licenseFile: string; icons: string[] }[];
|
|
25
|
+
/** A few of its icons' files, to show what it looks like (SAMPLES). */
|
|
26
|
+
samples: string[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** The icons each pack shows of itself, by the names the site draws them by. */
|
|
30
|
+
const SAMPLES = ['house', 'magnifying-glass', 'calendar-blank', 'envelope-simple', 'heart'];
|
|
31
|
+
|
|
32
|
+
type Manifest = Omit<IconPack, 'id' | 'theme' | 'samples'>;
|
|
33
|
+
const manifests = import.meta.glob<Manifest>(['@icons/*/pack.json', '/src/themes/*/icon-packs/*/pack.json'], {
|
|
34
|
+
eager: true,
|
|
35
|
+
import: 'default',
|
|
36
|
+
});
|
|
37
|
+
const files = import.meta.glob<string>(
|
|
38
|
+
[
|
|
39
|
+
'@icons/*/{house,magnifying-glass,calendar-blank,envelope-simple,heart}.svg',
|
|
40
|
+
'/src/themes/*/icon-packs/*/{house,magnifying-glass,calendar-blank,envelope-simple,heart}.svg',
|
|
41
|
+
],
|
|
42
|
+
{ eager: true, query: '?raw', import: 'default' },
|
|
43
|
+
);
|
|
44
|
+
const chosen = import.meta.glob<{ pack?: string }>('/src/data/icons.json', { eager: true, import: 'default' })['/src/data/icons.json'];
|
|
45
|
+
|
|
46
|
+
/** A pack's id from the path of a file in it: a theme's (src/themes/<theme>/icon-packs/<pack>/) or the system's (the
|
|
47
|
+
* CMS's icons/<pack>/, @icons). */
|
|
48
|
+
const idOf = (path: string) => {
|
|
49
|
+
const own = path.match(/\/themes\/([^/]+)\/icon-packs\/([^/]+)\//);
|
|
50
|
+
return own ? themePackId(own[1], own[2]) : path.split('/').at(-2)!;
|
|
51
|
+
};
|
|
52
|
+
const folderOf = (path: string) => path.slice(0, path.lastIndexOf('/') + 1);
|
|
53
|
+
|
|
54
|
+
/** The icon packs installed, by name. */
|
|
55
|
+
export const iconPacks: IconPack[] = Object.entries(manifests)
|
|
56
|
+
.map(([path, m]) => {
|
|
57
|
+
const id = idOf(path);
|
|
58
|
+
const folder = folderOf(path);
|
|
59
|
+
const pack: IconPack = {
|
|
60
|
+
id,
|
|
61
|
+
name: m.name,
|
|
62
|
+
author: m.author,
|
|
63
|
+
license: m.license,
|
|
64
|
+
samples: SAMPLES.map((name) => files[`${folder}${name}.svg`]).filter(Boolean),
|
|
65
|
+
};
|
|
66
|
+
if (m.version) pack.version = m.version;
|
|
67
|
+
if (m.url) pack.url = m.url;
|
|
68
|
+
if (m.includes?.length) pack.includes = m.includes;
|
|
69
|
+
const from = packTheme(id);
|
|
70
|
+
if (from) pack.theme = from;
|
|
71
|
+
return pack;
|
|
72
|
+
})
|
|
73
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
74
|
+
|
|
75
|
+
const installed = iconPacks.map((p) => p.id);
|
|
76
|
+
const standard = themes.find((x) => x.id === STANDARD_THEME);
|
|
77
|
+
|
|
78
|
+
/** The pack a theme uses while none is chosen, by its id. */
|
|
79
|
+
export const packOfTheme = (id: string): string =>
|
|
80
|
+
packInUse({ chosen: undefined, theme: id, icons: themes.find((x) => x.id === id)?.icons, standardIcons: standard?.icons, installed });
|
|
81
|
+
|
|
82
|
+
/** The pack the site and this editor are built with, by its id. */
|
|
83
|
+
export const iconPack: string = packInUse({
|
|
84
|
+
chosen: chosen?.pack,
|
|
85
|
+
theme: theme.id,
|
|
86
|
+
icons: theme.icons,
|
|
87
|
+
standardIcons: standard?.icons,
|
|
88
|
+
installed,
|
|
89
|
+
});
|
package/src/images.ts
CHANGED
|
@@ -1,163 +1,163 @@
|
|
|
1
|
-
// Pictures added in the editor. Before upload they are made at most 2000 px and WebP (JPEG in
|
|
2
|
-
// browsers that cannot write WebP, like Safari), as the old editor did; packages/editor/scripts/responsive-images.mjs
|
|
3
|
-
// makes the smaller sizes when the site is built. They go in public/images/uploads/<year>/<month>/
|
|
4
|
-
// like imported pictures, and are saved in the same commit as the page that uses them (on
|
|
5
|
-
// their own from Filer, Media.tsx). PDFs, videos and zip files, from Filer, are saved as they are, in
|
|
6
|
-
// public/filer/ (prepareFile).
|
|
7
|
-
import { useState } from 'react';
|
|
8
|
-
import { slugify } from './schema';
|
|
9
|
-
import { sourceColorsOf } from '../lib/dynamic-colors';
|
|
10
|
-
import { config } from '@site/config';
|
|
11
|
-
import type { Status } from './FormFrame';
|
|
12
|
-
import { t } from './i18n';
|
|
13
|
-
|
|
14
|
-
const MAX = 2000;
|
|
15
|
-
|
|
16
|
-
// url: a temporary address of the picture in this browser, shown until the site is rebuilt.
|
|
17
|
-
export type Upload = { url: string; path: string; bytes: Uint8Array; saved: boolean };
|
|
18
|
-
|
|
19
|
-
// The pictures made ready in this visit, by their address on the site: shown from the browser
|
|
20
|
-
// (shownAt), as the site has them only once it has been rebuilt.
|
|
21
|
-
const fresh = new Map<string, string>();
|
|
22
|
-
export const shownAt = (src: string) => fresh.get(src) ?? src;
|
|
23
|
-
// A picture renamed in this visit is shown from its old address until the site is rebuilt.
|
|
24
|
-
export const showMoved = (from: string, to: string) => fresh.set(to, shownAt(from));
|
|
25
|
-
|
|
26
|
-
/** What a file in the site's folder of files is, by its ending: a PDF, a video or a zip file. */
|
|
27
|
-
export type FileKind = 'pdf' | 'video' | 'zip';
|
|
28
|
-
|
|
29
|
-
// The endings of each kind, with the type a browser shows it as.
|
|
30
|
-
const ENDINGS: Record<string, [FileKind, string]> = {
|
|
31
|
-
pdf: ['pdf', 'application/pdf'],
|
|
32
|
-
mp4: ['video', 'video/mp4'],
|
|
33
|
-
m4v: ['video', 'video/mp4'],
|
|
34
|
-
mov: ['video', 'video/quicktime'],
|
|
35
|
-
webm: ['video', 'video/webm'],
|
|
36
|
-
zip: ['zip', 'application/zip'],
|
|
37
|
-
};
|
|
38
|
-
const endingOf = (name: string) => /\.([^./]+)$/.exec(name)?.[1].toLowerCase() ?? '';
|
|
39
|
-
|
|
40
|
-
/** A file's kind by its name or address, or null for one that is not a PDF, a video or a zip file. */
|
|
41
|
-
export const fileKindOf = (name: string): FileKind | null => ENDINGS[endingOf(name)]?.[0] ?? null;
|
|
42
|
-
|
|
43
|
-
/** The type a browser shows a file as, by its name ('' for a picture). */
|
|
44
|
-
export const typeOf = (name: string) => ENDINGS[endingOf(name)]?.[1] ?? '';
|
|
45
|
-
|
|
46
|
-
// A picked file's ending: its own, or for one without it the first of its type ("Guide" from a phone,
|
|
47
|
-
// application/pdf, as pdf); '' for another kind of file.
|
|
48
|
-
const pickedEnding = (file: File) =>
|
|
49
|
-
ENDINGS[endingOf(file.name)] ? endingOf(file.name) : (Object.keys(ENDINGS).find((e) => ENDINGS[e][1] === file.type) ?? '');
|
|
50
|
-
|
|
51
|
-
/** A picked file's kind, by its ending or else its type, or null for one that is not a PDF, a video or a zip file. */
|
|
52
|
-
export const pickedKindOf = (file: File): FileKind | null => ENDINGS[pickedEnding(file)]?.[0] ?? null;
|
|
53
|
-
|
|
54
|
-
/** A file picked on the phone or computer: a picture, or (`all`, in Filer) a picture, a PDF, a video or a
|
|
55
|
-
* zip file. */
|
|
56
|
-
export function pickImage(all = false): Promise<File | null> {
|
|
57
|
-
return new Promise((resolve) => {
|
|
58
|
-
const input = document.createElement('input');
|
|
59
|
-
input.type = 'file';
|
|
60
|
-
input.accept = all
|
|
61
|
-
? ['image/*', ...new Set(Object.values(ENDINGS).map(([, type]) => type)), ...Object.keys(ENDINGS).map((e) => `.${e}`)].join(',')
|
|
62
|
-
: 'image/*';
|
|
63
|
-
input.addEventListener('change', () => resolve(input.files?.[0] ?? null));
|
|
64
|
-
input.addEventListener('cancel', () => resolve(null));
|
|
65
|
-
input.click();
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** The colours in a picture that are best as a source colour (dynamic colour, sourceColorsOf), best first: one on the
|
|
70
|
-
* phone or computer (it is not uploaded), or one at an address of the editor's own. Read small, which is enough and quick. */
|
|
71
|
-
export async function colorsInPicture(file: Blob): Promise<string[]> {
|
|
72
|
-
const bitmap = await createImageBitmap(file);
|
|
73
|
-
const scale = Math.min(1, 128 / Math.max(bitmap.width, bitmap.height));
|
|
74
|
-
const canvas = document.createElement('canvas');
|
|
75
|
-
canvas.width = Math.max(1, Math.round(bitmap.width * scale));
|
|
76
|
-
canvas.height = Math.max(1, Math.round(bitmap.height * scale));
|
|
77
|
-
const context = canvas.getContext('2d')!;
|
|
78
|
-
context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
79
|
-
bitmap.close();
|
|
80
|
-
return sourceColorsOf(context.getImageData(0, 0, canvas.width, canvas.height).data);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export async function prepareImage(file: File): Promise<Upload> {
|
|
84
|
-
const bitmap = await createImageBitmap(file, { imageOrientation: 'from-image' }).catch(() => {
|
|
85
|
-
throw new Error(t.app.pictureNotOpened);
|
|
86
|
-
});
|
|
87
|
-
const scale = Math.min(1, MAX / Math.max(bitmap.width, bitmap.height));
|
|
88
|
-
const canvas = document.createElement('canvas');
|
|
89
|
-
canvas.width = Math.round(bitmap.width * scale);
|
|
90
|
-
canvas.height = Math.round(bitmap.height * scale);
|
|
91
|
-
canvas.getContext('2d')!.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
92
|
-
bitmap.close();
|
|
93
|
-
const encode = (type: string) => new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, type, 0.82));
|
|
94
|
-
let blob = await encode('image/webp');
|
|
95
|
-
if (blob?.type !== 'image/webp') blob = await encode('image/jpeg');
|
|
96
|
-
if (!blob) throw new Error(t.app.pictureNotReady);
|
|
97
|
-
|
|
98
|
-
const now = new Date();
|
|
99
|
-
const folder = `${config.folders.media}/${now.getFullYear()}/${String(now.getMonth() + 1).padStart(2, '0')}`;
|
|
100
|
-
// A short random ending, so two pictures called IMG_1234 do not get the same name.
|
|
101
|
-
const name = `${slugify(file.name.replace(/\.[^.]*$/, '')) || 'billede'}-${Math.random().toString(36).slice(2, 6)}`;
|
|
102
|
-
const ext = blob.type === 'image/webp' ? 'webp' : 'jpg';
|
|
103
|
-
const upload = {
|
|
104
|
-
url: URL.createObjectURL(blob),
|
|
105
|
-
path: `${folder}/${name}.${ext}`,
|
|
106
|
-
bytes: new Uint8Array(await blob.arrayBuffer()),
|
|
107
|
-
saved: false,
|
|
108
|
-
};
|
|
109
|
-
fresh.set(publicPath(upload.path), upload.url);
|
|
110
|
-
return upload;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** The most a PDF, a video or a zip file may weigh, in MB: it goes to GitHub in one request with the commit. */
|
|
114
|
-
export const FILE_MAX_MB = 10;
|
|
115
|
-
|
|
116
|
-
/** A PDF, a video or a zip file as it is, in the site's folder of files (`config.folders.files`, /filer/ on
|
|
117
|
-
* the site), named by its own name as an address ("Stress guide.pdf" as stress-guide.pdf, the ending in
|
|
118
|
-
* small letters), with -2, -3 … when the name is taken (`taken`: the paths there are). */
|
|
119
|
-
export async function prepareFile(file: File, taken: string[]): Promise<Upload> {
|
|
120
|
-
if (file.size > FILE_MAX_MB * 1024 * 1024) throw new Error(t.media.tooBig(FILE_MAX_MB));
|
|
121
|
-
const folder = config.folders.files;
|
|
122
|
-
const stem = slugify(file.name.replace(/\.[^.]*$/, '')) || 'fil';
|
|
123
|
-
const ending = pickedEnding(file);
|
|
124
|
-
let path = `${folder}/${stem}.${ending}`;
|
|
125
|
-
for (let n = 2; taken.includes(path); n++) path = `${folder}/${stem}-${n}.${ending}`;
|
|
126
|
-
const upload = { url: URL.createObjectURL(file), path, bytes: new Uint8Array(await file.arrayBuffer()), saved: false };
|
|
127
|
-
fresh.set(publicPath(upload.path), upload.url);
|
|
128
|
-
return upload;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// The address of an uploaded picture on the site.
|
|
132
|
-
export const publicPath = (path: string) => path.replace(/^public/, '');
|
|
133
|
-
|
|
134
|
-
// The pictures added in a form (a page, a fixed page), kept in the browser until "Gem", which saves
|
|
135
|
-
// them in the same commit. upload: pick one and make it ready; added: those not saved yet, newest
|
|
136
|
-
// first (offered in the gallery, MediaPicker.tsx); saving: the pictures a text uses.
|
|
137
|
-
export function useUploads(notify: (status: Status) => void) {
|
|
138
|
-
const [uploads, setUploads] = useState<Upload[]>([]);
|
|
139
|
-
|
|
140
|
-
async function upload(): Promise<Upload | null> {
|
|
141
|
-
const file = await pickImage();
|
|
142
|
-
if (!file) return null;
|
|
143
|
-
notify({ text: t.app.preparingPicture });
|
|
144
|
-
try {
|
|
145
|
-
const prepared = await prepareImage(file);
|
|
146
|
-
setUploads((u) => [...u, prepared]);
|
|
147
|
-
notify(null);
|
|
148
|
-
return prepared;
|
|
149
|
-
} catch (e) {
|
|
150
|
-
notify({ text: (e as Error).message, error: true });
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const added = uploads.filter((u) => !u.saved).reverse();
|
|
156
|
-
/** The pictures not saved yet that a text uses. */
|
|
157
|
-
const usedIn = (text: string) => uploads.filter((u) => !u.saved && text.includes(publicPath(u.path)));
|
|
158
|
-
const markSaved = (pictures: Upload[]) => setUploads((all) => all.map((u) => (pictures.includes(u) ? { ...u, saved: true } : u)));
|
|
159
|
-
|
|
160
|
-
return { upload, added, usedIn, markSaved };
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export type Uploads = ReturnType<typeof useUploads>;
|
|
1
|
+
// Pictures added in the editor. Before upload they are made at most 2000 px and WebP (JPEG in
|
|
2
|
+
// browsers that cannot write WebP, like Safari), as the old editor did; packages/editor/scripts/responsive-images.mjs
|
|
3
|
+
// makes the smaller sizes when the site is built. They go in public/images/uploads/<year>/<month>/
|
|
4
|
+
// like imported pictures, and are saved in the same commit as the page that uses them (on
|
|
5
|
+
// their own from Filer, Media.tsx). PDFs, videos and zip files, from Filer, are saved as they are, in
|
|
6
|
+
// public/filer/ (prepareFile).
|
|
7
|
+
import { useState } from 'react';
|
|
8
|
+
import { slugify } from './schema';
|
|
9
|
+
import { sourceColorsOf } from '../lib/dynamic-colors';
|
|
10
|
+
import { config } from '@site/config';
|
|
11
|
+
import type { Status } from './FormFrame';
|
|
12
|
+
import { t } from './i18n';
|
|
13
|
+
|
|
14
|
+
const MAX = 2000;
|
|
15
|
+
|
|
16
|
+
// url: a temporary address of the picture in this browser, shown until the site is rebuilt.
|
|
17
|
+
export type Upload = { url: string; path: string; bytes: Uint8Array; saved: boolean };
|
|
18
|
+
|
|
19
|
+
// The pictures made ready in this visit, by their address on the site: shown from the browser
|
|
20
|
+
// (shownAt), as the site has them only once it has been rebuilt.
|
|
21
|
+
const fresh = new Map<string, string>();
|
|
22
|
+
export const shownAt = (src: string) => fresh.get(src) ?? src;
|
|
23
|
+
// A picture renamed in this visit is shown from its old address until the site is rebuilt.
|
|
24
|
+
export const showMoved = (from: string, to: string) => fresh.set(to, shownAt(from));
|
|
25
|
+
|
|
26
|
+
/** What a file in the site's folder of files is, by its ending: a PDF, a video or a zip file. */
|
|
27
|
+
export type FileKind = 'pdf' | 'video' | 'zip';
|
|
28
|
+
|
|
29
|
+
// The endings of each kind, with the type a browser shows it as.
|
|
30
|
+
const ENDINGS: Record<string, [FileKind, string]> = {
|
|
31
|
+
pdf: ['pdf', 'application/pdf'],
|
|
32
|
+
mp4: ['video', 'video/mp4'],
|
|
33
|
+
m4v: ['video', 'video/mp4'],
|
|
34
|
+
mov: ['video', 'video/quicktime'],
|
|
35
|
+
webm: ['video', 'video/webm'],
|
|
36
|
+
zip: ['zip', 'application/zip'],
|
|
37
|
+
};
|
|
38
|
+
const endingOf = (name: string) => /\.([^./]+)$/.exec(name)?.[1].toLowerCase() ?? '';
|
|
39
|
+
|
|
40
|
+
/** A file's kind by its name or address, or null for one that is not a PDF, a video or a zip file. */
|
|
41
|
+
export const fileKindOf = (name: string): FileKind | null => ENDINGS[endingOf(name)]?.[0] ?? null;
|
|
42
|
+
|
|
43
|
+
/** The type a browser shows a file as, by its name ('' for a picture). */
|
|
44
|
+
export const typeOf = (name: string) => ENDINGS[endingOf(name)]?.[1] ?? '';
|
|
45
|
+
|
|
46
|
+
// A picked file's ending: its own, or for one without it the first of its type ("Guide" from a phone,
|
|
47
|
+
// application/pdf, as pdf); '' for another kind of file.
|
|
48
|
+
const pickedEnding = (file: File) =>
|
|
49
|
+
ENDINGS[endingOf(file.name)] ? endingOf(file.name) : (Object.keys(ENDINGS).find((e) => ENDINGS[e][1] === file.type) ?? '');
|
|
50
|
+
|
|
51
|
+
/** A picked file's kind, by its ending or else its type, or null for one that is not a PDF, a video or a zip file. */
|
|
52
|
+
export const pickedKindOf = (file: File): FileKind | null => ENDINGS[pickedEnding(file)]?.[0] ?? null;
|
|
53
|
+
|
|
54
|
+
/** A file picked on the phone or computer: a picture, or (`all`, in Filer) a picture, a PDF, a video or a
|
|
55
|
+
* zip file. */
|
|
56
|
+
export function pickImage(all = false): Promise<File | null> {
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
const input = document.createElement('input');
|
|
59
|
+
input.type = 'file';
|
|
60
|
+
input.accept = all
|
|
61
|
+
? ['image/*', ...new Set(Object.values(ENDINGS).map(([, type]) => type)), ...Object.keys(ENDINGS).map((e) => `.${e}`)].join(',')
|
|
62
|
+
: 'image/*';
|
|
63
|
+
input.addEventListener('change', () => resolve(input.files?.[0] ?? null));
|
|
64
|
+
input.addEventListener('cancel', () => resolve(null));
|
|
65
|
+
input.click();
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The colours in a picture that are best as a source colour (dynamic colour, sourceColorsOf), best first: one on the
|
|
70
|
+
* phone or computer (it is not uploaded), or one at an address of the editor's own. Read small, which is enough and quick. */
|
|
71
|
+
export async function colorsInPicture(file: Blob): Promise<string[]> {
|
|
72
|
+
const bitmap = await createImageBitmap(file);
|
|
73
|
+
const scale = Math.min(1, 128 / Math.max(bitmap.width, bitmap.height));
|
|
74
|
+
const canvas = document.createElement('canvas');
|
|
75
|
+
canvas.width = Math.max(1, Math.round(bitmap.width * scale));
|
|
76
|
+
canvas.height = Math.max(1, Math.round(bitmap.height * scale));
|
|
77
|
+
const context = canvas.getContext('2d')!;
|
|
78
|
+
context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
79
|
+
bitmap.close();
|
|
80
|
+
return sourceColorsOf(context.getImageData(0, 0, canvas.width, canvas.height).data);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function prepareImage(file: File): Promise<Upload> {
|
|
84
|
+
const bitmap = await createImageBitmap(file, { imageOrientation: 'from-image' }).catch(() => {
|
|
85
|
+
throw new Error(t.app.pictureNotOpened);
|
|
86
|
+
});
|
|
87
|
+
const scale = Math.min(1, MAX / Math.max(bitmap.width, bitmap.height));
|
|
88
|
+
const canvas = document.createElement('canvas');
|
|
89
|
+
canvas.width = Math.round(bitmap.width * scale);
|
|
90
|
+
canvas.height = Math.round(bitmap.height * scale);
|
|
91
|
+
canvas.getContext('2d')!.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
92
|
+
bitmap.close();
|
|
93
|
+
const encode = (type: string) => new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, type, 0.82));
|
|
94
|
+
let blob = await encode('image/webp');
|
|
95
|
+
if (blob?.type !== 'image/webp') blob = await encode('image/jpeg');
|
|
96
|
+
if (!blob) throw new Error(t.app.pictureNotReady);
|
|
97
|
+
|
|
98
|
+
const now = new Date();
|
|
99
|
+
const folder = `${config.folders.media}/${now.getFullYear()}/${String(now.getMonth() + 1).padStart(2, '0')}`;
|
|
100
|
+
// A short random ending, so two pictures called IMG_1234 do not get the same name.
|
|
101
|
+
const name = `${slugify(file.name.replace(/\.[^.]*$/, '')) || 'billede'}-${Math.random().toString(36).slice(2, 6)}`;
|
|
102
|
+
const ext = blob.type === 'image/webp' ? 'webp' : 'jpg';
|
|
103
|
+
const upload = {
|
|
104
|
+
url: URL.createObjectURL(blob),
|
|
105
|
+
path: `${folder}/${name}.${ext}`,
|
|
106
|
+
bytes: new Uint8Array(await blob.arrayBuffer()),
|
|
107
|
+
saved: false,
|
|
108
|
+
};
|
|
109
|
+
fresh.set(publicPath(upload.path), upload.url);
|
|
110
|
+
return upload;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The most a PDF, a video or a zip file may weigh, in MB: it goes to GitHub in one request with the commit. */
|
|
114
|
+
export const FILE_MAX_MB = 10;
|
|
115
|
+
|
|
116
|
+
/** A PDF, a video or a zip file as it is, in the site's folder of files (`config.folders.files`, /filer/ on
|
|
117
|
+
* the site), named by its own name as an address ("Stress guide.pdf" as stress-guide.pdf, the ending in
|
|
118
|
+
* small letters), with -2, -3 … when the name is taken (`taken`: the paths there are). */
|
|
119
|
+
export async function prepareFile(file: File, taken: string[]): Promise<Upload> {
|
|
120
|
+
if (file.size > FILE_MAX_MB * 1024 * 1024) throw new Error(t.media.tooBig(FILE_MAX_MB));
|
|
121
|
+
const folder = config.folders.files;
|
|
122
|
+
const stem = slugify(file.name.replace(/\.[^.]*$/, '')) || 'fil';
|
|
123
|
+
const ending = pickedEnding(file);
|
|
124
|
+
let path = `${folder}/${stem}.${ending}`;
|
|
125
|
+
for (let n = 2; taken.includes(path); n++) path = `${folder}/${stem}-${n}.${ending}`;
|
|
126
|
+
const upload = { url: URL.createObjectURL(file), path, bytes: new Uint8Array(await file.arrayBuffer()), saved: false };
|
|
127
|
+
fresh.set(publicPath(upload.path), upload.url);
|
|
128
|
+
return upload;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// The address of an uploaded picture on the site.
|
|
132
|
+
export const publicPath = (path: string) => path.replace(/^public/, '');
|
|
133
|
+
|
|
134
|
+
// The pictures added in a form (a page, a fixed page), kept in the browser until "Gem", which saves
|
|
135
|
+
// them in the same commit. upload: pick one and make it ready; added: those not saved yet, newest
|
|
136
|
+
// first (offered in the gallery, MediaPicker.tsx); saving: the pictures a text uses.
|
|
137
|
+
export function useUploads(notify: (status: Status) => void) {
|
|
138
|
+
const [uploads, setUploads] = useState<Upload[]>([]);
|
|
139
|
+
|
|
140
|
+
async function upload(): Promise<Upload | null> {
|
|
141
|
+
const file = await pickImage();
|
|
142
|
+
if (!file) return null;
|
|
143
|
+
notify({ text: t.app.preparingPicture });
|
|
144
|
+
try {
|
|
145
|
+
const prepared = await prepareImage(file);
|
|
146
|
+
setUploads((u) => [...u, prepared]);
|
|
147
|
+
notify(null);
|
|
148
|
+
return prepared;
|
|
149
|
+
} catch (e) {
|
|
150
|
+
notify({ text: (e as Error).message, error: true });
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const added = uploads.filter((u) => !u.saved).reverse();
|
|
156
|
+
/** The pictures not saved yet that a text uses. */
|
|
157
|
+
const usedIn = (text: string) => uploads.filter((u) => !u.saved && text.includes(publicPath(u.path)));
|
|
158
|
+
const markSaved = (pictures: Upload[]) => setUploads((all) => all.map((u) => (pictures.includes(u) ? { ...u, saved: true } : u)));
|
|
159
|
+
|
|
160
|
+
return { upload, added, usedIn, markSaved };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export type Uploads = ReturnType<typeof useUploads>;
|