@morphemecms/cms 0.12.0 → 0.13.0-next-20261106203613

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/defaults/blockTexts.ts +9 -9
  3. package/defaults/collections.ts +257 -257
  4. package/defaults/colors.ts +74 -74
  5. package/defaults/company.ts +9 -9
  6. package/defaults/editor.ts +65 -65
  7. package/defaults/i18n/da-DK/blockTexts.ts +12 -0
  8. package/defaults/i18n/da-DK/collections.ts +220 -0
  9. package/defaults/i18n/da-DK/colors.ts +102 -0
  10. package/defaults/i18n/da-DK/company.ts +7 -0
  11. package/defaults/i18n/da-DK/editor.ts +13 -0
  12. package/defaults/i18n/da-DK/index.ts +31 -0
  13. package/defaults/i18n/da-DK/internalLinks.ts +12 -0
  14. package/defaults/i18n/da-DK/opens.ts +7 -0
  15. package/defaults/i18n/da-DK/popup.ts +9 -0
  16. package/defaults/i18n/da-DK/privacy.ts +38 -0
  17. package/defaults/i18n/da-DK/rules.ts +56 -0
  18. package/defaults/i18n/da-DK/sitedata.ts +8 -0
  19. package/defaults/i18n/da-DK/standard.ts +8 -0
  20. package/defaults/i18n/da-DK/standardPages.ts +17 -0
  21. package/defaults/internalLinks.ts +8 -8
  22. package/defaults/opens.ts +2 -2
  23. package/defaults/popup.tsx +7 -7
  24. package/defaults/privacy.ts +37 -37
  25. package/defaults/rules.ts +68 -66
  26. package/defaults/schema.ts +1 -1
  27. package/defaults/sitedata.ts +4 -4
  28. package/defaults/standard.ts +9 -6
  29. package/defaults/standardPages.ts +14 -14
  30. package/defaults/texts.ts +7 -510
  31. package/lib/appRoute.d.mts +2 -2
  32. package/lib/appRoute.mjs +3 -3
  33. package/lib/appRoute.test.mjs +8 -8
  34. package/lib/licenses.mjs +2 -2
  35. package/lib/licenses.owner.test.mjs +9 -0
  36. package/lib/menu.mjs +4 -4
  37. package/lib/menu.test.mjs +101 -101
  38. package/lib/scheduled.mjs +1 -1
  39. package/lib/storeEntries.test.mjs +3 -0
  40. package/lib/translate.d.mts +11 -0
  41. package/lib/translate.mjs +13 -1
  42. package/lib/translate.test.mjs +23 -2
  43. package/package.json +1 -1
  44. package/scripts/app-screenshots.mjs +7 -7
  45. package/scripts/check-content.mjs +10 -1
  46. package/scripts/check-language.mjs +0 -1
  47. package/scripts/editor-screens.mjs +3 -3
  48. package/site/defineSite.mjs +2 -2
  49. package/site/schema.ts +3 -3
  50. package/src/Advanced.tsx +2 -2
  51. package/src/App.tsx +78 -77
  52. package/src/AppAdds.tsx +1 -1
  53. package/src/AppList.tsx +15 -15
  54. package/src/BlockDialog.tsx +1 -1
  55. package/src/BlockParts.tsx +6 -6
  56. package/src/BlockSettings.tsx +2 -2
  57. package/src/BrandForm.tsx +2 -2
  58. package/src/Colors.tsx +6 -6
  59. package/src/CompanyForm.tsx +1 -1
  60. package/src/Consents.tsx +3 -3
  61. package/src/DataForm.tsx +3 -3
  62. package/src/Deleted.tsx +3 -3
  63. package/src/EditorFooter.tsx +2 -2
  64. package/src/EntryForm.tsx +2 -2
  65. package/src/FooterSettings.tsx +2 -2
  66. package/src/HeaderSettings.tsx +3 -3
  67. package/src/Keywords.tsx +5 -5
  68. package/src/Library.tsx +71 -71
  69. package/src/Licenses.tsx +8 -8
  70. package/src/MainDrawerParts.tsx +3 -3
  71. package/src/MainMenu.tsx +1 -1
  72. package/src/Media.tsx +7 -7
  73. package/src/MediaTags.tsx +2 -2
  74. package/src/Notifications.tsx +2 -2
  75. package/src/Parts.tsx +17 -17
  76. package/src/Redirects.tsx +8 -8
  77. package/src/Screen.tsx +3 -3
  78. package/src/SeoScores.tsx +6 -6
  79. package/src/SideColumn.tsx +2 -2
  80. package/src/StartFab.tsx +2 -2
  81. package/src/ThemeStore.tsx +4 -4
  82. package/src/Tokens.tsx +2 -2
  83. package/src/apps.ts +5 -5
  84. package/src/checkSite.ts +86 -20
  85. package/src/editor.css +4 -16
  86. package/src/examples.ts +1 -1
  87. package/src/fabScreens.test.mjs +4 -4
  88. package/src/fabScreens.ts +4 -4
  89. package/src/i18n/da-DK/library.ts +2 -2
  90. package/src/i18n/da-DK/text.ts +1 -1
  91. package/src/i18n/en-US/apps.ts +1 -1
  92. package/src/i18n/en-US/common.ts +8 -8
  93. package/src/i18n/en-US/library.ts +5 -5
  94. package/src/i18n/en-US/parts.ts +3 -3
  95. package/src/i18n/en-US/text.ts +1 -1
  96. package/src/i18n/index.ts +4 -8
  97. package/src/images.ts +2 -2
  98. package/src/licenseData.ts +3 -3
  99. package/src/menuChildren.ts +5 -5
  100. package/src/movedRoutes.d.mts +8 -1
  101. package/src/movedRoutes.mjs +100 -19
  102. package/src/movedRoutes.test.mjs +123 -6
  103. package/src/nav.ts +1 -1
  104. package/src/notificationData.ts +1 -1
  105. package/src/schema.ts +2 -2
  106. package/src/site.ts +37 -13
  107. package/src/standard/accordion/index.tsx +1 -1
  108. package/src/standard/accordion/shared.ts +1 -1
  109. package/src/standard/buttons/index.tsx +1 -1
  110. package/src/standard/cards/index.tsx +1 -1
  111. package/src/standard/cards/shared.ts +1 -1
  112. package/src/standard/cookies/index.tsx +1 -1
  113. package/src/standard/posts/index.tsx +1 -1
  114. package/src/standard/testimonials/index.tsx +1 -1
  115. package/src/standard/testimonials/list.ts +1 -1
  116. package/src/store.ts +4 -4
  117. package/src/tagUses.d.mts +1 -1
  118. package/src/tagUses.mjs +2 -2
  119. package/src/tagUses.test.mjs +1 -1
  120. package/src/ui.tsx +7 -7
  121. package/theme/layouts/Base.astro +2 -2
