erudit 4.0.0-dev.5 → 4.0.1
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/app/app.vue +34 -34
- package/app/assets/icons/array-check.svg +2 -2
- package/app/assets/icons/array-lines.svg +2 -2
- package/app/assets/icons/array-star.svg +2 -2
- package/app/assets/icons/arrow/from-circle.svg +2 -2
- package/app/assets/icons/arrow/left.svg +2 -2
- package/app/assets/icons/arrow/outward.svg +2 -2
- package/app/assets/icons/arrow/to-circle.svg +2 -2
- package/app/assets/icons/arrow/up-to-right.svg +2 -2
- package/app/assets/icons/aside-open.svg +2 -2
- package/app/assets/icons/asterisk.svg +2 -2
- package/app/assets/icons/book-question.svg +2 -2
- package/app/assets/icons/construction.svg +2 -2
- package/app/assets/icons/draw.svg +2 -2
- package/app/assets/icons/erudit.svg +2 -2
- package/app/assets/icons/file-lines.svg +2 -2
- package/app/assets/icons/files.svg +2 -2
- package/app/assets/icons/folder-open.svg +2 -2
- package/app/assets/icons/folder.svg +2 -2
- package/app/assets/icons/graduation.svg +2 -2
- package/app/assets/icons/handshake.svg +2 -2
- package/app/assets/icons/lines.svg +2 -2
- package/app/assets/icons/plus-circle.svg +2 -2
- package/app/assets/icons/puzzle.svg +2 -2
- package/app/assets/icons/rocket.svg +2 -2
- package/app/assets/icons/search/check.svg +2 -2
- package/app/assets/icons/search/dead.svg +2 -2
- package/app/assets/icons/search/glass.svg +2 -2
- package/app/assets/icons/search/wtf.svg +2 -2
- package/app/assets/icons/sun-moon.svg +2 -2
- package/app/assets/icons/translate.svg +2 -2
- package/app/assets/icons/warning.svg +2 -2
- package/app/components/EruditLink.ts +9 -9
- package/app/components/FancyBold.vue +16 -16
- package/app/components/FancyCard.vue +52 -52
- package/app/components/FancyCardTag.vue +16 -16
- package/app/components/Loading.vue +7 -7
- package/app/components/Prose.vue +55 -55
- package/app/components/ScrollHolder.vue +20 -20
- package/app/components/SmartMedia.vue +107 -107
- package/app/components/ads/Ads.vue +39 -39
- package/app/components/ads/AdsBannerAside.vue +45 -45
- package/app/components/ads/AdsBannerBottom.vue +17 -17
- package/app/components/ads/AdsReplacer.vue +26 -26
- package/app/components/ads/provider/Custom.vue +19 -19
- package/app/components/ads/provider/Yandex.vue +84 -84
- package/app/components/aside/AsideListItem.vue +61 -61
- package/app/components/aside/AsideMajor.vue +8 -8
- package/app/components/aside/AsideMinor.vue +68 -68
- package/app/components/aside/AsidePlainMessage.vue +9 -9
- package/app/components/aside/AsideSwitch.vue +76 -76
- package/app/components/aside/major/PaneHolder.vue +106 -106
- package/app/components/aside/major/PaneSwitcher.vue +68 -68
- package/app/components/aside/major/PaneSwitcherButton.vue +38 -38
- package/app/components/aside/major/PaneTemplate.vue +5 -5
- package/app/components/aside/major/SiteInfo.vue +66 -66
- package/app/components/aside/major/contentNav/PaneBookNav.vue +107 -107
- package/app/components/aside/major/contentNav/PaneGlobalNav.vue +19 -19
- package/app/components/aside/major/contentNav/items/ContentNavBook.vue +21 -21
- package/app/components/aside/major/contentNav/items/ContentNavFolder.vue +91 -91
- package/app/components/aside/major/contentNav/items/ContentNavItem.vue +33 -33
- package/app/components/aside/major/contentNav/items/ContentNavPage.vue +14 -14
- package/app/components/aside/major/contentNav/items/ContentNavSeparator.vue +61 -61
- package/app/components/aside/major/contentNav/items/ContentNavTopic.vue +18 -18
- package/app/components/aside/major/contentNav/items/Flags.vue +50 -50
- package/app/components/aside/major/contentNav/items/ItemTemplate.vue +27 -27
- package/app/components/aside/major/languages/PaneLanguages.vue +55 -55
- package/app/components/aside/major/pages/PanePages.vue +60 -60
- package/app/components/aside/major/search/PaneSearch.vue +156 -156
- package/app/components/aside/major/search/SearchInput.vue +103 -103
- package/app/components/aside/major/search/SearchResult.vue +154 -154
- package/app/components/aside/major/search/SearchStatus.vue +48 -48
- package/app/components/aside/major/search/search.worker.ts +164 -164
- package/app/components/aside/major/settings/BuildTime.vue +25 -25
- package/app/components/aside/major/settings/EngineVersion.vue +14 -14
- package/app/components/aside/major/settings/PaneSettings.vue +17 -17
- package/app/components/aside/major/settings/ThemeSwitcher.vue +55 -55
- package/app/components/aside/major/settings/repository/Repository.vue +33 -33
- package/app/components/aside/major/settings/repository/SecondaryGitHub.vue +71 -71
- package/app/components/aside/minor/AsideMinorPane.vue +5 -5
- package/app/components/aside/minor/AsideMinorPlainHeader.vue +20 -20
- package/app/components/aside/minor/content/AsideMinorContentContributions.vue +47 -47
- package/app/components/aside/minor/content/AsideMinorContentPage.vue +37 -37
- package/app/components/aside/minor/content/AsideMinorContentTopic.vue +61 -61
- package/app/components/aside/minor/content/ButtonPaneContributions.vue +101 -101
- package/app/components/aside/minor/content/ButtonPaneImprove.vue +112 -112
- package/app/components/aside/minor/content/Contribution.vue +36 -36
- package/app/components/aside/minor/content/Toc.vue +163 -163
- package/app/components/aside/minor/content/TocItem.vue +91 -91
- package/app/components/aside/minor/content/TopicPartButton.vue +35 -35
- package/app/components/aside/minor/contributor/AsideMinorContributor.vue +36 -36
- package/app/components/aside/minor/contributor/ItemBook.vue +33 -33
- package/app/components/aside/minor/contributor/ItemContent.vue +17 -17
- package/app/components/aside/minor/news/AsideMinorNews.vue +132 -132
- package/app/components/aside/minor/news/NewsItem.vue +69 -69
- package/app/components/aside/minor/news/RenderNewsElement.vue +42 -42
- package/app/components/aside/minor/news/elements/Mix.vue +11 -11
- package/app/components/aside/minor/news/elements/P.vue +14 -14
- package/app/components/aside/minor/news/elements/Ref.vue +59 -59
- package/app/components/aside/minor/news/elements/Text.vue +18 -18
- package/app/components/indexPage/IndexPagePersons.vue +116 -116
- package/app/components/main/MainAction.vue +26 -26
- package/app/components/main/MainBreadcrumbs.vue +28 -28
- package/app/components/main/MainContentChild.vue +44 -44
- package/app/components/main/MainContentChildren.vue +13 -13
- package/app/components/main/MainDecoration.vue +15 -15
- package/app/components/main/MainDescription.vue +13 -13
- package/app/components/main/MainFlag.vue +129 -129
- package/app/components/main/MainFlags.vue +17 -17
- package/app/components/main/MainGlow.vue +12 -12
- package/app/components/main/MainQuickLink.vue +90 -90
- package/app/components/main/MainQuickLinks.vue +40 -40
- package/app/components/main/MainQuote.vue +149 -149
- package/app/components/main/MainQuoteLoader.vue +86 -86
- package/app/components/main/MainSection.vue +44 -44
- package/app/components/main/MainSectionPreamble.vue +5 -5
- package/app/components/main/MainSubTitle.vue +11 -11
- package/app/components/main/MainTitle.vue +32 -32
- package/app/components/main/MainTopicPartPage.vue +88 -88
- package/app/components/main/MainTopicPartSwitch.vue +67 -67
- package/app/components/main/connections/Deps.vue +30 -30
- package/app/components/main/connections/Externals.vue +80 -80
- package/app/components/main/connections/MainConnections.vue +90 -90
- package/app/components/main/connections/MainConnectionsButton.vue +81 -81
- package/app/components/main/connections/ScrollPane.vue +10 -10
- package/app/components/main/contentStats/Item.vue +28 -28
- package/app/components/main/contentStats/ItemElement.vue +19 -19
- package/app/components/main/contentStats/ItemMaterials.vue +11 -11
- package/app/components/main/contentStats/MainContentStats.vue +50 -50
- package/app/components/preview/Preview.vue +161 -161
- package/app/components/preview/PreviewError.vue +10 -10
- package/app/components/preview/PreviewLoading.vue +5 -5
- package/app/components/preview/PreviewScreen.vue +64 -64
- package/app/components/preview/PreviewScreenButton.vue +39 -39
- package/app/components/preview/screen/ContentPage.vue +47 -47
- package/app/components/preview/screen/DirectLink.vue +23 -23
- package/app/components/preview/screen/Unique.vue +53 -53
- package/app/components/site/SiteAside.vue +67 -67
- package/app/components/site/SiteAsideOverlay.vue +21 -21
- package/app/components/site/SiteMain.vue +13 -13
- package/app/components/transition/Fade.vue +30 -30
- package/app/components/transition/Slide.vue +47 -47
- package/app/components/tree/TreeContainer.vue +5 -5
- package/app/components/tree/TreeItem.vue +36 -36
- package/app/composables/ads.ts +23 -23
- package/app/composables/analytics.ts +102 -102
- package/app/composables/appElements.ts +40 -40
- package/app/composables/aside.ts +28 -28
- package/app/composables/asideMajorPane.ts +135 -135
- package/app/composables/asideMinor.ts +109 -109
- package/app/composables/contentNav.ts +7 -7
- package/app/composables/favicon.ts +103 -103
- package/app/composables/file.ts +4 -4
- package/app/composables/formatText.ts +100 -100
- package/app/composables/loading.ts +26 -26
- package/app/composables/mainContent.ts +31 -31
- package/app/composables/og.ts +184 -184
- package/app/composables/phrases.ts +126 -126
- package/app/composables/preview.ts +53 -53
- package/app/composables/route.ts +6 -6
- package/app/composables/theme.ts +67 -67
- package/app/composables/url.ts +30 -30
- package/app/formatters/ru.ts +14 -14
- package/app/pages/article/[...articleId].vue +12 -12
- package/app/pages/book/[...bookId].vue +56 -56
- package/app/pages/contributor/[contributorId].vue +144 -144
- package/app/pages/contributors.vue +95 -95
- package/app/pages/group/[...groupId].vue +58 -58
- package/app/pages/index.vue +113 -113
- package/app/pages/page/[...pageId].vue +60 -60
- package/app/pages/practice/[...practiceId].vue +12 -12
- package/app/pages/sponsors.vue +88 -88
- package/app/pages/summary/[...summaryId].vue +12 -12
- package/app/plugins/appSetup/client/htmlBranding.ts +6 -6
- package/app/plugins/appSetup/client/welcome.ts +37 -37
- package/app/plugins/appSetup/config.ts +6 -6
- package/app/plugins/appSetup/global.ts +3 -3
- package/app/plugins/appSetup/index.ts +27 -27
- package/app/plugins/prerender.server.ts +40 -40
- package/app/router.options.ts +5 -5
- package/app/scripts/theme.js +30 -30
- package/app/styles/main.css +176 -176
- package/bin/erudit.js +12 -12
- package/modules/erudit/env.ts +8 -8
- package/modules/erudit/globals/content.ts +4 -4
- package/modules/erudit/globals/contributor.ts +1 -1
- package/modules/erudit/globals/eruditConfig.ts +5 -5
- package/modules/erudit/globals/problem.ts +1 -1
- package/modules/erudit/globals/prose.ts +5 -1
- package/modules/erudit/globals/public.ts +18 -18
- package/modules/erudit/index.ts +50 -50
- package/modules/erudit/logger.ts +10 -10
- package/modules/erudit/setup/elements/appTemplate.ts +44 -44
- package/modules/erudit/setup/elements/globalTemplate.ts +82 -82
- package/modules/erudit/setup/elements/globalTypes.ts +259 -259
- package/modules/erudit/setup/elements/setup.ts +163 -163
- package/modules/erudit/setup/elements/shared.ts +10 -10
- package/modules/erudit/setup/elements/tagsTable.ts +28 -28
- package/modules/erudit/setup/fullRestart.ts +61 -61
- package/modules/erudit/setup/globals.ts +210 -206
- package/modules/erudit/setup/publicAssets.ts +43 -43
- package/modules/erudit/setup/runtimeConfig.ts +106 -106
- package/modules/erudit/watcher.ts +20 -20
- package/nuxt.config.ts +102 -102
- package/package.json +5 -5
- package/proxy/prose.app.ts +1 -1
- package/proxy/prose.ts +1 -1
- package/server/api/aside/major/frontNav/book/[...shortId].ts +10 -10
- package/server/api/aside/major/frontNav/global.ts +5 -5
- package/server/api/aside/major/pages.ts +6 -6
- package/server/api/contributor/list.ts +60 -60
- package/server/api/contributor/page/[contributorId].ts +68 -68
- package/server/api/indexPage.ts +81 -81
- package/server/api/language/functions.ts +11 -11
- package/server/api/language/phrase/[phraseKey].ts +35 -35
- package/server/api/main/content/[...contentTypePath].ts +149 -149
- package/server/api/news/batch/[batchIndex].ts +5 -5
- package/server/api/pageSponsors.ts +56 -56
- package/server/api/prerender/content.ts +79 -79
- package/server/api/prerender/default.ts +35 -35
- package/server/api/prerender/files.ts +10 -10
- package/server/api/prerender/frontNav.ts +13 -13
- package/server/api/prerender/language.ts +7 -7
- package/server/api/prerender/news.ts +8 -8
- package/server/api/prerender/quotes.ts +15 -15
- package/server/api/preview/contentPage/[...contentTypePath].ts +63 -63
- package/server/api/preview/contentUnique/[...contentTypePathUnique].ts +73 -73
- package/server/api/problemScript/[...problemScriptPath].ts +88 -88
- package/server/api/quote/data/[quoteId].ts +84 -84
- package/server/api/quote/ids.ts +3 -3
- package/server/erudit/build.ts +132 -132
- package/server/erudit/cameos/build.ts +161 -161
- package/server/erudit/config.ts +13 -13
- package/server/erudit/content/global/build.ts +300 -300
- package/server/erudit/content/global/singleton.ts +5 -5
- package/server/erudit/content/nav/build.ts +459 -459
- package/server/erudit/content/nav/front.ts +125 -125
- package/server/erudit/content/nav/repository/books.ts +37 -37
- package/server/erudit/content/nav/repository/get.ts +33 -33
- package/server/erudit/content/nav/repository/hasChildren.ts +5 -5
- package/server/erudit/content/nav/repository/hasNav.ts +3 -3
- package/server/erudit/content/nav/repository/hasParent.ts +5 -5
- package/server/erudit/content/nav/repository/id.ts +9 -9
- package/server/erudit/content/nav/repository/index.ts +9 -9
- package/server/erudit/content/nav/repository/order.ts +14 -14
- package/server/erudit/content/nav/repository/previousNext.ts +35 -35
- package/server/erudit/content/nav/repository/walk.ts +127 -127
- package/server/erudit/content/nav/setup.ts +13 -13
- package/server/erudit/content/nav/types.ts +24 -24
- package/server/erudit/content/repository/breadcrumbs.ts +24 -24
- package/server/erudit/content/repository/children.ts +47 -47
- package/server/erudit/content/repository/connections.ts +35 -35
- package/server/erudit/content/repository/contentLink.ts +16 -16
- package/server/erudit/content/repository/decoration.ts +23 -23
- package/server/erudit/content/repository/deps.ts +121 -121
- package/server/erudit/content/repository/description.ts +11 -11
- package/server/erudit/content/repository/elementSnippets.ts +105 -105
- package/server/erudit/content/repository/externals.ts +50 -50
- package/server/erudit/content/repository/flags.ts +33 -33
- package/server/erudit/content/repository/seo.ts +12 -12
- package/server/erudit/content/repository/stats.ts +97 -97
- package/server/erudit/content/repository/title.ts +27 -27
- package/server/erudit/content/repository/topicParts.ts +39 -39
- package/server/erudit/content/repository/unique.ts +76 -76
- package/server/erudit/content/resolve/book.ts +29 -29
- package/server/erudit/content/resolve/group.ts +34 -34
- package/server/erudit/content/resolve/index.ts +272 -272
- package/server/erudit/content/resolve/page.ts +80 -80
- package/server/erudit/content/resolve/topic.ts +220 -220
- package/server/erudit/content/resolve/utils/contentError.ts +10 -10
- package/server/erudit/content/resolve/utils/insertContentItem.ts +113 -113
- package/server/erudit/content/resolve/utils/insertContentResolved.ts +126 -126
- package/server/erudit/content/search.ts +146 -146
- package/server/erudit/contributors/build.ts +158 -158
- package/server/erudit/contributors/global.ts +3 -3
- package/server/erudit/contributors/repository/avatarUrl.ts +10 -10
- package/server/erudit/contributors/repository/contributions.ts +171 -171
- package/server/erudit/contributors/repository/count.ts +3 -3
- package/server/erudit/contributors/search.ts +34 -34
- package/server/erudit/db/repository/pushFile.ts +23 -23
- package/server/erudit/db/repository/pushProblemScript.ts +19 -19
- package/server/erudit/db/repository/pushProseLink.ts +40 -40
- package/server/erudit/db/schema/cameos.ts +7 -7
- package/server/erudit/db/schema/content.ts +18 -18
- package/server/erudit/db/schema/contentContributions.ts +15 -15
- package/server/erudit/db/schema/contentDeps.ts +21 -21
- package/server/erudit/db/schema/contentElementStats.ts +20 -20
- package/server/erudit/db/schema/contentProseLinks.ts +23 -23
- package/server/erudit/db/schema/contentSnippets.ts +15 -15
- package/server/erudit/db/schema/contentToc.ts +16 -16
- package/server/erudit/db/schema/contentUniques.ts +19 -19
- package/server/erudit/db/schema/contributors.ts +12 -12
- package/server/erudit/db/schema/files.ts +11 -11
- package/server/erudit/db/schema/groups.ts +6 -6
- package/server/erudit/db/schema/index.ts +17 -17
- package/server/erudit/db/schema/news.ts +7 -7
- package/server/erudit/db/schema/pages.ts +7 -7
- package/server/erudit/db/schema/problemScripts.ts +14 -14
- package/server/erudit/db/schema/sponsors.ts +9 -9
- package/server/erudit/db/schema/topics.ts +9 -9
- package/server/erudit/db/setup.ts +62 -62
- package/server/erudit/db/types.ts +10 -10
- package/server/erudit/global.ts +38 -38
- package/server/erudit/importer.ts +94 -94
- package/server/erudit/index.ts +96 -96
- package/server/erudit/language/list/en.ts +116 -116
- package/server/erudit/language/list/ru.ts +120 -120
- package/server/erudit/language/list.ts +6 -6
- package/server/erudit/language/setup.ts +36 -36
- package/server/erudit/language/types.ts +16 -16
- package/server/erudit/logger.ts +73 -73
- package/server/erudit/news/build.ts +112 -112
- package/server/erudit/news/repository/batch.ts +61 -61
- package/server/erudit/path.ts +19 -19
- package/server/erudit/prose/repository/finalize.ts +68 -68
- package/server/erudit/prose/repository/get.ts +54 -54
- package/server/erudit/prose/repository/resolve.ts +17 -17
- package/server/erudit/prose/storage/callout.ts +16 -16
- package/server/erudit/prose/storage/image.ts +22 -22
- package/server/erudit/prose/storage/link.ts +222 -222
- package/server/erudit/prose/storage/problemScript.ts +23 -23
- package/server/erudit/prose/storage/video.ts +16 -16
- package/server/erudit/prose/transform/bundleProblemScript.ts +6 -6
- package/server/erudit/prose/transform/extensions.ts +15 -15
- package/server/erudit/quote/repository/ids.ts +31 -31
- package/server/erudit/repository.ts +96 -96
- package/server/erudit/sponsors/build.ts +161 -161
- package/server/erudit/sponsors/repository/avatarUrl.ts +10 -10
- package/server/erudit/sponsors/repository/count.ts +4 -4
- package/server/erudit/staticFile.ts +28 -28
- package/server/plugins/augmentCss.ts +17 -17
- package/server/plugins/lang.ts +5 -5
- package/server/plugins/metaViewport.ts +15 -15
- package/server/plugins/theme.ts +13 -13
- package/server/routes/file/[...path].ts +18 -18
- package/server/routes/robots.txt.ts +9 -9
- package/server/routes/search.json.gz.ts +76 -76
- package/server/routes/sitemap.xml.ts +83 -83
- package/shared/search/encoders.ts +20 -20
- package/shared/types/asideMajorPages.ts +4 -4
- package/shared/types/breadcrumbs.ts +9 -9
- package/shared/types/contentChildren.ts +10 -10
- package/shared/types/contentConnections.ts +27 -27
- package/shared/types/contentStats.ts +6 -6
- package/shared/types/elementSnippet.ts +14 -14
- package/shared/types/frontContentNav.ts +38 -38
- package/shared/types/indexPage.ts +20 -20
- package/shared/types/language.ts +191 -191
- package/shared/types/mainContent.ts +69 -69
- package/shared/types/news.ts +13 -13
- package/shared/types/preview.ts +28 -28
- package/shared/types/runtimeConfig.ts +46 -46
- package/shared/types/search.ts +79 -79
- package/shared/utils/contentTypePath.ts +63 -63
- package/shared/utils/icons.ts +11 -11
- package/shared/utils/pages.ts +23 -23
- package/shared/utils/stringColor.ts +13 -13
- package/shared/utils/toStringEqual.ts +21 -21
- package/shared/utils/zip.ts +64 -64
- package/test/shared/utils/zip.test.ts +8 -8
- package/tsconfig.json +17 -17
|
@@ -1,126 +1,126 @@
|
|
|
1
|
-
type PayloadLanguage = {
|
|
2
|
-
phrases: Partial<Record<LanguagePhraseKey, PayloadLanguagePhraseValue>>;
|
|
3
|
-
functions?: Record<string, string>;
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
type PhraseCaller<T extends readonly LanguagePhraseKey[]> = {
|
|
7
|
-
[K in T[number]]: LanguagePhrases[K];
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const initialPayloadLanguage = {
|
|
11
|
-
phrases: {},
|
|
12
|
-
functions: undefined,
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
//
|
|
16
|
-
// "functions" and "phrases" exist both on server and client side
|
|
17
|
-
// On server side they serve as module-cache
|
|
18
|
-
// On client side they act as singletons for whole Nuxt App
|
|
19
|
-
// This is okay because language can't be changed after server initialization
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
let functions: Record<string, Function> | undefined = undefined;
|
|
23
|
-
const phrases: Partial<LanguagePhrases> = {};
|
|
24
|
-
|
|
25
|
-
export function usePhrases<const T extends readonly LanguagePhraseKey[]>(
|
|
26
|
-
...phraseKeys: T
|
|
27
|
-
): Promise<PhraseCaller<T>> {
|
|
28
|
-
const nuxtApp = useNuxtApp();
|
|
29
|
-
const payloadKey = 'raw-language';
|
|
30
|
-
const payloadLanguage: PayloadLanguage =
|
|
31
|
-
(nuxtApp.static.data[payloadKey] ||=
|
|
32
|
-
nuxtApp.payload.data[payloadKey] ||=
|
|
33
|
-
initialPayloadLanguage);
|
|
34
|
-
|
|
35
|
-
const ensureFunctionsInPayload = async () => {
|
|
36
|
-
if (payloadLanguage.functions) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const strFunctions = await $fetch<Record<string, string>>(
|
|
41
|
-
'/api/language/functions',
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
payloadLanguage.functions = strFunctions;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const restoreFunctionsFromPayload = async () => {
|
|
48
|
-
if (functions) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
functions = {};
|
|
53
|
-
for (const [funcName, funcBody] of Object.entries(
|
|
54
|
-
payloadLanguage.functions!,
|
|
55
|
-
)) {
|
|
56
|
-
functions[funcName] = new Function('return ' + funcBody)();
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const processPhrase = async (phraseKey: LanguagePhraseKey) => {
|
|
61
|
-
const phraseInPayload = payloadLanguage.phrases[phraseKey];
|
|
62
|
-
const phraseRestored = phrases[phraseKey];
|
|
63
|
-
|
|
64
|
-
let payloadPhraseValue: PayloadLanguagePhraseValue;
|
|
65
|
-
|
|
66
|
-
if (phraseInPayload) {
|
|
67
|
-
payloadPhraseValue = phraseInPayload;
|
|
68
|
-
} else {
|
|
69
|
-
try {
|
|
70
|
-
payloadPhraseValue = await $fetch<PayloadLanguagePhraseValue>(
|
|
71
|
-
`/api/language/phrase/${phraseKey}`,
|
|
72
|
-
{
|
|
73
|
-
responseType: 'json',
|
|
74
|
-
},
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
payloadLanguage.phrases[phraseKey] = payloadPhraseValue;
|
|
78
|
-
} catch {
|
|
79
|
-
throw createError({
|
|
80
|
-
statusCode: 503,
|
|
81
|
-
statusMessage: 'Service Unavailable',
|
|
82
|
-
message: `Failed to fetch phrase "${phraseKey}"!`,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (payloadPhraseValue.type === 'missing') {
|
|
88
|
-
throw createError({
|
|
89
|
-
statusCode: 404,
|
|
90
|
-
statusMessage: 'Unknown phrase!',
|
|
91
|
-
message: `There is no phrase with key "${phraseKey}"!`,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (phraseRestored) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
switch (payloadPhraseValue.type) {
|
|
100
|
-
case 'string':
|
|
101
|
-
phrases[phraseKey] = payloadPhraseValue.value as any;
|
|
102
|
-
break;
|
|
103
|
-
case 'function':
|
|
104
|
-
phrases[phraseKey] = new Function(
|
|
105
|
-
'funcs',
|
|
106
|
-
`
|
|
107
|
-
with (funcs) {
|
|
108
|
-
return ${payloadPhraseValue.value};
|
|
109
|
-
}
|
|
110
|
-
`,
|
|
111
|
-
)(functions!);
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
return Promise.resolve()
|
|
117
|
-
.then(ensureFunctionsInPayload)
|
|
118
|
-
.then(restoreFunctionsFromPayload)
|
|
119
|
-
.then(() =>
|
|
120
|
-
phraseKeys.reduce<Promise<void>>(
|
|
121
|
-
(p, key) => p.then(() => processPhrase(key)),
|
|
122
|
-
Promise.resolve(),
|
|
123
|
-
),
|
|
124
|
-
)
|
|
125
|
-
.then(() => phrases as PhraseCaller<T>);
|
|
126
|
-
}
|
|
1
|
+
type PayloadLanguage = {
|
|
2
|
+
phrases: Partial<Record<LanguagePhraseKey, PayloadLanguagePhraseValue>>;
|
|
3
|
+
functions?: Record<string, string>;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
type PhraseCaller<T extends readonly LanguagePhraseKey[]> = {
|
|
7
|
+
[K in T[number]]: LanguagePhrases[K];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const initialPayloadLanguage = {
|
|
11
|
+
phrases: {},
|
|
12
|
+
functions: undefined,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
//
|
|
16
|
+
// "functions" and "phrases" exist both on server and client side
|
|
17
|
+
// On server side they serve as module-cache
|
|
18
|
+
// On client side they act as singletons for whole Nuxt App
|
|
19
|
+
// This is okay because language can't be changed after server initialization
|
|
20
|
+
//
|
|
21
|
+
|
|
22
|
+
let functions: Record<string, Function> | undefined = undefined;
|
|
23
|
+
const phrases: Partial<LanguagePhrases> = {};
|
|
24
|
+
|
|
25
|
+
export function usePhrases<const T extends readonly LanguagePhraseKey[]>(
|
|
26
|
+
...phraseKeys: T
|
|
27
|
+
): Promise<PhraseCaller<T>> {
|
|
28
|
+
const nuxtApp = useNuxtApp();
|
|
29
|
+
const payloadKey = 'raw-language';
|
|
30
|
+
const payloadLanguage: PayloadLanguage =
|
|
31
|
+
(nuxtApp.static.data[payloadKey] ||=
|
|
32
|
+
nuxtApp.payload.data[payloadKey] ||=
|
|
33
|
+
initialPayloadLanguage);
|
|
34
|
+
|
|
35
|
+
const ensureFunctionsInPayload = async () => {
|
|
36
|
+
if (payloadLanguage.functions) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const strFunctions = await $fetch<Record<string, string>>(
|
|
41
|
+
'/api/language/functions',
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
payloadLanguage.functions = strFunctions;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const restoreFunctionsFromPayload = async () => {
|
|
48
|
+
if (functions) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
functions = {};
|
|
53
|
+
for (const [funcName, funcBody] of Object.entries(
|
|
54
|
+
payloadLanguage.functions!,
|
|
55
|
+
)) {
|
|
56
|
+
functions[funcName] = new Function('return ' + funcBody)();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const processPhrase = async (phraseKey: LanguagePhraseKey) => {
|
|
61
|
+
const phraseInPayload = payloadLanguage.phrases[phraseKey];
|
|
62
|
+
const phraseRestored = phrases[phraseKey];
|
|
63
|
+
|
|
64
|
+
let payloadPhraseValue: PayloadLanguagePhraseValue;
|
|
65
|
+
|
|
66
|
+
if (phraseInPayload) {
|
|
67
|
+
payloadPhraseValue = phraseInPayload;
|
|
68
|
+
} else {
|
|
69
|
+
try {
|
|
70
|
+
payloadPhraseValue = await $fetch<PayloadLanguagePhraseValue>(
|
|
71
|
+
`/api/language/phrase/${phraseKey}`,
|
|
72
|
+
{
|
|
73
|
+
responseType: 'json',
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
payloadLanguage.phrases[phraseKey] = payloadPhraseValue;
|
|
78
|
+
} catch {
|
|
79
|
+
throw createError({
|
|
80
|
+
statusCode: 503,
|
|
81
|
+
statusMessage: 'Service Unavailable',
|
|
82
|
+
message: `Failed to fetch phrase "${phraseKey}"!`,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (payloadPhraseValue.type === 'missing') {
|
|
88
|
+
throw createError({
|
|
89
|
+
statusCode: 404,
|
|
90
|
+
statusMessage: 'Unknown phrase!',
|
|
91
|
+
message: `There is no phrase with key "${phraseKey}"!`,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (phraseRestored) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
switch (payloadPhraseValue.type) {
|
|
100
|
+
case 'string':
|
|
101
|
+
phrases[phraseKey] = payloadPhraseValue.value as any;
|
|
102
|
+
break;
|
|
103
|
+
case 'function':
|
|
104
|
+
phrases[phraseKey] = new Function(
|
|
105
|
+
'funcs',
|
|
106
|
+
`
|
|
107
|
+
with (funcs) {
|
|
108
|
+
return ${payloadPhraseValue.value};
|
|
109
|
+
}
|
|
110
|
+
`,
|
|
111
|
+
)(functions!);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
return Promise.resolve()
|
|
117
|
+
.then(ensureFunctionsInPayload)
|
|
118
|
+
.then(restoreFunctionsFromPayload)
|
|
119
|
+
.then(() =>
|
|
120
|
+
phraseKeys.reduce<Promise<void>>(
|
|
121
|
+
(p, key) => p.then(() => processPhrase(key)),
|
|
122
|
+
Promise.resolve(),
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
.then(() => phrases as PhraseCaller<T>);
|
|
126
|
+
}
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
export const usePreview = () => {
|
|
2
|
-
const previewState = useState<PreviewState>('preview', () => ({
|
|
3
|
-
opened: false,
|
|
4
|
-
request: undefined,
|
|
5
|
-
history: [],
|
|
6
|
-
blink: 0,
|
|
7
|
-
}));
|
|
8
|
-
|
|
9
|
-
const hasPreviousRequest = computed(
|
|
10
|
-
() => previewState.value.history.length > 0,
|
|
11
|
-
);
|
|
12
|
-
|
|
13
|
-
function setPreview(request: PreviewRequest) {
|
|
14
|
-
if (toStringEqual(request, previewState.value.request)) {
|
|
15
|
-
if (previewState.value.opened) {
|
|
16
|
-
previewState.value.blink += 1;
|
|
17
|
-
} else {
|
|
18
|
-
previewState.value.opened = true;
|
|
19
|
-
}
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (previewState.value.request) {
|
|
24
|
-
previewState.value.history.push(previewState.value.request);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
previewState.value.request = request;
|
|
28
|
-
previewState.value.opened = true;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function setPreviousPreview() {
|
|
32
|
-
if (!hasPreviousRequest.value) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
previewState.value.request = previewState.value.history.pop();
|
|
37
|
-
previewState.value.opened = true;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function closePreview() {
|
|
41
|
-
previewState.value.opened = false;
|
|
42
|
-
previewState.value.request = undefined;
|
|
43
|
-
previewState.value.history = [];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
previewState,
|
|
48
|
-
hasPreviousRequest,
|
|
49
|
-
setPreview,
|
|
50
|
-
setPreviousPreview,
|
|
51
|
-
closePreview,
|
|
52
|
-
};
|
|
53
|
-
};
|
|
1
|
+
export const usePreview = () => {
|
|
2
|
+
const previewState = useState<PreviewState>('preview', () => ({
|
|
3
|
+
opened: false,
|
|
4
|
+
request: undefined,
|
|
5
|
+
history: [],
|
|
6
|
+
blink: 0,
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
const hasPreviousRequest = computed(
|
|
10
|
+
() => previewState.value.history.length > 0,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
function setPreview(request: PreviewRequest) {
|
|
14
|
+
if (toStringEqual(request, previewState.value.request)) {
|
|
15
|
+
if (previewState.value.opened) {
|
|
16
|
+
previewState.value.blink += 1;
|
|
17
|
+
} else {
|
|
18
|
+
previewState.value.opened = true;
|
|
19
|
+
}
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (previewState.value.request) {
|
|
24
|
+
previewState.value.history.push(previewState.value.request);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
previewState.value.request = request;
|
|
28
|
+
previewState.value.opened = true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function setPreviousPreview() {
|
|
32
|
+
if (!hasPreviousRequest.value) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
previewState.value.request = previewState.value.history.pop();
|
|
37
|
+
previewState.value.opened = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function closePreview() {
|
|
41
|
+
previewState.value.opened = false;
|
|
42
|
+
previewState.value.request = undefined;
|
|
43
|
+
previewState.value.history = [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
previewState,
|
|
48
|
+
hasPreviousRequest,
|
|
49
|
+
setPreview,
|
|
50
|
+
setPreviousPreview,
|
|
51
|
+
closePreview,
|
|
52
|
+
};
|
|
53
|
+
};
|
package/app/composables/route.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export function useRoutePath() {
|
|
2
|
-
const route = useRoute();
|
|
3
|
-
return computed(() =>
|
|
4
|
-
route.path.endsWith('/') ? route.path : route.path + '/',
|
|
5
|
-
);
|
|
6
|
-
}
|
|
1
|
+
export function useRoutePath() {
|
|
2
|
+
const route = useRoute();
|
|
3
|
+
return computed(() =>
|
|
4
|
+
route.path.endsWith('/') ? route.path : route.path + '/',
|
|
5
|
+
);
|
|
6
|
+
}
|
package/app/composables/theme.ts
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
const binaryThemes = ['light', 'dark'] as const;
|
|
2
|
-
const themePreferences = [...binaryThemes, 'system'] as const;
|
|
3
|
-
const localStorageKey = 'theme';
|
|
4
|
-
|
|
5
|
-
export type BinaryTheme = (typeof binaryThemes)[number];
|
|
6
|
-
export type ThemePreference = (typeof themePreferences)[number];
|
|
7
|
-
|
|
8
|
-
export const useTheme = () => {
|
|
9
|
-
if (import.meta.server) {
|
|
10
|
-
throw createError({
|
|
11
|
-
statusCode: 400,
|
|
12
|
-
statusMessage: 'Theme composable is not available on server side!',
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const preference = useState(
|
|
17
|
-
'theme-preference',
|
|
18
|
-
() => localStorage.getItem(localStorageKey) as ThemePreference,
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
themePref: preference,
|
|
23
|
-
binaryTheme: computed(() => pref2binary(preference.value)),
|
|
24
|
-
setTheme: (newPref: ThemePreference) => {
|
|
25
|
-
localStorage.setItem(localStorageKey, newPref);
|
|
26
|
-
preference.value = newPref;
|
|
27
|
-
},
|
|
28
|
-
cycleTheme: () => {
|
|
29
|
-
const current = preference.value;
|
|
30
|
-
const next =
|
|
31
|
-
themePreferences[
|
|
32
|
-
(themePreferences.indexOf(current) + 1) % themePreferences.length
|
|
33
|
-
]!;
|
|
34
|
-
localStorage.setItem(localStorageKey, next);
|
|
35
|
-
preference.value = next;
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export const initThemeWatcher = () => {
|
|
41
|
-
onMounted(() => {
|
|
42
|
-
const { themePref, binaryTheme } = useTheme();
|
|
43
|
-
|
|
44
|
-
watch(binaryTheme, (newValue) => {
|
|
45
|
-
document.documentElement.setAttribute('data-theme', newValue);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
window
|
|
49
|
-
.matchMedia('(prefers-color-scheme: dark)')
|
|
50
|
-
.addEventListener('change', (event) => {
|
|
51
|
-
const newValue = event.matches ? 'dark' : 'light';
|
|
52
|
-
if (themePref.value === 'system') {
|
|
53
|
-
document.documentElement.setAttribute('data-theme', newValue);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
function pref2binary(pref: ThemePreference): BinaryTheme {
|
|
60
|
-
if (binaryThemes.includes(pref as BinaryTheme)) {
|
|
61
|
-
return pref as BinaryTheme;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
65
|
-
? 'dark'
|
|
66
|
-
: 'light';
|
|
67
|
-
}
|
|
1
|
+
const binaryThemes = ['light', 'dark'] as const;
|
|
2
|
+
const themePreferences = [...binaryThemes, 'system'] as const;
|
|
3
|
+
const localStorageKey = 'theme';
|
|
4
|
+
|
|
5
|
+
export type BinaryTheme = (typeof binaryThemes)[number];
|
|
6
|
+
export type ThemePreference = (typeof themePreferences)[number];
|
|
7
|
+
|
|
8
|
+
export const useTheme = () => {
|
|
9
|
+
if (import.meta.server) {
|
|
10
|
+
throw createError({
|
|
11
|
+
statusCode: 400,
|
|
12
|
+
statusMessage: 'Theme composable is not available on server side!',
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const preference = useState(
|
|
17
|
+
'theme-preference',
|
|
18
|
+
() => localStorage.getItem(localStorageKey) as ThemePreference,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
themePref: preference,
|
|
23
|
+
binaryTheme: computed(() => pref2binary(preference.value)),
|
|
24
|
+
setTheme: (newPref: ThemePreference) => {
|
|
25
|
+
localStorage.setItem(localStorageKey, newPref);
|
|
26
|
+
preference.value = newPref;
|
|
27
|
+
},
|
|
28
|
+
cycleTheme: () => {
|
|
29
|
+
const current = preference.value;
|
|
30
|
+
const next =
|
|
31
|
+
themePreferences[
|
|
32
|
+
(themePreferences.indexOf(current) + 1) % themePreferences.length
|
|
33
|
+
]!;
|
|
34
|
+
localStorage.setItem(localStorageKey, next);
|
|
35
|
+
preference.value = next;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const initThemeWatcher = () => {
|
|
41
|
+
onMounted(() => {
|
|
42
|
+
const { themePref, binaryTheme } = useTheme();
|
|
43
|
+
|
|
44
|
+
watch(binaryTheme, (newValue) => {
|
|
45
|
+
document.documentElement.setAttribute('data-theme', newValue);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
window
|
|
49
|
+
.matchMedia('(prefers-color-scheme: dark)')
|
|
50
|
+
.addEventListener('change', (event) => {
|
|
51
|
+
const newValue = event.matches ? 'dark' : 'light';
|
|
52
|
+
if (themePref.value === 'system') {
|
|
53
|
+
document.documentElement.setAttribute('data-theme', newValue);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function pref2binary(pref: ThemePreference): BinaryTheme {
|
|
60
|
+
if (binaryThemes.includes(pref as BinaryTheme)) {
|
|
61
|
+
return pref as BinaryTheme;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
65
|
+
? 'dark'
|
|
66
|
+
: 'light';
|
|
67
|
+
}
|
package/app/composables/url.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { sn } from 'unslash';
|
|
2
|
-
|
|
3
|
-
export function useBaseUrl() {
|
|
4
|
-
const runtimeConfig = useRuntimeConfig();
|
|
5
|
-
const baseUrl = runtimeConfig.app.baseURL;
|
|
6
|
-
|
|
7
|
-
return (path: string): string => {
|
|
8
|
-
if (path.startsWith('/')) {
|
|
9
|
-
return `${baseUrl}${path.slice(1)}`;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return path;
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function useSiteUrl() {
|
|
17
|
-
const runtimeConfig = useRuntimeConfig();
|
|
18
|
-
const siteUrl = runtimeConfig.public.siteUrl;
|
|
19
|
-
|
|
20
|
-
return (path?: string): string => {
|
|
21
|
-
// Return external URLs as is
|
|
22
|
-
if (path) {
|
|
23
|
-
if (!path.startsWith('/')) {
|
|
24
|
-
return path;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return sn(siteUrl, path || '');
|
|
29
|
-
};
|
|
30
|
-
}
|
|
1
|
+
import { sn } from 'unslash';
|
|
2
|
+
|
|
3
|
+
export function useBaseUrl() {
|
|
4
|
+
const runtimeConfig = useRuntimeConfig();
|
|
5
|
+
const baseUrl = runtimeConfig.app.baseURL;
|
|
6
|
+
|
|
7
|
+
return (path: string): string => {
|
|
8
|
+
if (path.startsWith('/')) {
|
|
9
|
+
return `${baseUrl}${path.slice(1)}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return path;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function useSiteUrl() {
|
|
17
|
+
const runtimeConfig = useRuntimeConfig();
|
|
18
|
+
const siteUrl = runtimeConfig.public.siteUrl;
|
|
19
|
+
|
|
20
|
+
return (path?: string): string => {
|
|
21
|
+
// Return external URLs as is
|
|
22
|
+
if (path) {
|
|
23
|
+
if (!path.startsWith('/')) {
|
|
24
|
+
return path;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return sn(siteUrl, path || '');
|
|
29
|
+
};
|
|
30
|
+
}
|
package/app/formatters/ru.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
export default (text: string) => {
|
|
2
|
-
text = ruStickyPrepositions(text);
|
|
3
|
-
return text;
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Formats prepositions in Russian text so that they are always adjacent to the next word and are not left hanging “in the air” when the line breaks.
|
|
8
|
-
*/
|
|
9
|
-
function ruStickyPrepositions(text: string): string {
|
|
10
|
-
return text.replace(
|
|
11
|
-
/ (в|не|без|для|до|за|из|к|на|над|о|об|от|по|под|при|про|с|у|через|вокруг|около|после|перед|между|внутри|вне|из-за|из-под|ради|сквозь|среди|насчёт|вследствие|благодаря|несмотря|наперекор|вопреки|подле|возле|рядом|навстречу) /gimu,
|
|
12
|
-
' $1\xa0',
|
|
13
|
-
);
|
|
14
|
-
}
|
|
1
|
+
export default (text: string) => {
|
|
2
|
+
text = ruStickyPrepositions(text);
|
|
3
|
+
return text;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Formats prepositions in Russian text so that they are always adjacent to the next word and are not left hanging “in the air” when the line breaks.
|
|
8
|
+
*/
|
|
9
|
+
function ruStickyPrepositions(text: string): string {
|
|
10
|
+
return text.replace(
|
|
11
|
+
/ (в|не|без|для|до|за|из|к|на|над|о|об|от|по|под|при|про|с|у|через|вокруг|около|после|перед|между|внутри|вне|из-за|из-под|ради|сквозь|среди|насчёт|вследствие|благодаря|несмотря|наперекор|вопреки|подле|возле|рядом|навстречу) /gimu,
|
|
12
|
+
' $1\xa0',
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const route = useRoute();
|
|
3
|
-
const articleId = Array.isArray(route.params.articleId)
|
|
4
|
-
? route.params.articleId.join('/')
|
|
5
|
-
: route.params.articleId!;
|
|
6
|
-
const contentTypePath = stringifyContentTypePath('article', articleId);
|
|
7
|
-
const mainContent = await useMainContent<MainContentTopicPart>(contentTypePath);
|
|
8
|
-
</script>
|
|
9
|
-
|
|
10
|
-
<template>
|
|
11
|
-
<MainTopicPartPage :mainContent />
|
|
12
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const route = useRoute();
|
|
3
|
+
const articleId = Array.isArray(route.params.articleId)
|
|
4
|
+
? route.params.articleId.join('/')
|
|
5
|
+
: route.params.articleId!;
|
|
6
|
+
const contentTypePath = stringifyContentTypePath('article', articleId);
|
|
7
|
+
const mainContent = await useMainContent<MainContentTopicPart>(contentTypePath);
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<MainTopicPartPage :mainContent />
|
|
12
|
+
</template>
|