@morphemecms/cms 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/astro.mjs +3 -0
- package/bin/morpheme.mjs +3 -1
- package/defaults/blockTexts.ts +2 -2
- package/defaults/collections.ts +10 -0
- package/defaults/editor.ts +57 -53
- package/defaults/privacy.ts +4 -1
- package/defaults/rules.ts +36 -38
- package/defaults/standardPages.ts +0 -3
- package/defaults/texts.ts +11 -2
- package/demo/src/content/pages/about.md +2 -0
- package/icons/lucide/browser-fill.svg +1 -0
- package/icons/lucide/buildings-fill.svg +1 -0
- package/icons/lucide/certificate-fill.svg +1 -0
- package/icons/lucide/chart-line-up-fill.svg +1 -0
- package/icons/lucide/chats-fill.svg +1 -0
- package/icons/lucide/envelope-simple-fill.svg +1 -0
- package/icons/lucide/file-text-fill.svg +1 -0
- package/icons/lucide/folder-simple-fill.svg +1 -0
- package/icons/lucide/gauge.svg +1 -1
- package/icons/lucide/gear-six.svg +1 -0
- package/icons/lucide/globe-simple.svg +1 -0
- package/icons/lucide/heart-fill.svg +1 -0
- package/icons/lucide/layout.svg +1 -0
- package/icons/lucide/list-checks-fill.svg +1 -0
- package/icons/lucide/magnifying-glass-fill.svg +1 -0
- package/icons/lucide/megaphone-fill.svg +1 -0
- package/icons/lucide/newspaper-fill.svg +1 -0
- package/icons/lucide/paint-brush-broad-fill.svg +1 -0
- package/icons/lucide/puzzle-piece-fill.svg +1 -0
- package/icons/lucide/shield-check-fill.svg +1 -0
- package/icons/lucide/sidebar-simple.svg +1 -1
- package/icons/lucide/sidebar.svg +1 -0
- package/icons/lucide/square-half-bottom-fill.svg +1 -0
- package/icons/lucide/square-half.svg +1 -0
- package/icons/lucide/squares-four-fill.svg +1 -0
- package/icons/lucide/storefront-fill.svg +1 -0
- package/icons/lucide/tag-fill.svg +1 -0
- package/icons/lucide/toggle-right-fill.svg +1 -0
- package/icons/lucide/trash-fill.svg +1 -0
- package/icons/lucide/user-fill.svg +1 -0
- package/icons/lucide/wrench-fill.svg +1 -0
- package/icons/material-symbols/browser-fill.svg +1 -0
- package/icons/material-symbols/buildings-fill.svg +1 -0
- package/icons/material-symbols/certificate-fill.svg +1 -0
- package/icons/material-symbols/chart-line-up-fill.svg +1 -0
- package/icons/material-symbols/chats-fill.svg +1 -0
- package/icons/material-symbols/envelope-simple-fill.svg +1 -0
- package/icons/material-symbols/file-text-fill.svg +1 -0
- package/icons/material-symbols/folder-simple-fill.svg +1 -0
- package/icons/material-symbols/gauge.svg +1 -1
- package/icons/material-symbols/gear-six.svg +1 -0
- package/icons/material-symbols/globe-simple.svg +1 -0
- package/icons/material-symbols/heart-fill.svg +1 -0
- package/icons/material-symbols/layout.svg +1 -0
- package/icons/material-symbols/list-checks-fill.svg +1 -0
- package/icons/material-symbols/magnifying-glass-fill.svg +1 -0
- package/icons/material-symbols/megaphone-fill.svg +1 -0
- package/icons/material-symbols/newspaper-fill.svg +1 -0
- package/icons/material-symbols/paint-brush-broad-fill.svg +1 -0
- package/icons/material-symbols/puzzle-piece-fill.svg +1 -0
- package/icons/material-symbols/shield-check-fill.svg +1 -0
- package/icons/material-symbols/sidebar-simple.svg +1 -1
- package/icons/material-symbols/sidebar.svg +1 -0
- package/icons/material-symbols/square-half-bottom-fill.svg +1 -0
- package/icons/material-symbols/square-half.svg +1 -0
- package/icons/material-symbols/squares-four-fill.svg +1 -0
- package/icons/material-symbols/storefront-fill.svg +1 -0
- package/icons/material-symbols/tag-fill.svg +1 -0
- package/icons/material-symbols/toggle-right-fill.svg +1 -0
- package/icons/material-symbols/trash-fill.svg +1 -0
- package/icons/material-symbols/user-fill.svg +1 -0
- package/icons/material-symbols/wrench-fill.svg +1 -0
- package/icons/phosphor/browser-fill.svg +1 -0
- package/icons/phosphor/buildings-fill.svg +1 -0
- package/icons/phosphor/certificate-fill.svg +1 -0
- package/icons/phosphor/chart-line-up-fill.svg +1 -0
- package/icons/phosphor/chats-fill.svg +1 -0
- package/icons/phosphor/envelope-simple-fill.svg +1 -0
- package/icons/phosphor/file-text-fill.svg +1 -0
- package/icons/phosphor/folder-simple-fill.svg +1 -0
- package/icons/phosphor/gear-six.svg +1 -0
- package/icons/phosphor/globe-simple.svg +1 -0
- package/icons/phosphor/heart-fill.svg +1 -0
- package/icons/phosphor/layout.svg +1 -0
- package/icons/phosphor/list-checks-fill.svg +1 -0
- package/icons/phosphor/magnifying-glass-fill.svg +1 -0
- package/icons/phosphor/megaphone-fill.svg +1 -0
- package/icons/phosphor/newspaper-fill.svg +1 -0
- package/icons/phosphor/paint-brush-broad-fill.svg +1 -0
- package/icons/phosphor/puzzle-piece-fill.svg +1 -0
- package/icons/phosphor/shield-check-fill.svg +1 -0
- package/icons/phosphor/sidebar.svg +1 -0
- package/icons/phosphor/square-half-bottom-fill.svg +1 -0
- package/icons/phosphor/square-half.svg +1 -0
- package/icons/phosphor/squares-four-fill.svg +1 -0
- package/icons/phosphor/storefront-fill.svg +1 -0
- package/icons/phosphor/tag-fill.svg +1 -0
- package/icons/phosphor/toggle-right-fill.svg +1 -0
- package/icons/phosphor/trash-fill.svg +1 -0
- package/icons/phosphor/user-fill.svg +1 -0
- package/icons/phosphor/wrench-fill.svg +1 -0
- package/lib/breakpoints.d.mts +13 -0
- package/lib/breakpoints.mjs +20 -0
- package/lib/breakpoints.test.mjs +39 -0
- package/lib/build-lane.d.mts +15 -0
- package/lib/build-lane.mjs +23 -0
- package/lib/build-lane.test.mjs +74 -0
- package/lib/build-settings.d.mts +16 -0
- package/lib/build-settings.mjs +39 -0
- package/lib/build-settings.test.mjs +41 -0
- package/lib/contracts.d.mts +3 -2
- package/lib/contracts.mjs +3 -2
- package/lib/drawer-swipe.d.mts +10 -0
- package/lib/drawer-swipe.mjs +17 -0
- package/lib/drawer-swipe.test.mjs +33 -0
- package/lib/drawerKeys.d.mts +9 -0
- package/lib/drawerKeys.mjs +26 -0
- package/lib/drawerKeys.test.mjs +21 -0
- package/lib/eyebrow.d.mts +6 -0
- package/lib/eyebrow.mjs +24 -0
- package/lib/eyebrow.test.mjs +20 -0
- package/lib/front-sections.test.mjs +82 -0
- package/lib/heading-levels.d.mts +7 -0
- package/lib/heading-levels.mjs +16 -0
- package/lib/heading-levels.test.mjs +37 -0
- package/lib/inline.ts +3 -2
- package/lib/markdown.ts +2 -0
- package/lib/materialIcon.d.mts +21 -0
- package/lib/materialIcon.mjs +29 -0
- package/lib/materialIcon.test.mjs +45 -0
- package/lib/menu-layout.d.mts +11 -0
- package/lib/menu-layout.mjs +14 -0
- package/lib/menu-layout.test.mjs +23 -0
- package/lib/menu.d.mts +59 -0
- package/lib/menu.mjs +129 -0
- package/lib/menu.test.mjs +291 -0
- package/lib/rehype-emphasize.d.mts +4 -0
- package/lib/rehype-emphasize.mjs +20 -0
- package/lib/rehype-emphasize.test.mjs +39 -0
- package/lib/section-in-view.d.mts +8 -0
- package/lib/section-in-view.mjs +25 -0
- package/lib/section-in-view.test.mjs +35 -0
- package/lib/section-marker.d.mts +5 -0
- package/lib/section-marker.mjs +18 -0
- package/lib/section-marker.test.mjs +22 -0
- package/lib/section-ranges.d.mts +12 -0
- package/lib/section-ranges.mjs +43 -0
- package/lib/section-ranges.test.mjs +61 -0
- package/lib/sheet-stops.d.mts +68 -0
- package/lib/sheet-stops.mjs +198 -0
- package/lib/sheet-stops.test.mjs +155 -0
- package/lib/still-timer.d.mts +5 -0
- package/lib/still-timer.mjs +17 -0
- package/lib/still-timer.test.mjs +42 -0
- package/lib/text-sections.d.mts +10 -0
- package/lib/text-sections.mjs +45 -0
- package/lib/text-sections.test.mjs +78 -0
- package/lib/video.mjs +2 -1
- package/lib/without-h1.d.mts +1 -0
- package/lib/without-h1.mjs +7 -0
- package/lib/without-h1.test.mjs +26 -0
- package/lib/word-at.d.mts +6 -0
- package/lib/word-at.mjs +20 -0
- package/lib/word-at.test.mjs +19 -0
- package/package.json +3 -1
- package/pages/editor/version.json.js +22 -2
- package/scripts/already-checked.mjs +11 -0
- package/scripts/already-checked.test.mjs +18 -0
- package/scripts/build-settings-problems.mjs +27 -0
- package/scripts/build-settings-problems.test.mjs +35 -0
- package/scripts/check-content.mjs +42 -4
- package/scripts/check-worker.mjs +96 -4
- package/scripts/ci-build.mjs +85 -0
- package/scripts/ci-build.test.mjs +137 -0
- package/scripts/editor-roundtrip.mjs +62 -3
- package/scripts/editor-screens.mjs +21 -7
- package/scripts/git-env.mjs +6 -0
- package/scripts/step-command.mjs +34 -0
- package/scripts/step-command.test.mjs +50 -0
- package/scripts/text-problems.mjs +83 -0
- package/scripts/text-problems.test.mjs +74 -0
- package/scripts/worker-modules.mjs +11 -1
- package/site/breakpoints.d.mts +6 -0
- package/site/breakpoints.mjs +52 -0
- package/site/breakpoints.test.mjs +36 -0
- package/site/casing.d.mts +3 -0
- package/site/casing.mjs +26 -0
- package/site/content.config.ts +2 -0
- package/site/content.ts +2 -4
- package/site/defineSite.d.mts +1 -0
- package/site/defineSite.mjs +3 -0
- package/site/home.ts +3 -2
- package/site/icons.d.mts +116 -4
- package/site/icons.mjs +62 -6
- package/site/images.mjs +1 -1
- package/site/live-diff.d.mts +22 -0
- package/site/live-diff.mjs +98 -0
- package/site/live-diff.test.mjs +235 -0
- package/site/materialSymbols.d.mts +5 -0
- package/site/materialSymbols.mjs +19 -0
- package/site/sections.ts +1 -1
- package/site/theme.d.mts +1 -1
- package/site/theme.mjs +7 -1
- package/src/ActionParts.tsx +1 -1
- package/src/Advanced.tsx +101 -20
- package/src/App.tsx +47 -121
- package/src/AppList.tsx +1 -1
- package/src/BlockParts.tsx +3 -3
- package/src/Colors.tsx +1 -1
- package/src/CommunicationParts.tsx +1 -1
- package/src/ContainmentParts.tsx +80 -28
- package/src/Deleted.tsx +18 -4
- package/src/EditorFooter.tsx +14 -0
- package/src/EntryForm.tsx +75 -39
- package/src/FormFrame.tsx +3 -8
- package/src/Library.tsx +46 -51
- package/src/Licenses.tsx +27 -11
- package/src/MainDrawerParts.tsx +60 -0
- package/src/MainMenu.tsx +136 -0
- package/src/MarkdownEditor.tsx +175 -32
- package/src/Media.tsx +69 -16
- package/src/MediaTags.tsx +91 -0
- package/src/ModalSheetSample.tsx +47 -0
- package/src/NavigationParts.tsx +67 -21
- package/src/NavigationRestParts.tsx +1 -1
- package/src/Parts.tsx +71 -0
- package/src/PartsShown.tsx +1 -1
- package/src/PickerParts.tsx +2 -2
- package/src/Redirects.tsx +3 -2
- package/src/Screen.tsx +93 -90
- package/src/SelectionParts.tsx +22 -6
- package/src/SiteLogoLink.tsx +14 -0
- package/src/StartFab.tsx +22 -0
- package/src/StartStatus.tsx +1 -1
- package/src/autoTags.d.mts +28 -0
- package/src/autoTags.mjs +68 -0
- package/src/autoTags.test.mjs +109 -0
- package/src/buildSettings.test.mjs +20 -0
- package/src/buildSettings.ts +20 -0
- package/src/checkSite.ts +14 -1
- package/src/deploy.ts +7 -9
- package/src/editor.css +150 -17
- package/src/eyebrow.d.mts +5 -0
- package/src/eyebrow.mjs +53 -0
- package/src/eyebrowState.d.mts +21 -0
- package/src/eyebrowState.mjs +62 -0
- package/src/eyebrowState.test.mjs +78 -0
- package/src/fileRecords.d.mts +115 -0
- package/src/fileRecords.mjs +224 -0
- package/src/fileRecords.test.mjs +224 -0
- package/src/fileTagFilter.d.mts +30 -0
- package/src/fileTagFilter.mjs +44 -0
- package/src/fileTagFilter.test.mjs +57 -0
- package/src/fileTags.d.mts +56 -0
- package/src/fileTags.mjs +100 -0
- package/src/fileTags.test.mjs +127 -0
- package/src/firstHeading.d.mts +6 -0
- package/src/firstHeading.mjs +37 -0
- package/src/i18n/da-DK/advanced.ts +27 -12
- package/src/i18n/da-DK/app.ts +9 -7
- package/src/i18n/da-DK/apps.ts +16 -16
- package/src/i18n/da-DK/blockSettings.ts +9 -9
- package/src/i18n/da-DK/blocks.ts +2 -2
- package/src/i18n/da-DK/check.ts +5 -2
- package/src/i18n/da-DK/cms.ts +14 -12
- package/src/i18n/da-DK/colors.ts +5 -5
- package/src/i18n/da-DK/common.ts +2 -2
- package/src/i18n/da-DK/company.ts +3 -3
- package/src/i18n/da-DK/consents.ts +6 -6
- package/src/i18n/da-DK/forms.ts +6 -6
- package/src/i18n/da-DK/history.ts +6 -6
- package/src/i18n/da-DK/keywords.ts +2 -2
- package/src/i18n/da-DK/library.ts +17 -15
- package/src/i18n/da-DK/licenses.ts +4 -4
- package/src/i18n/da-DK/media.ts +10 -4
- package/src/i18n/da-DK/parts.ts +62 -25
- package/src/i18n/da-DK/preview.ts +5 -6
- package/src/i18n/da-DK/redirects.ts +7 -7
- package/src/i18n/da-DK/seo.ts +1 -1
- package/src/i18n/da-DK/text.ts +2 -0
- package/src/i18n/da-DK/theme.ts +5 -5
- package/src/i18n/en-US/advanced.ts +34 -18
- package/src/i18n/en-US/app.ts +7 -5
- package/src/i18n/en-US/apps.ts +9 -9
- package/src/i18n/en-US/blockSettings.ts +4 -4
- package/src/i18n/en-US/blocks.ts +6 -6
- package/src/i18n/en-US/check.ts +6 -3
- package/src/i18n/en-US/cms.ts +15 -14
- package/src/i18n/en-US/colors.ts +18 -18
- package/src/i18n/en-US/common.ts +3 -3
- package/src/i18n/en-US/consents.ts +4 -4
- package/src/i18n/en-US/forms.ts +5 -5
- package/src/i18n/en-US/history.ts +2 -2
- package/src/i18n/en-US/library.ts +21 -15
- package/src/i18n/en-US/licenses.ts +6 -6
- package/src/i18n/en-US/media.ts +7 -1
- package/src/i18n/en-US/parts.ts +60 -21
- package/src/i18n/en-US/preview.ts +5 -6
- package/src/i18n/en-US/redirects.ts +3 -3
- package/src/i18n/en-US/seo.ts +1 -1
- package/src/i18n/en-US/text.ts +3 -0
- package/src/i18n/en-US/theme.ts +6 -6
- package/src/icon.tsx +90 -27
- package/src/licenseData.test.mjs +28 -0
- package/src/licenseData.ts +12 -1
- package/src/markdown.d.mts +2 -0
- package/src/markdown.mjs +73 -5
- package/src/markdown.test.mjs +74 -0
- package/src/materialIcons.ts +8 -0
- package/src/menuChildren.ts +36 -0
- package/src/menuOpen.ts +34 -0
- package/src/pagePreview.tsx +23 -19
- package/src/r2Caching.ts +14 -0
- package/src/sectionFrame.ts +39 -0
- package/src/sectionState.d.mts +19 -0
- package/src/sectionState.mjs +31 -0
- package/src/sectionState.test.mjs +59 -0
- package/src/sections.d.mts +9 -7
- package/src/sections.mjs +74 -27
- package/src/sections.test.mjs +59 -0
- package/src/sidebar.ts +2 -2
- package/src/site.ts +27 -17
- package/src/stillCursor.d.mts +8 -0
- package/src/stillCursor.mjs +19 -0
- package/src/stillCursor.test.mjs +26 -0
- package/src/tagUses.d.mts +47 -0
- package/src/tagUses.mjs +121 -0
- package/src/tagUses.test.mjs +141 -0
- package/src/themeUi.ts +10 -0
- package/src/topBarSlot.tsx +37 -0
- package/src/ui.tsx +654 -150
- package/src/virtual.d.ts +5 -0
- package/starter/README.md +2 -1
- package/starter/package.json +2 -1
- package/starter/wrangler.jsonc +3 -2
- package/theme/blocks/cards/LinkCards.tsx +2 -1
- package/theme/blocks/hero/View.tsx +2 -1
- package/theme/blocks/video/View.tsx +2 -1
- package/theme/components/BlogIndex.astro +2 -2
- package/theme/components/BlogPosts.astro +1 -1
- package/theme/components/BlogPosts.tsx +9 -6
- package/theme/components/Container.astro +1 -1
- package/theme/components/CookieConsent.astro +1 -1
- package/theme/components/EndBlock.astro +1 -1
- package/theme/components/Footer.astro +5 -5
- package/theme/components/Header.astro +3 -3
- package/theme/components/Icon.astro +2 -2
- package/theme/components/MobileMenu.tsx +72 -61
- package/theme/components/MobileNav.astro +2 -2
- package/theme/components/PageHeader.astro +2 -2
- package/theme/components/PageSection.astro +1 -1
- package/theme/components/PopupButton.tsx +2 -2
- package/theme/components/PopupView.tsx +2 -2
- package/theme/components/PostList.tsx +7 -3
- package/theme/components/RichText.astro +22 -7
- package/theme/components/Section.astro +17 -10
- package/theme/components/SectionHeading.tsx +1 -1
- package/theme/components/home/PageBlock.astro +4 -4
- package/theme/components/home/Text.astro +3 -3
- package/theme/layouts/PageLayout.astro +3 -3
- package/theme/layouts/PostLayout.astro +9 -8
- package/theme/styles/base.css +13 -3
- package/theme/styles/blocks.css +97 -10
- package/theme/styles/components.css +9 -9
- package/theme/styles/elevation.test.mjs +168 -0
- package/theme/styles/global.css +65 -59
- package/theme/styles/ui.css +460 -131
- package/theme/templates/Home.astro +1 -1
- package/theme/templates/New.astro +5 -5
- package/theme/templates/NotFound.astro +2 -2
- package/theme/ui/BottomSheet.tsx +118 -21
- package/theme/ui/BottomSheetResizeIcon.tsx +13 -0
- package/theme/ui/Button.tsx +4 -3
- package/theme/ui/Dialog.tsx +1 -1
- package/theme/ui/FabMenu.tsx +13 -12
- package/theme/ui/LoadingIndicator.tsx +1 -1
- package/theme/ui/NavigationDrawer.tsx +116 -15
- package/theme/ui/NavigationRail.tsx +22 -13
- package/theme/ui/Search.tsx +1 -1
- package/theme/ui/SettingRow.tsx +31 -0
- package/theme/ui/Switch.tsx +11 -11
- package/theme/ui/TextField.tsx +17 -6
- package/theme/ui/bottomSheet.script.ts +3 -2
- package/theme/ui/bottomSheetControl.ts +131 -46
- package/theme/ui/fabMenu.script.ts +69 -53
- package/theme/ui/modalSheetSize.ts +214 -0
- package/theme/ui/navigationDrawerControl.ts +35 -0
- package/theme/ui/search.script.ts +4 -2
- package/worker/index.js +3 -0
- package/worker/plan-status.js +46 -0
- package/worker/push.js +7 -5
- package/worker/r2-status.js +2 -1
- package/icons/lucide/desktop.svg +0 -1
- package/icons/material-symbols/desktop.svg +0 -1
- package/icons/phosphor/desktop.svg +0 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// The Material Symbols (Outlined) files as the module `virtual:material-symbols-ssr`, answered by themePlugin (theme.mjs):
|
|
2
|
+
// all of them as text on the server render (so a backup icon, icon.tsx, is in the built page's HTML at once), nothing in the browser
|
|
3
|
+
// (where src/materialIcons.ts is the one lazy chunk). Exposes MATERIAL_SSR and materialSsrModule(). Reads @material-symbols/svg-400.
|
|
4
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
|
|
7
|
+
/** The virtual module's id as imported (packages/editor/src/icon.tsx). */
|
|
8
|
+
export const MATERIAL_SSR = 'virtual:material-symbols-ssr';
|
|
9
|
+
|
|
10
|
+
/** The module's source: `{ '/node_modules/@material-symbols/svg-400/outlined/<name>.svg': '<svg …>' }`, the keys as
|
|
11
|
+
* import.meta.glob names them (src/materialIcons.ts). With `ssr` false: an empty object. */
|
|
12
|
+
export function materialSsrModule(ssr) {
|
|
13
|
+
if (!ssr) return 'export default {};';
|
|
14
|
+
const dir = new URL('node_modules/@material-symbols/svg-400/outlined/', pathToFileURL(`${process.cwd()}/`));
|
|
15
|
+
const files = {};
|
|
16
|
+
for (const name of readdirSync(dir))
|
|
17
|
+
if (name.endsWith('.svg')) files[`/node_modules/@material-symbols/svg-400/outlined/${name}`] = readFileSync(new URL(name, dir), 'utf8');
|
|
18
|
+
return `export default ${JSON.stringify(files)};`;
|
|
19
|
+
}
|
package/site/sections.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// The space above a section of the blog page and the 404 page (packages/editor/site/standardPages.ts): less above the first, right
|
|
2
2
|
// under the page's header.
|
|
3
|
-
export const space = (i: number) => (i === 0 ? 'mt-10
|
|
3
|
+
export const space = (i: number) => (i === 0 ? 'mt-10 medium:mt-14' : 'mt-16 medium:mt-24');
|
package/site/theme.d.mts
CHANGED
|
@@ -66,7 +66,7 @@ export function themePlugin(): {
|
|
|
66
66
|
optimizeDeps?: undefined;
|
|
67
67
|
};
|
|
68
68
|
resolveId(source: any, importer: any): string | null;
|
|
69
|
-
load(id: any): string | null;
|
|
69
|
+
load(id: any, options: any): string | null;
|
|
70
70
|
};
|
|
71
71
|
/** What a block is missing to be drawn from its view (packages/editor/src/standard/view.tsx): a standard block's
|
|
72
72
|
* contract its view.ts and load.ts, an app's block (src/apps/<app>/blocks/<type>/) its viewOf.ts, load.ts and View.tsx; and the
|
package/site/theme.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
11
11
|
import { svgParts } from '@morphemecms/cms/lib/svg.mjs';
|
|
12
12
|
import { declarationProblems, ownerId } from '@morphemecms/cms/lib/licenses.mjs';
|
|
13
13
|
import { installedPacks, isIconName, packFolder, packProblems, phosphorFile, svgProblems, usedIcons } from './icons.mjs';
|
|
14
|
+
import { MATERIAL_SSR, materialSsrModule } from './materialSymbols.mjs';
|
|
14
15
|
import { ICON_PACK_FILE, ICON_PACK_LICENSE, SYSTEM_PACKS, THEME_PACKS_FOLDER, packInUse, packTheme, themePackId } from './iconPacks.mjs';
|
|
15
16
|
import { MOTION_SCHEMES } from '@morphemecms/cms/lib/motion-schemes.mjs';
|
|
16
17
|
import { storeImportProblems } from './storeTheme.mjs';
|
|
@@ -226,6 +227,7 @@ const PHOSPHOR_REACT = '@phosphor-icons/react';
|
|
|
226
227
|
const PHOSPHOR_FILE = /@phosphor-icons\/core\/assets\/(?:regular|fill)\/([a-z0-9-]+)\.svg\?raw$/;
|
|
227
228
|
const REACT_ICONS = '\0site-theme-icons';
|
|
228
229
|
const FILE_ICON = '\0site-theme-icon:';
|
|
230
|
+
const MATERIAL_SSR_ID = `\0${MATERIAL_SSR}`;
|
|
229
231
|
|
|
230
232
|
/** The React component's name of a Phosphor icon by its file's name (`caret-right` → CaretRight), when Phosphor has it. */
|
|
231
233
|
const reactName = (name) => {
|
|
@@ -455,6 +457,8 @@ export const aliases = () => ({
|
|
|
455
457
|
* pack and no icons of its own nothing is changed. The pack is read when the site is built, so a new choice
|
|
456
458
|
* (src/data/icons.json) or theme is in the site and the editor after the next build. */
|
|
457
459
|
export function themePlugin() {
|
|
460
|
+
// With forward slashes, as Vite writes ids: on Windows a mixed id (C:\…\theme\components/MobileNav.astro) hides a
|
|
461
|
+
// component's script from Astro, which looks it up with forward slashes.
|
|
458
462
|
const themes = [activeTheme(), STANDARD_THEME].map((t) => fileURLToPath(new URL(`${themeFolder(t)}/`, root)).replaceAll('\\', '/'));
|
|
459
463
|
const icons = themeIcons();
|
|
460
464
|
const own = Object.keys(icons).length > 0;
|
|
@@ -467,6 +471,7 @@ export function themePlugin() {
|
|
|
467
471
|
// The theme's icons import Phosphor's own.
|
|
468
472
|
if (source === PHOSPHOR_REACT) return own && importer !== REACT_ICONS ? REACT_ICONS : null;
|
|
469
473
|
if (source === ALL_ICONS) return '\0' + ALL_ICONS;
|
|
474
|
+
if (source === MATERIAL_SSR) return MATERIAL_SSR_ID;
|
|
470
475
|
const icon = source.match(PHOSPHOR_FILE)?.[1];
|
|
471
476
|
if (icon) return svgParts(icons[icon]) ? FILE_ICON + icon : null;
|
|
472
477
|
if (source.startsWith('@site/')) return siteFile(source.slice('@site/'.length));
|
|
@@ -479,7 +484,8 @@ export function themePlugin() {
|
|
|
479
484
|
if (statSync(dir + file + ext, { throwIfNoEntry: false })?.isFile()) return dir + file + ext;
|
|
480
485
|
return null;
|
|
481
486
|
},
|
|
482
|
-
load(id) {
|
|
487
|
+
load(id, options) {
|
|
488
|
+
if (id === MATERIAL_SSR_ID) return materialSsrModule(options?.ssr);
|
|
483
489
|
if (id === REACT_ICONS) return reactIcons(icons);
|
|
484
490
|
if (id === '\0' + ALL_ICONS)
|
|
485
491
|
return allIconsModule(fileURLToPath(new URL('node_modules/@phosphor-icons/core/assets/regular/', root)), icons);
|
package/src/ActionParts.tsx
CHANGED
|
@@ -187,7 +187,7 @@ export default function ActionParts() {
|
|
|
187
187
|
<SiteFabMenu key={size} size={size} icon="chats" label={say.contact} closeLabel={say.close} items={fabItems} />
|
|
188
188
|
))}
|
|
189
189
|
</div>
|
|
190
|
-
<div className="grid gap-6
|
|
190
|
+
<div className="grid gap-6 medium:grid-cols-2">
|
|
191
191
|
{fabMenuKinds.map((kind) => (
|
|
192
192
|
<div key={kind} className="flex justify-end pt-52">
|
|
193
193
|
<SiteFabMenu kind={kind} icon="chats" label={say.contact} closeLabel={say.close} items={fabItems} open />
|
package/src/Advanced.tsx
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
// saved at once in its own commit. Off, the Worker asks GitHub for everything. It can only be turned on when Cloudflare
|
|
4
4
|
// has the bucket (the Worker asks, /api/r2), else a deploy would fail; under the switch, why not, and, until the site is
|
|
5
5
|
// built with the change, that it comes then. Above it, where the bucket is kept ("Placering": placed by Cloudflare, or
|
|
6
|
-
// within the EU), changed only while it is off, also in its own commit.
|
|
6
|
+
// within the EU, a MenuRow with the choice in a dropdown at the right), changed only while it is off, also in its own commit. Under it, the two build switches ("Builds":
|
|
7
|
+
// fast content builds, paid build plan) in src/data/build.json (buildSettings.ts), each saved at once in its own commit. The paid
|
|
8
|
+
// build plan can only be turned on when Cloudflare says the account has Workers Paid (the Worker asks, /api/plan); under it, why not.
|
|
7
9
|
import { useEffect, useState } from 'react';
|
|
10
|
+
import { ArrowSquareOut } from '@phosphor-icons/react';
|
|
8
11
|
import Screen from './Screen';
|
|
9
|
-
import { EmptyNote,
|
|
12
|
+
import { EmptyNote, GroupTitle, LinkButton, MenuRow, ProblemNote, SupportingText, SwitchRow } from './ui';
|
|
10
13
|
import { ConflictError, type RepoFile } from './github';
|
|
11
14
|
import type { Env } from './App';
|
|
12
15
|
import { savedText } from './deploy';
|
|
@@ -17,12 +20,16 @@ import {
|
|
|
17
20
|
jurisdictionName,
|
|
18
21
|
jurisdictionOf,
|
|
19
22
|
r2CachingOn,
|
|
23
|
+
planStatus,
|
|
20
24
|
r2Status,
|
|
21
25
|
withJurisdiction,
|
|
22
26
|
withR2Caching,
|
|
23
27
|
type BucketState,
|
|
28
|
+
type PaidState,
|
|
24
29
|
} from './r2Caching';
|
|
25
30
|
import { config } from '@site/editor';
|
|
31
|
+
import { buildSettingsOf, withBuildSetting, type BuildSetting } from './buildSettings';
|
|
32
|
+
import { BUILD_SETTINGS_PATH } from '../lib/build-settings.mjs';
|
|
26
33
|
import { siteT, t } from './i18n';
|
|
27
34
|
|
|
28
35
|
export const ADVANCED_ROUTE = 'avanceret';
|
|
@@ -30,12 +37,21 @@ export const ADVANCED_ROUTE = 'avanceret';
|
|
|
30
37
|
// "Placering" when Cloudflare places the bucket (no jurisdiction).
|
|
31
38
|
const AUTO = 'auto';
|
|
32
39
|
|
|
40
|
+
// Cloudflare's dashboard sends on to the account's Billing page.
|
|
41
|
+
const BILLING_URL = 'https://dash.cloudflare.com/?to=/:account/billing';
|
|
42
|
+
|
|
33
43
|
export default function Advanced({ env }: { env: Env }) {
|
|
34
44
|
const [file, setFile] = useState<RepoFile | null>(null);
|
|
35
45
|
const [status, setStatus] = useState<{ bound: boolean; bucket: BucketState } | null>(null);
|
|
46
|
+
// The build settings file: undefined while it loads, null when the site has none yet (the defaults apply).
|
|
47
|
+
const [build, setBuild] = useState<RepoFile | null | undefined>(undefined);
|
|
48
|
+
// What Cloudflare says of the account's plan: null while it is asked.
|
|
49
|
+
const [paid, setPaid] = useState<PaidState | null>(null);
|
|
50
|
+
const [buildProblem, setBuildProblem] = useState('');
|
|
36
51
|
const [problem, setProblem] = useState('');
|
|
37
52
|
const [saving, setSaving] = useState(false);
|
|
38
53
|
useEffect(() => {
|
|
54
|
+
planStatus().then(setPaid);
|
|
39
55
|
env
|
|
40
56
|
.load([config.wrangler])
|
|
41
57
|
.then(([found]) => {
|
|
@@ -44,6 +60,10 @@ export default function Advanced({ env }: { env: Env }) {
|
|
|
44
60
|
ask(found.text);
|
|
45
61
|
})
|
|
46
62
|
.catch((e) => setProblem(t.advanced.notLoaded((e as Error).message)));
|
|
63
|
+
env
|
|
64
|
+
.load([BUILD_SETTINGS_PATH])
|
|
65
|
+
.then(([found]) => setBuild(found ?? null))
|
|
66
|
+
.catch((e) => setBuildProblem(t.advanced.buildSettingsNotLoaded((e as Error).message)));
|
|
47
67
|
}, []);
|
|
48
68
|
|
|
49
69
|
// What Cloudflare says of the bucket the settings name, where they say it is kept.
|
|
@@ -74,26 +94,64 @@ export default function Advanced({ env }: { env: Env }) {
|
|
|
74
94
|
...(jurisdiction && !(JURISDICTIONS as readonly string[]).includes(jurisdiction) ? [jurisdiction] : []),
|
|
75
95
|
];
|
|
76
96
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const path = file.path;
|
|
97
|
+
// Saves one file as its own commit (`oid` null: a new file); the file as saved, or null when it was not.
|
|
98
|
+
async function commitFile(path: string, oid: string | null, text: string, message: string, change: string): Promise<RepoFile | null> {
|
|
80
99
|
setSaving(true);
|
|
81
100
|
env.notify({ text: t.common.saving });
|
|
82
101
|
try {
|
|
83
|
-
const oids = await env.commit(message, [{ path, content: text, oid
|
|
102
|
+
const oids = await env.commit(message, [{ path, content: text, oid }], change);
|
|
84
103
|
const saved = { path, oid: oids[path], text };
|
|
85
|
-
setFile(saved);
|
|
86
104
|
env.onSaved(saved);
|
|
87
105
|
env.notify({ text: savedText(change), kind: 'saved' });
|
|
88
|
-
return
|
|
106
|
+
return saved;
|
|
89
107
|
} catch (e) {
|
|
90
108
|
env.notify({ text: e instanceof ConflictError ? e.message : t.common.notSaved((e as Error).message), error: true, kind: 'failed' });
|
|
91
|
-
return
|
|
109
|
+
return null;
|
|
92
110
|
} finally {
|
|
93
111
|
setSaving(false);
|
|
94
112
|
}
|
|
95
113
|
}
|
|
96
114
|
|
|
115
|
+
async function save(text: string, message: string, change: string): Promise<boolean> {
|
|
116
|
+
if (!file) return false;
|
|
117
|
+
const saved = await commitFile(file.path, file.oid, text, message, change);
|
|
118
|
+
if (saved) setFile(saved);
|
|
119
|
+
return !!saved;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const settings = buildSettingsOf(build?.text);
|
|
123
|
+
const buildSwitches: { key: BuildSetting; label: string; hint: string; change: string; commit: (on: boolean) => string }[] = [
|
|
124
|
+
{
|
|
125
|
+
key: 'contentLane',
|
|
126
|
+
label: t.advanced.fastContentBuilds,
|
|
127
|
+
hint: t.advanced.fastContentBuildsHint,
|
|
128
|
+
change: t.app.changes.fastContentBuilds,
|
|
129
|
+
commit: siteT.advanced.fastContentBuildsCommit,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
key: 'paidBuilds',
|
|
133
|
+
label: t.advanced.paidBuilds,
|
|
134
|
+
hint: t.advanced.paidBuildsHint,
|
|
135
|
+
change: t.app.changes.paidBuilds,
|
|
136
|
+
commit: siteT.advanced.paidBuildsCommit,
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
// Paid builds are off until Cloudflare says the account has Workers Paid; on, they can always be turned off.
|
|
141
|
+
const paidLocked = (key: BuildSetting, on: boolean) => key === 'paidBuilds' && !on && paid !== 'yes';
|
|
142
|
+
|
|
143
|
+
async function turnBuild(key: BuildSetting, next: boolean, commit: (on: boolean) => string, change: string) {
|
|
144
|
+
if (build === undefined || (next && paidLocked(key, settings[key]))) return;
|
|
145
|
+
const saved = await commitFile(
|
|
146
|
+
BUILD_SETTINGS_PATH,
|
|
147
|
+
build?.oid ?? null,
|
|
148
|
+
withBuildSetting(build?.text, key, next),
|
|
149
|
+
commit(next),
|
|
150
|
+
t.app.changes[next ? 'turnedOn' : 'turnedOff'](change),
|
|
151
|
+
);
|
|
152
|
+
if (saved) setBuild(saved);
|
|
153
|
+
}
|
|
154
|
+
|
|
97
155
|
async function turn(next: boolean) {
|
|
98
156
|
if (!file || on === null || (next && !ready)) return;
|
|
99
157
|
await save(
|
|
@@ -124,17 +182,14 @@ export default function Advanced({ env }: { env: Env }) {
|
|
|
124
182
|
<ProblemNote>{t.advanced.noLine}</ProblemNote>
|
|
125
183
|
) : (
|
|
126
184
|
<>
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
/>
|
|
136
|
-
</FormField>
|
|
137
|
-
</div>
|
|
185
|
+
<MenuRow
|
|
186
|
+
label={t.advanced.place}
|
|
187
|
+
hint={t.advanced.placeHint}
|
|
188
|
+
value={jurisdiction ?? AUTO}
|
|
189
|
+
options={places.map((p) => ({ value: p, label: p === AUTO ? t.advanced.placeAuto : jurisdictionName(p) }))}
|
|
190
|
+
disabled={saving || on}
|
|
191
|
+
onChange={place}
|
|
192
|
+
/>
|
|
138
193
|
<SwitchRow
|
|
139
194
|
label={t.blockSettings.active}
|
|
140
195
|
texts={[t.library.on, t.library.off]}
|
|
@@ -147,6 +202,32 @@ export default function Advanced({ env }: { env: Env }) {
|
|
|
147
202
|
)}
|
|
148
203
|
</div>
|
|
149
204
|
</section>
|
|
205
|
+
<section className="mb-8">
|
|
206
|
+
<GroupTitle>{t.advanced.builds}</GroupTitle>
|
|
207
|
+
<div className="px-4">
|
|
208
|
+
{buildProblem ? (
|
|
209
|
+
<ProblemNote>{buildProblem}</ProblemNote>
|
|
210
|
+
) : (
|
|
211
|
+
<>
|
|
212
|
+
{buildSwitches.map(({ key, label, hint, change, commit }) => (
|
|
213
|
+
<SwitchRow
|
|
214
|
+
key={key}
|
|
215
|
+
label={label}
|
|
216
|
+
hint={hint}
|
|
217
|
+
texts={[t.library.on, t.library.off]}
|
|
218
|
+
selected={settings[key]}
|
|
219
|
+
disabled={saving || build === undefined || paidLocked(key, settings[key])}
|
|
220
|
+
onChange={(next) => turnBuild(key, next, commit, change)}
|
|
221
|
+
/>
|
|
222
|
+
))}
|
|
223
|
+
{paid && !settings.paidBuilds && paid !== 'yes' && <SupportingText className="mt-2">{t.advanced.plan[paid]}</SupportingText>}
|
|
224
|
+
<LinkButton href={BILLING_URL} target="_blank" rel="noopener" kind="text" size="small" icon={ArrowSquareOut}>
|
|
225
|
+
{t.advanced.billing}
|
|
226
|
+
</LinkButton>
|
|
227
|
+
</>
|
|
228
|
+
)}
|
|
229
|
+
</div>
|
|
230
|
+
</section>
|
|
150
231
|
</Screen>
|
|
151
232
|
);
|
|
152
233
|
}
|
package/src/App.tsx
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
// The editor at /editor/ (docs/editor.md): sign in with GitHub, pick a part of the site, edit and
|
|
2
|
-
// save. The address after # says what is open, so reloading keeps it:
|
|
2
|
+
// save. The layout: the full-height navigation rail or drawer (MainMenu.tsx) with, beside it, the top bar (topBarSlot.tsx) over the screen. The address after # says what is open, so reloading keeps it:
|
|
3
3
|
// (empty) the start page
|
|
4
4
|
// liste/<id> the list of a part, e.g. liste/priser, and liste/<id>/<app> a tab an installed app adds to it
|
|
5
5
|
// (AppDefinition.tabs): liste/cookies/cookiescanner
|
|
6
6
|
// header what is in the header on every page (the menu)
|
|
7
7
|
// lister the short lists (the testimonials, the apps' lists)
|
|
8
|
-
// indstillinger
|
|
9
|
-
//
|
|
8
|
+
// indstillinger/appstore the app store, its apps, and indstillinger/appstore/temaer its themes (AppList.tsx StorePage); the
|
|
9
|
+
// other settings are screens of their own, in the drawer's last section (MainMenu.tsx)
|
|
10
10
|
// ny/<id> a new entry, ny/<id>/<kind> one of a kind (a pop-up: standard, or an app's)
|
|
11
11
|
// virksomheden the business, and virksomheden/<part> its parts: kontakt, betaling; and
|
|
12
12
|
// virksomheden/betaling/<part> its own parts: mobilepay, bank
|
|
13
13
|
// <screen> a screen with parts of the site's own (partScreens), and <screen>/<part> its parts:
|
|
14
14
|
// privatliv/cookiebanner, privatliv/privatlivssider, or one of the editor's own screens (Part.opens):
|
|
15
15
|
// privatliv/samtykker the visitors' answers in the cookie banner (Consents.tsx); and the settings of the apps installed with
|
|
16
|
-
// its category: maaling/google-analytics
|
|
16
|
+
// its category: maaling/google-analytics
|
|
17
17
|
// filer the uploaded pictures, PDFs, videos and zip files (Media.tsx)
|
|
18
18
|
// seo "Søgemaskineoptimering" (Library.tsx Seo): seo/videresendelser the redirects' two lists
|
|
19
19
|
// (Redirects.tsx): seo/videresendelser/alle the redirects, and seo/videresendelser/ikke-fundet
|
|
@@ -138,10 +138,8 @@ import {
|
|
|
138
138
|
List,
|
|
139
139
|
Lists,
|
|
140
140
|
PartsHome,
|
|
141
|
-
partsIn,
|
|
142
141
|
PartsScreen,
|
|
143
142
|
Seo,
|
|
144
|
-
Settings,
|
|
145
143
|
Start,
|
|
146
144
|
Theme,
|
|
147
145
|
ThemePage,
|
|
@@ -150,24 +148,14 @@ import {
|
|
|
150
148
|
MotionSchemes,
|
|
151
149
|
motionOf,
|
|
152
150
|
} from './Library';
|
|
153
|
-
import {
|
|
154
|
-
|
|
155
|
-
asWritten,
|
|
156
|
-
blockParts,
|
|
157
|
-
collections,
|
|
158
|
-
companyParts,
|
|
159
|
-
groups,
|
|
160
|
-
icons,
|
|
161
|
-
listRowsOf,
|
|
162
|
-
lists,
|
|
163
|
-
partScreens,
|
|
164
|
-
seoParts,
|
|
165
|
-
} from './site';
|
|
166
|
-
import { config, header, relationsOf, type SiteData, siteName, siteOf, testimonialsOf, usedBy, whatIsAt } from '@site/editor';
|
|
151
|
+
import { appearance, asWritten, blockParts, collections, companyParts, listRowsOf, partScreens, seoParts } from './site';
|
|
152
|
+
import { config, relationsOf, type SiteData, siteName, siteOf, testimonialsOf, usedBy, whatIsAt } from '@site/editor';
|
|
167
153
|
import { themes } from './themes';
|
|
168
154
|
import Screen from './Screen';
|
|
169
|
-
import { Button, EmptyNote,
|
|
170
|
-
import {
|
|
155
|
+
import { Button, EmptyNote, ProblemNote, startRipples } from './ui';
|
|
156
|
+
import { useMenuOpen } from './menuOpen';
|
|
157
|
+
import { MainMenu } from './MainMenu';
|
|
158
|
+
import { TopBarLayout } from './topBarSlot';
|
|
171
159
|
import { Ask, ask } from './ask';
|
|
172
160
|
import { LinkAsk } from './LinkSheet';
|
|
173
161
|
import { type ListRows, type Option, type TestimonialRow } from './fields';
|
|
@@ -285,6 +273,10 @@ const MOVED: Record<string, string> = {
|
|
|
285
273
|
videresendelser: 'seo/videresendelser',
|
|
286
274
|
'videresendelser/alle': 'seo/videresendelser/alle',
|
|
287
275
|
'videresendelser/ikke-fundet': 'seo/videresendelser/ikke-fundet',
|
|
276
|
+
// The settings of the booking and newsletter apps were on a screen "Promovering"; they are on the apps' own pages now.
|
|
277
|
+
promovering: 'apps',
|
|
278
|
+
'promovering/calendly': 'apps/calendly/indstillinger',
|
|
279
|
+
'promovering/mailchimp': 'apps/mailchimp/indstillinger',
|
|
288
280
|
[STORE_BEFORE]: STORE_ROUTE,
|
|
289
281
|
[`${STORE_BEFORE}/temaer`]: STORE_THEMES,
|
|
290
282
|
};
|
|
@@ -317,11 +309,6 @@ function Editor() {
|
|
|
317
309
|
// Whether someone is signed in (the Worker keeps the sign-in, packages/editor/worker/session.js): null until the Worker or this
|
|
318
310
|
// browser's copy of the files (cache.ts) has said.
|
|
319
311
|
const [signedIn, setSignedIn] = useState<boolean | null>(null);
|
|
320
|
-
// Whether the sidebar is open on a computer (sidebar.ts).
|
|
321
|
-
const sidebar = useSidebar();
|
|
322
|
-
// While it is closed: the mouse over the rail, which then shows the whole sidebar over the content.
|
|
323
|
-
const [peek, setPeek] = useState(false);
|
|
324
|
-
const peekTimer = useRef(0);
|
|
325
312
|
const [user, setUser] = useState<Viewer | null>(null);
|
|
326
313
|
const [files, setFiles] = useState<RepoFile[] | null>(null);
|
|
327
314
|
const [error, setError] = useState('');
|
|
@@ -329,10 +316,11 @@ function Editor() {
|
|
|
329
316
|
// Counts the visits to an address: a form starts fresh on each, but not when a new entry that was
|
|
330
317
|
// just saved gets its own address.
|
|
331
318
|
const [visit, setVisit] = useState(0);
|
|
332
|
-
// The messages at the bottom
|
|
319
|
+
// The messages at the bottom and the save bar stand next to the standard drawer while it is open (editor.css).
|
|
320
|
+
const { open: menuOpen } = useMenuOpen();
|
|
333
321
|
useEffect(() => {
|
|
334
|
-
document.documentElement.dataset.sidebar =
|
|
335
|
-
}, [
|
|
322
|
+
document.documentElement.dataset.sidebar = menuOpen ? 'open' : 'closed';
|
|
323
|
+
}, [menuOpen]);
|
|
336
324
|
// Stops waiting for the last save to be on the site.
|
|
337
325
|
const stopWaiting = useRef<() => void>(undefined);
|
|
338
326
|
// What the saves the editor waits for the website to have changed (save).
|
|
@@ -679,97 +667,35 @@ function Editor() {
|
|
|
679
667
|
);
|
|
680
668
|
}
|
|
681
669
|
|
|
682
|
-
// The part of the site that is open, for the sidebar: the menu under "Header", the lists and testimonials under
|
|
683
|
-
// "Lister", the front page under "Udseende".
|
|
684
|
-
const open = collectionOf(collections, route)?.collection;
|
|
685
|
-
const part = open?.id || route.replace(/^(liste|ny|slettet)\/([^/]+).*/, '$2');
|
|
686
|
-
const current = lists.includes(part)
|
|
687
|
-
? 'lister'
|
|
688
|
-
: header.includes(part)
|
|
689
|
-
? 'header'
|
|
690
|
-
: part.startsWith('virksomheden')
|
|
691
|
-
? 'virksomheden'
|
|
692
|
-
: part.startsWith('blokke')
|
|
693
|
-
? 'blokke'
|
|
694
|
-
: part.startsWith('udseende')
|
|
695
|
-
? 'udseende'
|
|
696
|
-
: part.startsWith('seo')
|
|
697
|
-
? 'seo'
|
|
698
|
-
: part.startsWith('apps')
|
|
699
|
-
? 'apps'
|
|
700
|
-
: (partScreens.find((s) => part.startsWith(`${s.id}/`))?.id ?? part);
|
|
701
|
-
|
|
702
670
|
return (
|
|
703
671
|
<>
|
|
704
|
-
<
|
|
705
|
-
{
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
>
|
|
720
|
-
<NavigationRail
|
|
721
|
-
label={t.library.sidebar}
|
|
722
|
-
menuLabel={sidebar ? t.library.closeSidebar : t.library.openSidebar}
|
|
723
|
-
expanded={sidebar}
|
|
724
|
-
peek={peek}
|
|
725
|
-
onExpandedChange={(open) => {
|
|
726
|
-
setPeek(false);
|
|
727
|
-
setSidebar(open);
|
|
728
|
-
}}
|
|
729
|
-
header={
|
|
730
|
-
<a href="#" className="flex min-w-0 items-center gap-3 font-semibold">
|
|
731
|
-
<img src="/editor/icon-192.png" alt="" className="size-7 shrink-0 rounded-lg" />
|
|
732
|
-
<span className="truncate">{siteName}</span>
|
|
733
|
-
</a>
|
|
734
|
-
}
|
|
735
|
-
>
|
|
736
|
-
{groups.map((g) => (
|
|
737
|
-
<NavigationRailSection key={g.title} label={g.title}>
|
|
738
|
-
{partsIn(g.ids, files ?? []).map(({ id, label, href, count }) => (
|
|
739
|
-
<NavigationRailItem
|
|
740
|
-
key={id}
|
|
741
|
-
href={href}
|
|
742
|
-
icon={icons[id]}
|
|
743
|
-
label={label}
|
|
744
|
-
count={files ? count : null}
|
|
745
|
-
selected={current === id}
|
|
746
|
-
/>
|
|
747
|
-
))}
|
|
748
|
-
</NavigationRailSection>
|
|
749
|
-
))}
|
|
750
|
-
</NavigationRail>
|
|
751
|
-
</aside>
|
|
752
|
-
|
|
753
|
-
<div className="min-w-0 flex-1">
|
|
754
|
-
{files ? (
|
|
755
|
-
<>
|
|
756
|
-
{user && !user.canSave && (
|
|
757
|
-
<p className="bg-accent px-4 py-3 pt-[calc(0.75rem+env(safe-area-inset-top))] text-center text-sm text-on-accent">
|
|
758
|
-
{t.app.cannotSave(user.login)}
|
|
759
|
-
</p>
|
|
760
|
-
)}
|
|
672
|
+
<TopBarLayout
|
|
673
|
+
aside={<MainMenu route={route} files={files} />}
|
|
674
|
+
above={
|
|
675
|
+
files &&
|
|
676
|
+
user &&
|
|
677
|
+
!user.canSave && (
|
|
678
|
+
<p className="bg-accent px-4 py-3 pt-[calc(0.75rem+env(safe-area-inset-top))] text-center text-sm text-on-accent">
|
|
679
|
+
{t.app.cannotSave(user.login)}
|
|
680
|
+
</p>
|
|
681
|
+
)
|
|
682
|
+
}
|
|
683
|
+
>
|
|
684
|
+
<>
|
|
685
|
+
<div className="editor-content min-w-0 flex-1">
|
|
686
|
+
{files ? (
|
|
761
687
|
<Page key={visit} route={route} files={files} env={env} user={user} builtAt={builtAt} onLogout={signOut} />
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
</
|
|
688
|
+
) : (
|
|
689
|
+
<Screen title={siteName}>
|
|
690
|
+
<EmptyNote busy={!error}>{error || t.app.loading}</EmptyNote>
|
|
691
|
+
</Screen>
|
|
692
|
+
)}
|
|
693
|
+
</div>
|
|
694
|
+
|
|
695
|
+
<LinkAsk groups={linkGroups} />
|
|
696
|
+
<Ask />
|
|
697
|
+
</>
|
|
698
|
+
</TopBarLayout>
|
|
773
699
|
</>
|
|
774
700
|
);
|
|
775
701
|
}
|
|
@@ -809,15 +735,15 @@ function formAt(route: string, files: RepoFile[]): { part: Part; at: string; pag
|
|
|
809
735
|
type PageProps = { route: string; files: RepoFile[]; env: Env; user: Viewer | null; builtAt: Date | null; onLogout: () => void };
|
|
810
736
|
|
|
811
737
|
function Page({ route, files, env, user, builtAt, onLogout }: PageProps) {
|
|
812
|
-
if (!route) return <Start
|
|
738
|
+
if (!route) return <Start builtAt={builtAt} />;
|
|
813
739
|
if (route === 'konto') return <Account user={user} files={files} onLogout={onLogout} />;
|
|
814
740
|
if (route === 'notifikationer') return <Notifications />;
|
|
815
|
-
if (route === 'indstillinger') return <Settings files={files} />;
|
|
816
741
|
if (route === 'header') return <Header files={files} />;
|
|
817
742
|
if (route === 'lister') return <Lists files={files} />;
|
|
818
743
|
if (route === 'virksomheden') return <Company files={files} />;
|
|
819
744
|
if (route === 'blokke') return <Blocks files={files} />;
|
|
820
|
-
if (route === 'filer'
|
|
745
|
+
if (route === 'filer' || route.startsWith('filer/'))
|
|
746
|
+
return <Media key={route} files={files} env={env} open={route === 'filer' ? undefined : '/' + route.slice('filer/'.length)} />;
|
|
821
747
|
if (route === 'seo') return <Seo />;
|
|
822
748
|
if (route === REDIRECTS_ROUTE) return <Redirects env={env} />;
|
|
823
749
|
if (route === SEO_SCORES_ROUTE) return <SeoScores files={files} env={env} />;
|
package/src/AppList.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// "Indstillinger" › "Apps" (#apps): the apps installed (src/apps/, apps.ts: the site's own and those from the app store,
|
|
2
|
-
// with one from the store not yet built as "Installeres
|
|
2
|
+
// with one from the store not yet built as "Installeres…"), each with who made it under its name and, from the store,
|
|
3
3
|
// that it has a newer version, with search (by name, who made it and what it does), the order (A–Å, Å–A, kept in this
|
|
4
4
|
// browser) and headings by the first letter, as the lists under "Indhold" have. "Indstillinger" › "Appstore"
|
|
5
5
|
// (#indstillinger/appstore): all apps, those in the app store (store.ts) and the site's own, installed or not, with search (by name, what they
|
package/src/BlockParts.tsx
CHANGED
|
@@ -291,7 +291,7 @@ function ButtonParts() {
|
|
|
291
291
|
* prefix and a suffix, the error's icon, a password with its button, a counter, read-only, and several lines (growing,
|
|
292
292
|
* and three rows); and a checkbox empty, ticked, with a line, disabled, indeterminate and wrong. */
|
|
293
293
|
function FieldParts() {
|
|
294
|
-
const fields = 'grid gap-6
|
|
294
|
+
const fields = 'grid gap-6 medium:grid-cols-2';
|
|
295
295
|
return (
|
|
296
296
|
<>
|
|
297
297
|
<Part name="TextField" use={screens.textField}>
|
|
@@ -380,7 +380,7 @@ function CardParts() {
|
|
|
380
380
|
<>
|
|
381
381
|
<Part name="Card" use={say.card}>
|
|
382
382
|
<Preview>
|
|
383
|
-
<div className="grid gap-4 p-4
|
|
383
|
+
<div className="grid gap-4 p-4 medium:grid-cols-2">
|
|
384
384
|
{cardKinds.map((k) => (
|
|
385
385
|
<SiteCard key={k} kind={k}>
|
|
386
386
|
<p className="font-medium">{say.kinds[k]}</p>
|
|
@@ -402,7 +402,7 @@ function CardParts() {
|
|
|
402
402
|
</Part>
|
|
403
403
|
<Part name="List" use={say.list}>
|
|
404
404
|
<Preview>
|
|
405
|
-
<div className="grid items-start gap-4 p-4
|
|
405
|
+
<div className="grid items-start gap-4 p-4 expanded:grid-cols-2">
|
|
406
406
|
<SiteCard>
|
|
407
407
|
<SiteList>
|
|
408
408
|
<SiteListItem headline={one} />
|
package/src/Colors.tsx
CHANGED
|
@@ -359,7 +359,7 @@ export default function Colors({ env, files, fallback }: { env: Env; files: Repo
|
|
|
359
359
|
first={preview(mode)}
|
|
360
360
|
main={
|
|
361
361
|
<>
|
|
362
|
-
<div className="grid gap-5
|
|
362
|
+
<div className="grid gap-5 medium:grid-cols-2">
|
|
363
363
|
{colorFields.map((field) => {
|
|
364
364
|
const id = `color-${mode}-${field.name}`;
|
|
365
365
|
return (
|
|
@@ -66,7 +66,7 @@ export default function CommunicationParts() {
|
|
|
66
66
|
</div>
|
|
67
67
|
</Example>
|
|
68
68
|
</div>
|
|
69
|
-
<SiteNavigationDrawer
|
|
69
|
+
<SiteNavigationDrawer>
|
|
70
70
|
<SiteNavigationDrawerRow>
|
|
71
71
|
<SiteNavigationDrawerItem href="">{say.home}</SiteNavigationDrawerItem>
|
|
72
72
|
</SiteNavigationDrawerRow>
|