@@ -4,7 +4,7 @@
4
4
  // packages/editor/src/icon.tsx), there or on one of the built site's pages. An icon drawn from Material Symbols instead
5
5
  // of the icon pack (data-icon-fallback) is only listed, with where it is: it does not fail.
6
6
  // The screens are found as the editor's own links: from the start screen (the drawer's links are in the page also while it is
7
- // closed) and "Dele", #udseende/dele, "Temaer", #udseende/temaer, "Søgemaskineoptimering", #seo, "Appstore", #indstillinger/appstore, and "Apps", #apps, which the menu no longer links to), and
7
+ // closed) and "Dele", #appearance/parts, "Temaer", #appearance/themes, "Søgemaskineoptimering", #seo, "Appstore", #settings/appstore, and "Apps", #apps, which the menu no longer links to), and
8
8
  // from each screen it opens, two steps down. GitHub is answered from the files in this repository, so it needs no login
9
9
  // and changes nothing. It runs on the built site: npm run build && npm run check:screens (or on another
10
10
  // folder with a built site, its path after the command: packages/editor/scripts/check-themes.mjs).
@@ -71,7 +71,7 @@ for (const [width, height] of [
71
71
  // From the start screen (the drawer's links), and from "Dele", "Temaer", "Søgemaskineoptimering" and "Appstore"
72
72
  // (the apps' pages and their developers'), whose screens are a step further down than the others. "Apps" (the list of
73
73
  // the apps installed) has no row in the menu, so it is a start of its own.
74
- const starts = ['', 'udseende/dele', 'udseende/temaer', 'seo', 'indstillinger/appstore', 'apps'];
74
+ const starts = ['', 'appearance/parts', 'appearance/themes', 'seo', 'settings/appstore', 'apps'];
75
75
  const seen = new Set(starts);
76
76
  let next = starts;
77
77
  for (let depth = 0; depth <= 2 && next.length; depth++) {
@@ -117,7 +117,7 @@ for (const [state, { shows, hides }] of Object.entries(keyStates)) {
117
117
  const page = await browser.newPage({ viewport: { width: 390, height: 844 }, locale: 'da-DK' });
118
118
  page.on('pageerror', (e) => problems.push(`Appstore (${state}): ${e.message}`));
119
119
  await openEditor(page, site, {}, state);
120
- await page.evaluate(() => (location.hash = 'indstillinger/appstore'));
120
+ await page.evaluate(() => (location.hash = 'settings/appstore'));
121
121
  await page.waitForTimeout(500);
122
122
  const text = await page.evaluate(() => document.body.innerText);
123
123
  for (const x of shows) if (!text.includes(x)) problems.push(`Appstore (${state}): "${x}" mangler`);
@@ -99,10 +99,10 @@ const STANDARD = {
99
99
  menu: 'src/data/menu.json',
100
100
  /** Addresses of the site that are not pages, which a new page or post cannot have. */
101
101
  reserved: ['/editor/', '/admin/', '/oauth/', '/images/', '/filer/', '/ny/', '/404/'],
102
- /** The lists the standard blocks use, by what they hold, and their ids (their addresses, #liste/<id>): the
102
+ /** The lists the standard blocks use, by what they hold, and their ids (their addresses, #list/<id>): the
103
103
  * testimonials, the list the block "Udtalelser" brings (src/standard/testimonials/list.ts), the tags, which a
104
104
  * testimonial's "Opret et tag" makes a new one of, and the cookies, the list the block "Cookieliste" brings. */
105
- lists: { testimonials: 'udtalelser', tags: 'kategorier', cookies: 'cookies' },
105
+ lists: { testimonials: 'testimonials', tags: 'tags', cookies: 'cookies' },
106
106
  /** The folder of the site's cookie list, "Cookies" under "Lister" (the standard block "Cookieliste",
107
107
  * src/standard/cookies/list.ts): a file per cookie, those an app sets added when it is installed and taken out when it
108
108
  * is uninstalled (src/cookieList.ts). */
package/site/schema.ts CHANGED
@@ -131,7 +131,7 @@ export function webPage({
131
131
  isPartOf: { '@id': ids.website },
132
132
  about: { '@id': ids.business },
133
133
  ...(image ? { primaryImageOfPage: abs(image) } : {}),
134
- ...(trail.length ? { breadcrumb: { '@id': `${url}#broedkrummer` } } : {}),
134
+ ...(trail.length ? { breadcrumb: { '@id': `${url}#breadcrumbs` } } : {}),
135
135
  ...(questions.length
136
136
  ? {
137
137
  mainEntity: questions.map((q) => ({
@@ -146,7 +146,7 @@ export function webPage({
146
146
  ? [
147
147
  {
148
148
  '@type': 'BreadcrumbList',
149
- '@id': `${url}#broedkrummer`,
149
+ '@id': `${url}#breadcrumbs`,
150
150
  itemListElement: trail.map((c, i) => ({ '@type': 'ListItem', position: i + 1, name: c.name, item: abs(c.href) })),
151
151
  },
152
152
  ]
@@ -175,7 +175,7 @@ export function blogPosting({
175
175
  const url = abs(path);
176
176
  return {
177
177
  '@type': 'BlogPosting',
178
- '@id': `${url}#indlaeg`,
178
+ '@id': `${url}#post`,
179
179
  headline: title,
180
180
  description,
181
181
  datePublished: date.toISOString(),
package/src/Advanced.tsx CHANGED
@@ -1,4 +1,4 @@
1
- // "Avanceret" (#avanceret), under "Indstillinger": R2 caching, the copy of the site's files in Cloudflare R2 the Worker
1
+ // "Avanceret" (#advanced), under "Indstillinger": R2 caching, the copy of the site's files in Cloudflare R2 the Worker
2
2
  // answers the editor from (packages/editor/worker/files.js), turned on and off with "Aktiveret" in the Worker's settings (r2Caching.ts),
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
@@ -32,7 +32,7 @@ import { buildSettingsOf, withBuildSetting, type BuildSetting } from './buildSet
32
32
  import { BUILD_SETTINGS_PATH } from '../lib/build-settings.mjs';
33
33
  import { siteT, t } from './i18n';
34
34
 
35
- export const ADVANCED_ROUTE = 'avanceret';
35
+ export const ADVANCED_ROUTE = 'advanced';
36
36
 
37
37
  // "Placering" when Cloudflare places the bucket (no jurisdiction).
38
38
  const AUTO = 'auto';
package/src/App.tsx CHANGED
@@ -1,50 +1,50 @@
1
1
  // The editor at /editor/ (docs/editor.md): sign in with GitHub, pick a part of the site, edit and
2
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
- // liste/<id> the list of a part, e.g. liste/priser, and liste/<id>/<app> a tab an installed app adds to it
5
- // (AppDefinition.tabs): liste/cookies/cookiescanner
6
- // lister the short lists (the testimonials, the apps' lists)
7
- // indstillinger/appstore the app store, its apps, and indstillinger/appstore/temaer its themes (AppList.tsx StorePage); the
4
+ // list/<id> the list of a part, e.g. list/priser, and list/<id>/<app> a tab an installed app adds to it
5
+ // (AppDefinition.tabs): list/cookies/cookiescanner
6
+ // lists the short lists (the testimonials, the apps' lists)
7
+ // settings/appstore the app store, its apps, and settings/appstore/themes its themes (AppList.tsx StorePage); the
8
8
  // other settings are screens of their own, in the drawer's last section (MainMenu.tsx)
9
- // ny/<id> a new entry, ny/<id>/<kind> one of a kind (a pop-up: standard, or an app's)
10
- // virksomheden the business, and virksomheden/<part> its parts: kontakt, betaling; and
11
- // virksomheden/betaling/<part> its own parts: mobilepay, bank
9
+ // new/<id> a new entry, new/<id>/<kind> one of a kind (a pop-up: standard, or an app's)
10
+ // company the business, and company/<part> its parts: contact, payment; and
11
+ // company/payment/<part> its own parts: mobilepay, bank
12
12
  // <screen> a screen with parts of the site's own (partScreens), and <screen>/<part> its parts:
13
- // privatliv/cookiebanner, privatliv/privatlivssider, or one of the editor's own screens (Part.opens):
14
- // privatliv/samtykker the visitors' answers in the cookie banner (Consents.tsx); and the settings of the apps installed with
15
- // its category: maaling/google-analytics
16
- // filer the uploaded pictures, PDFs, videos and zip files (Media.tsx)
17
- // seo "Søgemaskineoptimering" (Library.tsx Seo): seo/videresendelser the redirects' two lists
18
- // (Redirects.tsx): seo/videresendelser/alle the redirects, and seo/videresendelser/ikke-fundet
19
- // the addresses visitors came to that do not exist; seo/vurdering the SEO rating of every page and
13
+ // privacy/cookie-banner, privacy/privacy-pages, or one of the editor's own screens (Part.opens):
14
+ // privacy/consents the visitors' answers in the cookie banner (Consents.tsx); and the settings of the apps installed with
15
+ // its category: analytics/google-analytics
16
+ // files the uploaded pictures, PDFs, videos and zip files (Media.tsx)
17
+ // seo "Søgemaskineoptimering" (Library.tsx Seo): seo/redirects the redirects' two lists
18
+ // (Redirects.tsx): seo/redirects/all the redirects, and seo/redirects/not-found
19
+ // the addresses visitors came to that do not exist; seo/rating the SEO rating of every page and
20
20
  // post (SeoScores.tsx); and seo/<part> the site's own (seoParts)
21
- // udseende how the site looks (Library.tsx Theme): udseende/temaer the themes installed (Themes) and udseende/temaer/<theme>
22
- // a theme's page, with the button that swaps to it (ThemePage), udseende/ikonpakker the icon packs (IconPacks),
23
- // udseende/bevaegelse how it moves (MotionSchemes), udseende/sidekolonne the block beside the pages' text (SideColumn),
24
- // udseende/header the header's menus and settings with its preview (HeaderSettings.tsx; once at header),
25
- // udseende/footer the footer's texts and links with its preview (FooterSettings.tsx; once at src/data/footer.json),
26
- // udseende/farver the colours (Colors.tsx;
27
- // udseende/farver/reservefarver the site's own while an app changes the pages'), udseende/logo the logo
28
- // and icon (BrandForm.tsx), udseende/dele the parts (Library.tsx PartsHome): udseende/dele/editor the editor's, every
29
- // one in every state (Parts.tsx), udseende/dele/ui the theme's UI components, udseende/dele/blokke the
30
- // blocks, the standard ones and the apps', and udseende/dele/liste/<list> each kind of a list of kinds
31
- // ("Promovering"), its own and the apps' (BlockParts.tsx), and udseende/dele/vaerdier the
21
+ // appearance how the site looks (Library.tsx Theme): appearance/themes the themes installed (Themes) and appearance/themes/<theme>
22
+ // a theme's page, with the button that swaps to it (ThemePage), appearance/icon-packs the icon packs (IconPacks),
23
+ // appearance/motion how it moves (MotionSchemes), appearance/side-column the block beside the pages' text (SideColumn),
24
+ // appearance/header the header's menus and settings with its preview (HeaderSettings.tsx; once at header),
25
+ // appearance/footer the footer's texts and links with its preview (FooterSettings.tsx; once at src/data/footer.json),
26
+ // appearance/colors the colours (Colors.tsx;
27
+ // appearance/colors/fallback-colors the site's own while an app changes the pages'), appearance/logo the logo
28
+ // and icon (BrandForm.tsx), appearance/parts the parts (Library.tsx PartsHome): appearance/parts/editor the editor's, every
29
+ // one in every state (Parts.tsx), appearance/parts/ui the theme's UI components, appearance/parts/blocks the
30
+ // blocks, the standard ones and the apps', and appearance/parts/list/<list> each kind of a list of kinds
31
+ // ("Promovering"), its own and the apps' (BlockParts.tsx), and appearance/parts/values the
32
32
  // theme's design tokens, "Værdier" (Tokens.tsx)
33
- // blokvalg the blocks that can be put in, "Blokindstillinger" (BlockSettings.tsx)
34
- // avanceret "Avanceret", under "Indstillinger": the copy of the site's files in R2 (Advanced.tsx)
33
+ // block-settings the blocks that can be put in, "Blokindstillinger" (BlockSettings.tsx)
34
+ // advanced "Avanceret", under "Indstillinger": the copy of the site's files in R2 (Advanced.tsx)
35
35
  // morpheme "Morpheme", under "Indstillinger": the CMS's version, a newer one and its notes, an update's progress (CmsPage.tsx)
36
- // licenser "Licenser", under "Indstillinger": the licences of what the site and the editor are made with, and
37
- // licenser/<id> one with its text (Licenses.tsx)
36
+ // licenses "Licenser", under "Indstillinger": the licences of what the site and the editor are made with, and
37
+ // licenses/<id> one with its text (Licenses.tsx)
38
38
  // apps the apps that come with the site, apps/<app> one with its button to install it and, while it
39
39
  // is installed, the tabs of its page (AppList.tsx AppPage): Settings at apps/<app>, with its settings
40
- // form (also on the screen with its category, maaling/<app>, AppList.tsx settingsRoute), and
41
- // Information at apps/<app>/om (before an app's own screens, AppDefinition.screens)
42
- // apps/<app>/indstillinger/<field> the page of a field of its settings with its choices on a page of their own
43
- // (Field.page), over its Settings tab, which keeps what is in it: apps/content-colors/indstillinger/variant
40
+ // form (also on the screen with its category, analytics/<app>, AppList.tsx settingsRoute), and
41
+ // Information at apps/<app>/about (before an app's own screens, AppDefinition.screens)
42
+ // apps/<app>/settings/<field> the page of a field of its settings with its choices on a page of their own
43
+ // (Field.page), over its Settings tab, which keeps what is in it: apps/content-colors/settings/variant
44
44
  // <form>/<field> the page of a field of a part's form with its choices on a page of their own (Field.page), over the
45
45
  // form, which keeps what is in it (formAt)
46
- // blokke the blocks, and blokke/<block> the texts all its copies have (blockTexts.ts)
47
- // slettet/<id> what was deleted from a list, or slettet/filer the files (Deleted.tsx)
46
+ // blocks the blocks, and blocks/<block> the texts all its copies have (blockTexts.ts)
47
+ // deleted/<id> what was deleted from a list, or deleted/files the files (Deleted.tsx)
48
48
  // <file path> a file being edited, e.g. src/content/blog/mit-foerste-indlaeg.md
49
49
  import { useEffect, useMemo, useRef, useState } from 'react';
50
50
  import {
@@ -276,9 +276,9 @@ function routeOf(hash: string) {
276
276
  const route = movedRoute(screenOf(hash), STORE_MOVED);
277
277
  // An app's settings: on the screen with its category, when it has one ("Analytics", AppList.tsx settingsRoute), and so
278
278
  // the pages of their fields (Field.page) after them. Otherwise they are on the Settings tab of its page: apps/<id>
279
- // (a field's page, apps/<id>/indstillinger/<field>, stays, drawn by the same page).
280
- const app = apps.find((a) => route === `apps/${a.id}/indstillinger` || route.startsWith(`apps/${a.id}/indstillinger/`));
281
- const rest = app ? route.slice(`apps/${app.id}/indstillinger`.length) : '';
279
+ // (a field's page, apps/<id>/settings/<field>, stays, drawn by the same page).
280
+ const app = apps.find((a) => route === `apps/${a.id}/settings` || route.startsWith(`apps/${a.id}/settings/`));
281
+ const rest = app ? route.slice(`apps/${app.id}/settings`.length) : '';
282
282
  const kindScreen = app && settingsHome(app) && settingsRoute(app);
283
283
  const moved = !app ? route : kindScreen ? `${kindScreen}${rest}` : rest ? route : `apps/${app.id}`;
284
284
  if (moved !== screenOf(hash)) history.replaceState(history.state, '', `#${moved}`);
@@ -557,7 +557,7 @@ function Editor() {
557
557
  replace([file, ...(more?.files ?? [])], more?.removed);
558
558
  // A new entry, or a post that got a new address: from now on it is edited as the file it
559
559
  // became. A form that saves another file than the one at its address (Virksomheden) stays where it is.
560
- if (route.startsWith('ny/') || more?.removed.includes(route)) {
560
+ if (route.startsWith('new/') || more?.removed.includes(route)) {
561
561
  history.replaceState(history.state, '', `#${file.path}`);
562
562
  setRoute(file.path);
563
563
  }
@@ -578,7 +578,7 @@ function Editor() {
578
578
  replace([], [file.path]);
579
579
  // Back to the list, as with the back arrow: the deleted entry is not left behind it in the history.
580
580
  setUnsaved(false);
581
- up(`#liste/${collection.id}`);
581
+ up(`#list/${collection.id}`);
582
582
  notify({ text: savedText(change), kind: 'saved' });
583
583
  } catch (e) {
584
584
  notify({ text: t.app.notDeleted((e as Error).message), error: true, kind: 'failed' });
@@ -689,15 +689,15 @@ function Editor() {
689
689
  /** The part whose form is at an address (CompanyForm.tsx): one of "Virksomheden"'s (or of its parts', with the settings of the apps of their kinds), "Søgemaskineoptimering"'s, "Udseende"'s, or one on a screen of parts with
690
690
  * the settings of the apps of its kinds ("Analytics"). */
691
691
  function partAt(route: string, files: RepoFile[]): Part | null {
692
- // virksomheden/<part>, or virksomheden/<part>/<its part> (Part.parts).
692
+ // company/<part>, or company/<part>/<its part> (Part.parts).
693
693
  const [, companyId, ownId, ...more] = route.split('/');
694
- const company = route.startsWith('virksomheden/') && !more.length && companyParts.find((p) => p.id === companyId);
694
+ const company = route.startsWith('company/') && !more.length && companyParts.find((p) => p.id === companyId);
695
695
  return (
696
696
  (company &&
697
697
  (ownId ? [...(company.parts ?? []), ...appPartsIn(company, files)].find((p) => p.id === ownId) : company.file && company)) ||
698
- (route.startsWith('blokke/') && blockParts.find((p) => p.id === route.slice(7))) ||
698
+ (route.startsWith('blocks/') && blockParts.find((p) => p.id === route.slice(7))) ||
699
699
  (route.startsWith('seo/') && seoParts.find((p) => p.id === route.slice(4))) ||
700
- (route.startsWith('udseende/') && appearance.find((p) => p.id === route.slice(9))) ||
700
+ (route.startsWith('appearance/') && appearance.find((p) => p.id === route.slice('appearance/'.length))) ||
701
701
  partScreens.flatMap((s) => [...s.parts, ...appPartsIn(s, files)].filter((p) => route === `${s.id}/${p.id}`))[0] ||
702
702
  null
703
703
  );
@@ -718,13 +718,13 @@ type PageProps = { route: string; files: RepoFile[]; env: Env; user: Viewer | nu
718
718
 
719
719
  function Page({ route, files, env, user, builtAt, onLogout }: PageProps) {
720
720
  if (!route) return <Start builtAt={builtAt} />;
721
- if (route === 'konto') return <Account user={user} files={files} onLogout={onLogout} />;
722
- if (route === 'notifikationer') return <Notifications />;
723
- if (route === 'lister') return <Lists files={files} />;
724
- if (route === 'virksomheden') return <Company files={files} />;
725
- if (route === 'blokke') return <Blocks files={files} />;
726
- if (route === 'filer' || route.startsWith('filer/'))
727
- return <Media key={route} files={files} env={env} open={route === 'filer' ? undefined : '/' + route.slice('filer/'.length)} />;
721
+ if (route === 'account') return <Account user={user} files={files} onLogout={onLogout} />;
722
+ if (route === 'notifications') return <Notifications />;
723
+ if (route === 'lists') return <Lists files={files} />;
724
+ if (route === 'company') return <Company files={files} />;
725
+ if (route === 'blocks') return <Blocks files={files} />;
726
+ if (route === 'files' || route.startsWith('files/'))
727
+ return <Media key={route} files={files} env={env} open={route === 'files' ? undefined : '/' + route.slice('files/'.length)} />;
728
728
  if (route === 'seo') return <Seo />;
729
729
  if (route === REDIRECTS_ROUTE) return <Redirects env={env} />;
730
730
  if (route === SEO_SCORES_ROUTE) return <SeoScores files={files} env={env} />;
@@ -735,22 +735,23 @@ function Page({ route, files, env, user, builtAt, onLogout }: PageProps) {
735
735
  // A part of a screen of parts that is one of the editor's own screens (Part.opens), going back to that screen.
736
736
  const opening = partScreens.flatMap((s) => s.parts.filter((p) => p.opens && route === `${s.id}/${p.id}`).map((p) => ({ s, p })))[0];
737
737
  if (opening?.p.opens === 'consents') return <Consents env={env} back={{ href: `#${opening.s.id}`, label: opening.s.label }} />;
738
- if (route === 'udseende') return <Theme files={files} />;
739
- if (route === 'udseende/temaer') return <Themes env={env} files={files} />;
740
- if (route === 'udseende/ikonpakker') return <IconPacks env={env} files={files} />;
741
- if (route === 'udseende/bevaegelse') return <MotionSchemes env={env} files={files} />;
742
- if (route === 'udseende/sidekolonne') return <SideColumn env={env} files={files} />;
743
- if (route === 'udseende/header') return <HeaderSettings env={env} />;
744
- if (route === 'udseende/footer') return <FooterSettings env={env} />;
738
+ if (route === 'appearance') return <Theme files={files} />;
739
+ if (route === 'appearance/themes') return <Themes env={env} files={files} />;
740
+ if (route === 'appearance/icon-packs') return <IconPacks env={env} files={files} />;
741
+ if (route === 'appearance/motion') return <MotionSchemes env={env} files={files} />;
742
+ if (route === 'appearance/side-column') return <SideColumn env={env} files={files} />;
743
+ if (route === 'appearance/header') return <HeaderSettings env={env} />;
744
+ if (route === 'appearance/footer') return <FooterSettings env={env} />;
745
745
  // A theme installed has its page; one from the store that is not (or not yet built), its page in the store.
746
- const themeShown = route.startsWith('udseende/temaer/') && themes.find((x) => x.id === route.slice(16));
746
+ const themeShown = route.startsWith('appearance/themes/') && themes.find((x) => x.id === route.slice('appearance/themes/'.length));
747
747
  if (themeShown) return <ThemePage key={themeShown.id} of={themeShown} env={env} files={files} />;
748
- if (/^udseende\/temaer\/[^/]+$/.test(route)) return <StoreThemePage key={route} id={route.slice(16)} env={env} />;
748
+ if (/^appearance\/themes\/[^/]+$/.test(route))
749
+ return <StoreThemePage key={route} id={route.slice('appearance/themes/'.length)} env={env} />;
749
750
  // The colours, or the site's own on a page of the same form while an app changes the pages' (FALLBACK_ROUTE).
750
- if (route === 'udseende/farver' || route === FALLBACK_ROUTE)
751
+ if (route === 'appearance/colors' || route === FALLBACK_ROUTE)
751
752
  return <Colors env={env} files={files} fallback={route === FALLBACK_ROUTE} />;
752
- if (route === 'udseende/logo') return <BrandForm env={env} />;
753
- if (route === 'blokvalg') return <BlockSettings env={env} files={files} />;
753
+ if (route === 'appearance/logo') return <BrandForm env={env} />;
754
+ if (route === 'block-settings') return <BlockSettings env={env} files={files} />;
754
755
  if (route === ADVANCED_ROUTE) return <Advanced env={env} />;
755
756
  if (route === CMS_ROUTE) return <CmsPage />;
756
757
  if (route === LICENSES_ROUTE) return <Licenses />;
@@ -761,7 +762,7 @@ function Page({ route, files, env, user, builtAt, onLogout }: PageProps) {
761
762
  if (route.startsWith(`${DEVELOPER_ROUTE}/`))
762
763
  return <DeveloperPage key={route} slug={route.slice(DEVELOPER_ROUTE.length + 1)} env={env} files={files} />;
763
764
  // An app in the site (its own, or one from the store while it is installed) has its page, with the tabs Settings
764
- // (apps/<id>, and a settings field's page, apps/<id>/indstillinger/<field>) and Information (apps/<id>/om, matched
765
+ // (apps/<id>, and a settings field's page, apps/<id>/settings/<field>) and Information (apps/<id>/about, matched
765
766
  // before an app's own screens); one from the store that is not (or no longer) installed, its page in the store.
766
767
  const shownApp = route.startsWith('apps/') ? apps.find((a) => appRouteOf(route, a.id)) : undefined;
767
768
  const shown = shownApp && appRouteOf(route, shownApp.id);
@@ -776,29 +777,29 @@ function Page({ route, files, env, user, builtAt, onLogout }: PageProps) {
776
777
  route.startsWith('apps/') && !deeper.length && apps.find((a) => a.id === screenApp && screenId && a.screens?.[screenId]);
777
778
  if (ownScreen && installedApps(files).includes(ownScreen.id))
778
779
  return <AppScreenPage key={route} app={ownScreen} id={screenId} env={env} />;
779
- if (route === 'udseende/dele') return <PartsHome />;
780
- if (route === 'udseende/dele/editor') return <Parts env={env} />;
781
- if (route === 'udseende/dele/ui') return <ComponentParts />;
782
- if (route === 'udseende/dele/blokke') return <BlocksParts />;
783
- if (route.startsWith('udseende/dele/liste/')) return <KindParts key={route} id={route.slice(20)} />;
784
- if (route === 'udseende/dele/vaerdier') return <Tokens />;
785
- if (route.startsWith('slettet/')) return <Deleted id={route.slice(8)} files={files} env={env} />;
786
- // virksomheden/<part>, or virksomheden/<part>/<its part> (Part.parts).
780
+ if (route === 'appearance/parts') return <PartsHome />;
781
+ if (route === 'appearance/parts/editor') return <Parts env={env} />;
782
+ if (route === 'appearance/parts/ui') return <ComponentParts />;
783
+ if (route === 'appearance/parts/blocks') return <BlocksParts />;
784
+ if (route.startsWith('appearance/parts/list/')) return <KindParts key={route} id={route.slice('appearance/parts/list/'.length)} />;
785
+ if (route === 'appearance/parts/values') return <Tokens />;
786
+ if (route.startsWith('deleted/')) return <Deleted id={route.slice('deleted/'.length)} files={files} env={env} />;
787
+ // company/<part>, or company/<part>/<its part> (Part.parts).
787
788
  const [, companyId, ownId, ...more] = route.split('/');
788
- const company = route.startsWith('virksomheden/') && !more.length && companyParts.find((p) => p.id === companyId);
789
+ const company = route.startsWith('company/') && !more.length && companyParts.find((p) => p.id === companyId);
789
790
  if (company && company.parts && !ownId) return <Company part={company} files={files} />;
790
791
  // A part's form, or the page of one of its fields (Field.page) with the form under it: the same form, so it keeps what
791
792
  // is in it while the editor goes between them (useFormPages, inForm).
792
793
  const form = formAt(route, files);
793
794
  if (form) return <CompanyForm part={form.part} at={form.at} page={form.page} env={env} files={files} />;
794
- // A list, or a tab an installed app adds to it (liste/<id>/<app>).
795
+ // A list, or a tab an installed app adds to it (list/<id>/<app>).
795
796
  const [, listId, listTab, ...below] = route.split('/');
796
- const listed = route.startsWith('liste/') && !below.length && collections.find((c) => c.id === listId);
797
+ const listed = route.startsWith('list/') && !below.length && collections.find((c) => c.id === listId);
797
798
  if (listed) return <List key={listed.id} collection={listed} tab={listTab} files={files} env={env} />;
798
799
 
799
- // ny/<list>, or ny/<list>/<kind> for one of its kinds (a pop-up: standard, mailchimp).
800
+ // new/<list>, or new/<list>/<kind> for one of its kinds (a pop-up: standard, mailchimp).
800
801
  const [, newIn, newKind] = route.split('/');
801
- const creating = route.startsWith('ny/') && collections.find((c) => c.id === newIn && c.create);
802
+ const creating = route.startsWith('new/') && collections.find((c) => c.id === newIn && c.create);
802
803
  if (creating) {
803
804
  if (creating.kind === 'markdown') return <EntryForm collection={creating} file={newMarkdown(creating, notOffered(files))} env={env} />;
804
805
  if (creating.kind === 'folder')
package/src/AppAdds.tsx CHANGED
@@ -29,7 +29,7 @@ function addsOf(app: SiteApp, files: RepoFile[]) {
29
29
  ...kindsOf(app).map((k) => ({
30
30
  ...k,
31
31
  block: undefined,
32
- create: k.create ? { href: `#ny/${k.key}/${app.id}`, label: k.create } : undefined,
32
+ create: k.create ? { href: `#new/${k.key}/${app.id}`, label: k.create } : undefined,
33
33
  uses: entriesOf(app, k.key, files),
34
34
  })),
35
35
  ];
package/src/AppList.tsx CHANGED
@@ -2,7 +2,7 @@
2
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
- // (#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
5
+ // (#settings/appstore): all apps, those in the app store (store.ts) and the site's own, installed or not, with search (by name, what they
6
6
  // do, who made them and the words they are found by), the same order and a funnel by category. An app's page
7
7
  // (#apps/<app>) is the same for every app and without examples: its name, who made it, whether it is installed, its
8
8
  // version (from the store) and what it does, "Installer" (or "Opdater til …"); installed, it has two tabs: "Indstillinger"
@@ -11,10 +11,10 @@
11
11
  // adds (AppAdds.tsx: its blocks, the lists it brings: "Priser", each opening its list, and its kinds of the site's lists:
12
12
  // a Mailchimp popup; each with its switch at the end of its row and opening a sheet with where it is used and the same
13
13
  // switch, as under "Blokindstillinger", and a kind's button to a new one, while it is on), its notifications with their
14
- // switches, and "Afinstaller", AppSettingsTab.tsx) and "Information" (#apps/<app>/om, AppInfoTab.tsx: its pictures, what
14
+ // switches, and "Afinstaller", AppSettingsTab.tsx) and "Information" (#apps/<app>/about, AppInfoTab.tsx: its pictures, what
15
15
  // it does more fully ("Om appen"), what it needs of the site ("Rettigheder", needsOf in store.ts: its code on every page,
16
16
  // the addresses it loads from and sends forms to, its part of the Worker, a step in the build), the cookies it sets (and
17
- // whether the cookie banner asks first), who made it (a row to their page, #apps/udvikler/<name>, with all their apps),
17
+ // whether the cookie banner asks first), who made it (a row to their page, #apps/developer/<name>, with all their apps),
18
18
  // and where it is used); one not installed has no tabs; installing, updating and uninstalling are each saved at once in its own commit,
19
19
  // as a block turned on or off under "Blokindstillinger". The button pressed shows the loading indicator while it runs (busyAction.mjs). One of the site's own is installed or uninstalled in the site's
20
20
  // file of apps; one from the store is copied into the site, or deleted from it, with it (appChanges in store.ts), and
@@ -99,10 +99,10 @@ export function settingsHome(app: SiteApp): { route: string; label: string } | u
99
99
  const screen = partScreens.find((s) => s.apps?.includes(kind));
100
100
  if (screen) return { route: screen.id, label: screen.label };
101
101
  const company = companyParts.find((p) => p.parts && p.apps?.includes(kind));
102
- return company && { route: `virksomheden/${company.id}`, label: company.label };
102
+ return company && { route: `company/${company.id}`, label: company.label };
103
103
  }
104
104
 
105
- /** Where an app's settings are: on the screen with its kind (#maaling/google-analytics, #virksomheden/betaling/mobilepay),
105
+ /** Where an app's settings are: on the screen with its kind (#analytics/google-analytics, #company/payment/mobilepay),
106
106
  * else on the Settings tab of its page (#apps/<id>); null without settings. */
107
107
  export function settingsRoute(app: SiteApp): string | null {
108
108
  if (!app.settings) return null;
@@ -161,7 +161,7 @@ export const listsOf = (app: SiteApp, files: RepoFile[]) =>
161
161
  key: l.id,
162
162
  icon: l.icon,
163
163
  label: l.label,
164
- href: `#liste/${l.id}`,
164
+ href: `#list/${l.id}`,
165
165
  count: files.filter((f) => f.path.startsWith(`${l.folder}/`) && f.path.endsWith('.json')).length,
166
166
  }));
167
167
 
@@ -191,7 +191,7 @@ export function kindsOf(app: SiteApp) {
191
191
  icon: kind.icon,
192
192
  label: kind.label,
193
193
  hint: kind.hint,
194
- href: `#liste/${c.id}`,
194
+ href: `#list/${c.id}`,
195
195
  one: c.singular,
196
196
  more: c.label,
197
197
  create: c.create,
@@ -289,7 +289,7 @@ export function Apps({ env, files }: { env: Env; files: RepoFile[] }) {
289
289
  const installed = installedApps(files);
290
290
  const catalog = useCatalog(env);
291
291
  const store = Array.isArray(catalog) ? catalog : [];
292
- const [query, setQuery] = useKept('apps-soeg', '');
292
+ const [query, setQuery] = useKept('apps-search', '');
293
293
  const [sort, pickSort] = useSort();
294
294
 
295
295
  // The apps installed: the site's own and those from the store; and one from the store in the file of apps that the
@@ -401,7 +401,7 @@ function AppRow({ app, installed }: { app: ReturnType<typeof allApps>[number]; i
401
401
  );
402
402
  }
403
403
 
404
- /** A developer's page (#apps/udvikler/<name>): their name, and all their apps and themes (in the store and the site's
404
+ /** A developer's page (#apps/developer/<name>): their name, and all their apps and themes (in the store and the site's
405
405
  * own), each opening its page. */
406
406
  export function DeveloperPage({ slug, env, files }: { slug: string; env: Env; files: RepoFile[] }) {
407
407
  const installed = installedApps(files);
@@ -462,9 +462,9 @@ export function DeveloperRow({ name }: { name: string }) {
462
462
  );
463
463
  }
464
464
 
465
- /** "Appstore" (#indstillinger/appstore, under "Indstillinger"): in a tab of its own each in the top app bar, all apps,
465
+ /** "Appstore" (#settings/appstore, under "Indstillinger"): in a tab of its own each in the top app bar, all apps,
466
466
  * those in the app store and the site's own, each with what it does and whether it is installed, and all themes
467
- * (#indstillinger/appstore/temaer, ThemeStore.tsx), those in the store
467
+ * (#settings/appstore/themes, ThemeStore.tsx), those in the store
468
468
  * and those installed, each with its version and who made it; each opening its page. With search (by name, what it
469
469
  * does, who made it and the words it is found by, in both languages), the order and the filters by category (apps)
470
470
  * and by whether it is installed, each picked one as a chip. */
@@ -483,10 +483,10 @@ function StoreScreen({ env, files, tab, again }: { env: Env; files: RepoFile[];
483
483
  const catalog = useCatalog(env);
484
484
  const themeCatalog = useThemeCatalog(env);
485
485
  const connected = useStoreConnected(env);
486
- const [query, setQuery] = useKept('butik-soeg', '');
486
+ const [query, setQuery] = useKept('store-search', '');
487
487
  const [sort, pickSort] = useSort();
488
- const [category, setCategory] = useKept<AppCategory | ''>('butik-kategori', '');
489
- const [state, setState] = useKept<'' | 'installed' | 'notInstalled'>('butik-status', '');
488
+ const [category, setCategory] = useKept<AppCategory | ''>('store-category', '');
489
+ const [state, setState] = useKept<'' | 'installed' | 'notInstalled'>('store-status', '');
490
490
  const apps = tab === 'apps';
491
491
  const read = apps ? catalog : themeCatalog;
492
492
 
@@ -948,7 +948,7 @@ type AppProps = { app: SiteApp; env: Env; files: RepoFile[] };
948
948
 
949
949
  type AppTab = 'settings' | 'information';
950
950
 
951
- /** The tabs of an installed app's page, in the top bar: Settings (#apps/<id>) and Information (#apps/<id>/om). A form
951
+ /** The tabs of an installed app's page, in the top bar: Settings (#apps/<id>) and Information (#apps/<id>/about). A form
952
952
  * with unsaved changes asks first, as for every address (App.tsx). */
953
953
  const tabsOf = (app: SiteApp, tab: AppTab): TabChoice<AppTab> => ({
954
954
  label: t.apps.tabs,
@@ -116,7 +116,7 @@ const plain = (s: string) =>
116
116
 
117
117
  type InsertSort = 'az' | 'za' | 'group';
118
118
  const INSERT_SORTS: InsertSort[] = ['az', 'za', 'group'];
119
- const INSERT_SORT_KEY = 'editor-sort-indsaet';
119
+ const INSERT_SORT_KEY = 'editor-sort-insert';
120
120
 
121
121
  // A block's group: the app's name for an app's block, otherwise the standard blocks (also the picture and HTML).
122
122
  const groupOf = (b: SiteBlock) => (b.app ? (apps.find((a) => a.id === b.app)?.label ?? b.app) : t.blockSettings.standard);
@@ -1,5 +1,5 @@
1
- // "Dele" › "UI-komponenter" (#udseende/dele/ui), "Dele" › "Blokke" (#udseende/dele/blokke) and "Dele" › a list of kinds
2
- // ("Promovering", #udseende/dele/liste/<id>): how the site looks, every part in every version, to see and agree on it.
1
+ // "Dele" › "UI-komponenter" (#appearance/parts/ui), "Dele" › "Blokke" (#appearance/parts/blocks) and "Dele" › a list of kinds
2
+ // ("Promovering", #appearance/parts/list/<id>): how the site looks, every part in every version, to see and agree on it.
3
3
  // "UI-komponenter" also has the active theme's coverage (summary, mark on each part, filter by style) and the site frame's parts.
4
4
  // "UI-komponenter" has the theme's UI components (its buttons and icon buttons, text fields and checkboxes …,
5
5
  // packages/editor/theme/ui/, through @site/editor) in every kind, size and state; "Blokke" every block, the standard
@@ -52,7 +52,7 @@ import { t } from './i18n';
52
52
  import { Svg } from './icon';
53
53
 
54
54
  const { screens } = t.parts;
55
- const back = { href: '#udseende/dele', label: t.common.parts.editorParts };
55
+ const back = { href: '#appearance/parts', label: t.common.parts.editorParts };
56
56
 
57
57
  // The fields filled in with the plain picture where they have none (a card's, a list's items').
58
58
  function pictured(fields: Field[], props: BlockProps): BlockProps {
@@ -520,7 +520,7 @@ function CardParts() {
520
520
  );
521
521
  }
522
522
 
523
- /** "Dele" › "UI-komponenter" (#udseende/dele/ui): the theme's UI components in every kind, size and state. */
523
+ /** "Dele" › "UI-komponenter" (#appearance/parts/ui): the theme's UI components in every kind, size and state. */
524
524
  export function ComponentParts() {
525
525
  const groups = [
526
526
  { id: 'ui', title: screens.ui },
@@ -587,7 +587,7 @@ export function ComponentParts() {
587
587
  );
588
588
  }
589
589
 
590
- /** "Dele" › "Blokke" (#udseende/dele/blokke): every block, the standard ones and then the apps', each a group of its
590
+ /** "Dele" › "Blokke" (#appearance/parts/blocks): every block, the standard ones and then the apps', each a group of its
591
591
  * own, in all its versions. */
592
592
  export function BlocksParts() {
593
593
  const data = useMemo(exampleData, []);
@@ -616,7 +616,7 @@ export function BlocksParts() {
616
616
  /** The lists of kinds ("Promovering"), each a screen of "Dele". */
617
617
  export const kindLists = () => collections.filter((c): c is FolderCollection => c.kind === 'folder' && !!c.kinds && !!c.fields?.length);
618
618
 
619
- /** "Dele" › a list of kinds ("Promovering", #udseende/dele/liste/<id>): each of its kinds as the site draws it, its own
619
+ /** "Dele" › a list of kinds ("Promovering", #appearance/parts/list/<id>): each of its kinds as the site draws it, its own
620
620
  * (FolderCollection.draws: the pop-up, the bars) and the apps' (AppKind: Mailchimp's signup, once for each of its
621
621
  * states), each a group of its own, as a new one of the kind with the made-up texts (kindExample). */
622
622
  export function KindParts({ id }: { id: string }) {
@@ -1,4 +1,4 @@
1
- // "Blokke" › "Blokindstillinger" (#blokvalg): the blocks that can be put in the text of a page, in
1
+ // "Blokke" › "Blokindstillinger" (#block-settings): the blocks that can be put in the text of a page, in
2
2
  // groups, the editor's standard blocks (packages/editor/src/standard/) and those of each app installed under "Apps",
3
3
  // under the app's name (e.g. "Mailchimp",
4
4
  // src/apps/, AppList.tsx; an app not installed has none here), each with how many pages it is on at its right
@@ -243,7 +243,7 @@ export function KindSwitch({ app, list, env, files }: { app: SiteApp; list: stri
243
243
  type Sort = 'group' | 'az' | 'za';
244
244
  const SORTS: Sort[] = ['group', 'az', 'za'];
245
245
  // The order picked last time, in this browser.
246
- const SORT_KEY = 'editor-sort-blokvalg';
246
+ const SORT_KEY = 'editor-sort-block-settings';
247
247
  type OnOff = '' | 'on' | 'off';
248
248
  type Use = '' | 'used' | 'unused';
249
249
 
package/src/BrandForm.tsx CHANGED
@@ -1,4 +1,4 @@
1
- // "Udseende" › "Logo og ikon" (#udseende/logo): a new logo (the mark by the site's name) and a new icon (the browser
1
+ // "Udseende" › "Logo og ikon" (#appearance/logo): a new logo (the mark by the site's name) and a new icon (the browser
2
2
  // tab, Google and the editor's app icon), made from a picture the owner picks (brand.ts), in the site's colours
3
3
  // ("Farver"). Both are saved together in one commit with "Gem ændringer".
4
4
  import { useEffect, useState } from 'react';
@@ -120,7 +120,7 @@ export default function BrandForm({ env }: { env: Env }) {
120
120
  return (
121
121
  <FormFrame
122
122
  title={t.common.parts.brand}
123
- back={{ href: '#udseende', label: t.common.parts.appearance }}
123
+ back={{ href: '#appearance', label: t.common.parts.appearance }}
124
124
  dirty={dirty}
125
125
  saving={saving || drawing}
126
126
  onSave={save}
package/src/Colors.tsx CHANGED
@@ -1,4 +1,4 @@
1
- // "Udseende" › "Farver" (#udseende/farver): the site's colours, for light and dark mode, in the site's file of
1
+ // "Udseende" › "Farver" (#appearance/colors): the site's colours, for light and dark mode, in the site's file of
2
2
  // colours (config.colors, packages/editor/lib/colors.ts). The first tab, "Generator", where the screen starts, or one
3
3
  // mode at a time, picked with the tabs after it, and the editor is shown in the mode while the screen is open
4
4
  // (theme.ts holdTheme). Each mode has a little page drawn in its colours, the pairs that are too hard to
@@ -15,7 +15,7 @@
15
15
  // farver", src/apps/content-colors/: pageColors in packages/editor/site/pageColors.ts). While an app installed changes the pages'
16
16
  // colours (hooks/page-colors.ts), each mode shows the pages' colours instead (PageColors), as the site was last built
17
17
  // (/editor/page-colors.json, packages/editor/scripts/page-colors.mjs): first a notice that the apps give them, with a button to the
18
- // site's own, which are only the fallback ("Reservefarver", #udseende/farver/reservefarver, a page of the same form, so
18
+ // site's own, which are only the fallback ("Reservefarver", #appearance/colors/fallback-colors, a page of the same form, so
19
19
  // what is unsaved stays), then the apps, each to its settings, the pages with colours from a picture (the picture, the
20
20
  // page and its colours in the mode open), and those with the site's own. Saved with "Gem ændringer" in one commit; the
21
21
  // editor shows the new colours at once (theme.ts applyColors), the site when it is built again.
@@ -112,7 +112,7 @@ const CHANGE = t.app.changes.edited(t.app.changes.colors);
112
112
  type Tab = Mode | 'generator';
113
113
 
114
114
  /** The page with the site's own colours, while an app changes the pages' colours: the fallback. */
115
- export const FALLBACK_ROUTE = 'udseende/farver/reservefarver';
115
+ export const FALLBACK_ROUTE = 'appearance/colors/fallback-colors';
116
116
 
117
117
  /** The apps installed that change the pages' colours (hooks/page-colors.ts). */
118
118
  const colorApps = (files: RepoFile[]) => {
@@ -139,7 +139,7 @@ export default function Colors({ env, files, fallback }: { env: Env; files: Repo
139
139
  const changing = useMemo(() => colorApps(files), [files]);
140
140
  useEffect(() => () => holdTheme(null), []);
141
141
  // While an app changes the pages' colours, the modes show the pages', and the site's own are on a page of the form.
142
- useFormPages(changing.length ? ['udseende/farver', FALLBACK_ROUTE] : []);
142
+ useFormPages(changing.length ? ['appearance/colors', FALLBACK_ROUTE] : []);
143
143
  const ownShown = fallback || !changing.length;
144
144
  const appNames = new Intl.ListFormat(locale).format(changing.map((a) => a.label));
145
145
  const file = files.find((f) => f.path === config.colors);
@@ -207,8 +207,8 @@ export default function Colors({ env, files, fallback }: { env: Env; files: Repo
207
207
  title={changing.length && fallback ? t.colors.pages.fallback : t.common.parts.colors}
208
208
  back={
209
209
  changing.length && fallback
210
- ? { href: '#udseende/farver', label: t.common.parts.colors, guard: (go) => go() }
211
- : { href: '#udseende', label: t.common.parts.appearance }
210
+ ? { href: '#appearance/colors', label: t.common.parts.colors, guard: (go) => go() }
211
+ : { href: '#appearance', label: t.common.parts.appearance }
212
212
  }
213
213
  dirty={dirty}
214
214
  saving={saving}
@@ -113,7 +113,7 @@ export default function CompanyForm({ part, env, files, at: route, page }: Props
113
113
  ) : (
114
114
  <FormFrame
115
115
  title={part.label}
116
- back={part.back ?? { href: '#virksomheden', label: t.common.parts.company }}
116
+ back={part.back ?? { href: '#company', label: t.common.parts.company }}
117
117
  dirty={dirty}
118
118
  saving={saving}
119
119
  onSave={save}