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