@myelophone/nuxt 0.18.2

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 (259) hide show
  1. package/.devcontainer/Dockerfile +7 -0
  2. package/.devcontainer/devcontainer.json +21 -0
  3. package/.editorconfig +43 -0
  4. package/.github/workflows/package.yml +73 -0
  5. package/.nuxtignore +6 -0
  6. package/.releaserc.js +132 -0
  7. package/.yarn/releases/yarn-4.18.0.cjs +1000 -0
  8. package/.yarnrc.yml +8 -0
  9. package/CHANGELOG.md +141 -0
  10. package/LICENSE +77 -0
  11. package/README.md +3004 -0
  12. package/app/app.vue +455 -0
  13. package/app/assets/css/animation.css +81 -0
  14. package/app/assets/css/backgrounds.css +57 -0
  15. package/app/assets/css/base.css +182 -0
  16. package/app/assets/css/index.css +9 -0
  17. package/app/assets/css/links.css +12 -0
  18. package/app/assets/css/multimedia.css +102 -0
  19. package/app/assets/css/preloader.css +68 -0
  20. package/app/assets/css/print.css +40 -0
  21. package/app/assets/css/tailwind.css +20 -0
  22. package/app/assets/css/theme.css +29 -0
  23. package/app/components/consent/GoogleMap.vue +59 -0
  24. package/app/components/consent/Youtube.vue +27 -0
  25. package/app/components/cookie/Banner.vue +70 -0
  26. package/app/components/cookie/ConsentWrapper.vue +71 -0
  27. package/app/components/cookie/PrivacyPolicy.vue +179 -0
  28. package/app/components/cookie/SettingsModal.vue +387 -0
  29. package/app/components/grid/Col.vue +262 -0
  30. package/app/components/grid/Container.vue +236 -0
  31. package/app/components/grid/Row.vue +248 -0
  32. package/app/components/grid/Stack.vue +233 -0
  33. package/app/components/myelophone/Copyright.vue +5 -0
  34. package/app/components/myelophone/Welcome.vue +107 -0
  35. package/app/components/page/FullscreenPreloader.vue +177 -0
  36. package/app/components/page/Preloader.vue +18 -0
  37. package/app/components/page/ReadyLocalized.vue +19 -0
  38. package/app/components/ready/.gitkeep +0 -0
  39. package/app/components/safe/Email.vue +52 -0
  40. package/app/components/safe/ImgWithLoader.vue +180 -0
  41. package/app/components/safe/NuxtImg.vue +25 -0
  42. package/app/components/safe/NuxtPicture.vue +29 -0
  43. package/app/components/section/.gitkeep +0 -0
  44. package/app/components/seo/BrandInfo.vue +76 -0
  45. package/app/components/seo/ContentNoIndex.vue +35 -0
  46. package/app/components/seo/NoIndex.vue +22 -0
  47. package/app/components/seo/OrgInfo.vue +130 -0
  48. package/app/components/seo/ProfileInfo.vue +137 -0
  49. package/app/components/ui/Accordion.vue +163 -0
  50. package/app/components/ui/Alert.vue +179 -0
  51. package/app/components/ui/Avatar.vue +88 -0
  52. package/app/components/ui/Badge.vue +150 -0
  53. package/app/components/ui/Banner.vue +133 -0
  54. package/app/components/ui/Button.vue +175 -0
  55. package/app/components/ui/Card.vue +66 -0
  56. package/app/components/ui/Checkbox.vue +134 -0
  57. package/app/components/ui/Chip.vue +136 -0
  58. package/app/components/ui/CommandPalette.vue +434 -0
  59. package/app/components/ui/Console.vue +583 -0
  60. package/app/components/ui/CursorCreative.vue +148 -0
  61. package/app/components/ui/FeatureAccordion.vue +165 -0
  62. package/app/components/ui/FileUpload.vue +705 -0
  63. package/app/components/ui/GeoDependent.vue +72 -0
  64. package/app/components/ui/Heading.vue +51 -0
  65. package/app/components/ui/Icon.vue +42 -0
  66. package/app/components/ui/Icon8.vue +39 -0
  67. package/app/components/ui/Input.vue +88 -0
  68. package/app/components/ui/LangLink.vue +34 -0
  69. package/app/components/ui/LangVisible.vue +33 -0
  70. package/app/components/ui/LanguageSelect.vue +202 -0
  71. package/app/components/ui/LightBox.vue +155 -0
  72. package/app/components/ui/LocalTime.vue +170 -0
  73. package/app/components/ui/Modal.vue +161 -0
  74. package/app/components/ui/Popup.vue +200 -0
  75. package/app/components/ui/PostContent.server.vue +23 -0
  76. package/app/components/ui/ProtectedEmail.vue +52 -0
  77. package/app/components/ui/RelativeTime.vue +141 -0
  78. package/app/components/ui/ScheduledContent.vue +150 -0
  79. package/app/components/ui/ScrollToTop.client.vue +48 -0
  80. package/app/components/ui/SegmentedControl.vue +148 -0
  81. package/app/components/ui/SiteSearch.vue +1191 -0
  82. package/app/components/ui/Skeleton.vue +91 -0
  83. package/app/components/ui/SmartContrast.vue +74 -0
  84. package/app/components/ui/SnapContainer.vue +222 -0
  85. package/app/components/ui/SnapSection.vue +294 -0
  86. package/app/components/ui/SonnerStyle.vue +7 -0
  87. package/app/components/ui/SplitSection.vue +99 -0
  88. package/app/components/ui/Steps.vue +858 -0
  89. package/app/components/ui/StickyWrapper.vue +46 -0
  90. package/app/components/ui/Table.vue +251 -0
  91. package/app/components/ui/Tabs.vue +451 -0
  92. package/app/components/ui/TextColumn.vue +36 -0
  93. package/app/components/ui/Textarea.vue +251 -0
  94. package/app/components/ui/Toggler.vue +40 -0
  95. package/app/components/ui/Tooltip.vue +524 -0
  96. package/app/components/ui/TruncateText.vue +105 -0
  97. package/app/components/ui/ViewportSpacer.vue +72 -0
  98. package/app/components/view/AnimatedCounter.vue +121 -0
  99. package/app/components/view/AnnouncementBar.vue +104 -0
  100. package/app/components/view/AvatarGroup.vue +104 -0
  101. package/app/components/view/Breadcrumbs.vue +97 -0
  102. package/app/components/view/ContentWithToc.vue +152 -0
  103. package/app/components/view/Copyright.vue +68 -0
  104. package/app/components/view/CountdownTimer.vue +184 -0
  105. package/app/components/view/CreativeCTA.vue +143 -0
  106. package/app/components/view/CurrencySelect.vue +254 -0
  107. package/app/components/view/FeatureBoxGrid.vue +142 -0
  108. package/app/components/view/HorizontalMenu.vue +97 -0
  109. package/app/components/view/ImageCompare.vue +141 -0
  110. package/app/components/view/ImgSlider.vue +333 -0
  111. package/app/components/view/InfiniteMarquee.vue +147 -0
  112. package/app/components/view/InfoBar.vue +86 -0
  113. package/app/components/view/LogoGrid.vue +106 -0
  114. package/app/components/view/LogoSlider.vue +83 -0
  115. package/app/components/view/NotFound.vue +81 -0
  116. package/app/components/view/PartialBackground.vue +313 -0
  117. package/app/components/view/QuoteBig.vue +111 -0
  118. package/app/components/view/ResponsiveMenu.vue +219 -0
  119. package/app/components/view/ReviewsSlider.vue +230 -0
  120. package/app/components/view/SocialBar.vue +78 -0
  121. package/app/components/view/StickyHeadingContent.vue +158 -0
  122. package/app/components/view/TallyForm.vue +295 -0
  123. package/app/components/view/WarningOutdated.vue +113 -0
  124. package/app/composables/useApi.ts +88 -0
  125. package/app/composables/useAppSeo.ts +78 -0
  126. package/app/composables/useBreadcrumbs.ts +106 -0
  127. package/app/composables/useChangeUrl.ts +73 -0
  128. package/app/composables/useCommand.ts +28 -0
  129. package/app/composables/useCookieControl.ts +111 -0
  130. package/app/composables/useCookieModal.ts +9 -0
  131. package/app/composables/useDevice.ts +109 -0
  132. package/app/composables/useGeoFetch.ts +139 -0
  133. package/app/composables/useLoadScript.ts +36 -0
  134. package/app/composables/useModalState.ts +37 -0
  135. package/app/composables/useMultiLang.ts +278 -0
  136. package/app/composables/useNetwork.ts +49 -0
  137. package/app/composables/useNuxtVersion.ts +5 -0
  138. package/app/composables/usePiniaStore.ts +5 -0
  139. package/app/composables/usePopupControl.ts +57 -0
  140. package/app/composables/useQuery.ts +307 -0
  141. package/app/composables/useReadyPage.ts +182 -0
  142. package/app/composables/useSafeList.ts +90 -0
  143. package/app/composables/useScriptLoader.ts +221 -0
  144. package/app/composables/useSiteSearch.ts +32 -0
  145. package/app/composables/useSnapScroll.ts +24 -0
  146. package/app/composables/useStatic.ts +5 -0
  147. package/app/composables/useStorage.ts +252 -0
  148. package/app/composables/useStoreBroadcast.ts +146 -0
  149. package/app/composables/useThemeSync.ts +81 -0
  150. package/app/constants/layout.ts +109 -0
  151. package/app/constants/predefinedCookieScripts.ts +1222 -0
  152. package/app/constants/ui-events.ts +1 -0
  153. package/app/error.vue +169 -0
  154. package/app/layouts/default-blog.vue +5 -0
  155. package/app/layouts/default.vue +3 -0
  156. package/app/locales/en/commands.json +5 -0
  157. package/app/locales/en/cookies.json +182 -0
  158. package/app/locales/en/error.json +4 -0
  159. package/app/locales/en/interface.json +33 -0
  160. package/app/locales/en/myelophone.json +8 -0
  161. package/app/locales/en/outdated.json +5 -0
  162. package/app/locales/en/times.json +18 -0
  163. package/app/locales/pl/commands.json +5 -0
  164. package/app/locales/pl/cookies.json +182 -0
  165. package/app/locales/pl/error.json +4 -0
  166. package/app/locales/pl/interface.json +33 -0
  167. package/app/locales/pl/myelophone.json +8 -0
  168. package/app/locales/pl/outdated.json +5 -0
  169. package/app/locales/pl/times.json +18 -0
  170. package/app/locales/ru/commands.json +5 -0
  171. package/app/locales/ru/cookies.json +182 -0
  172. package/app/locales/ru/error.json +4 -0
  173. package/app/locales/ru/interface.json +33 -0
  174. package/app/locales/ru/myelophone.json +8 -0
  175. package/app/locales/ru/outdated.json +5 -0
  176. package/app/locales/ru/times.json +18 -0
  177. package/app/middleware/blog-layout.ts +28 -0
  178. package/app/middleware/i18n.global.ts +17 -0
  179. package/app/middleware/not-found-status.ts +11 -0
  180. package/app/modules/default-blog/index.ts +39 -0
  181. package/app/modules/default-blog/posts.ts +80 -0
  182. package/app/modules/default-sitemap/index.ts +116 -0
  183. package/app/modules/default-sitemap/runtime/sitemap.get.ts +42 -0
  184. package/app/modules/fullscreen-preloader/module.ts +43 -0
  185. package/app/modules/htaccess/.htaccess +38 -0
  186. package/app/modules/htaccess/index.ts +23 -0
  187. package/app/modules/i18n-shaker.mjs +221 -0
  188. package/app/modules/multi18n/module.ts +71 -0
  189. package/app/modules/myelophone-config/module.ts +241 -0
  190. package/app/pages/[...all].vue +9 -0
  191. package/app/pages/index.vue +3 -0
  192. package/app/pages/post/[...slug].vue +30 -0
  193. package/app/plugins/bfcache-refresh.client.ts +9 -0
  194. package/app/plugins/canonical.ts +30 -0
  195. package/app/plugins/csp-nonce.server.ts +41 -0
  196. package/app/plugins/multi18n.client.ts +21 -0
  197. package/app/plugins/multi18n.server.ts +15 -0
  198. package/app/plugins/nuxt-version.client.ts +4 -0
  199. package/app/plugins/restore.client.ts +21 -0
  200. package/app/plugins/reveal.ts +89 -0
  201. package/app/plugins/url-helper.client.ts +66 -0
  202. package/app/server/api/exchange-rates.get.ts +216 -0
  203. package/app/server/api/healthz.ts +1 -0
  204. package/app/server/api/logout.post.ts +6 -0
  205. package/app/server/middleware/normalize-url.ts +14 -0
  206. package/app/server/middleware/security-headers.ts +147 -0
  207. package/app/server/middleware/url-helper.ts +43 -0
  208. package/app/server/plugins/clean-html-spaces.ts +15 -0
  209. package/app/server/plugins/clean-styles.ts +94 -0
  210. package/app/server/plugins/compression.ts +122 -0
  211. package/app/server/plugins/css-preload.ts +26 -0
  212. package/app/server/plugins/early-hints.ts +31 -0
  213. package/app/server/plugins/force-noindex.ts +43 -0
  214. package/app/server/plugins/html-one-line.ts +17 -0
  215. package/app/server/plugins/inline-style.ts +7 -0
  216. package/app/server/routes/healthz.get.ts +1 -0
  217. package/app/server/tsconfig.json +3 -0
  218. package/app/stores/cart.ts +945 -0
  219. package/app/stores/commands.ts +43 -0
  220. package/app/stores/device.ts +156 -0
  221. package/app/stores/querySync.ts +5 -0
  222. package/app/stores/settings.ts +145 -0
  223. package/app/stores/user.ts +607 -0
  224. package/app/types/cookie.ts +89 -0
  225. package/app/types/myelophone.d.ts +87 -0
  226. package/app/types/site-search.ts +110 -0
  227. package/app/utils/asyncRefresh.ts +4 -0
  228. package/app/utils/blog.server.ts +142 -0
  229. package/app/utils/jsonLd.ts +56 -0
  230. package/app/utils/myelophoneConfig.ts +4 -0
  231. package/app/utils/sortBy.ts +18 -0
  232. package/app/utils/transliterate.ts +75 -0
  233. package/app/utils/ytVideo.ts +37 -0
  234. package/app/workers/site-search.worker.ts +661 -0
  235. package/biome.json +94 -0
  236. package/commitlint.config.js +3 -0
  237. package/nuxt.config.ts +539 -0
  238. package/package.json +101 -0
  239. package/playground/content/.gitkeep +0 -0
  240. package/playground/locales/.gitkeep +0 -0
  241. package/playground/myelophone.dev.ts +45 -0
  242. package/playground/nuxt.config.ts +105 -0
  243. package/playground/package.json +5 -0
  244. package/playground/pages/.gitkeep +0 -0
  245. package/playground/tsconfig.json +3 -0
  246. package/playwright.config.ts +23 -0
  247. package/public/browsers/chrome.svg +77 -0
  248. package/public/browsers/edge.svg +98 -0
  249. package/public/browsers/firefox.svg +300 -0
  250. package/public/favicon.ico +0 -0
  251. package/public/myelophone_eng.png +0 -0
  252. package/public/myelophone_eng_white.png +0 -0
  253. package/spa-loading-template.html +72 -0
  254. package/tests/home.spec.ts +9 -0
  255. package/tests/utils.ts +23 -0
  256. package/tsconfig.json +16 -0
  257. package/vite/plugins/clean-css.ts +190 -0
  258. package/vite/plugins/postcss-add-viewportunits.ts +33 -0
  259. package/vite/plugins/postcss-viewport-fallback.ts +49 -0
@@ -0,0 +1,33 @@
1
+ {
2
+ "expand": "Zwiększać",
3
+ "collapse": "Zawalić się",
4
+ "close": "Zamknąć",
5
+ "readMore": "Przeczytaj więcej",
6
+ "goMore": "Idź dalej",
7
+ "goHome": "Do strony głównej",
8
+ "goHomeFull": "Przejdź do strony głównej",
9
+ "pageNotFound": "Ups! Strona nie została znaleziona.",
10
+ "pageNotFoundText": "Strona, której szukasz mogła zostać usunięta lub nigdy nie istniała.",
11
+ "pageNotFoundDescription": "Strona nie została znaleziona. Sprawdź adres URL.",
12
+ "serverError": "Błąd serwera",
13
+ "somethingWentWrong": "Wystąpił nieoczekiwany błąd na serwerze. Pracujemy już nad rozwiązaniem tego problemu. Spróbuj ponownie później.",
14
+ "updatePage": "Odśwież stronę",
15
+ "noGoogleMapAddress": "Nie podano adresu ani identyfikatora miejsca",
16
+ "nodata": "Brak danych",
17
+ "siteSearch": {
18
+ "ariaLabel": "Wyszukiwanie w witrynie",
19
+ "placeholder": "Szukaj w treści stron…",
20
+ "clear": "Wyczyść wyszukiwanie",
21
+ "indexing": "Przygotowywanie wyszukiwania…",
22
+ "indexingProgress": "Przygotowywanie: {indexed} z {total} stron",
23
+ "indexed": "Zindeksowane strony: {count}",
24
+ "remote": "Wyszukiwanie na serwerze",
25
+ "searching": "Wyszukiwanie…",
26
+ "operatorLabel": "Łączenie wyszukiwanych słów za pomocą AND lub OR",
27
+ "results": "Wyniki wyszukiwania",
28
+ "noResults": "Nic nie znaleziono",
29
+ "typeMore": "Wpisz co najmniej {count} znaki(-ów)",
30
+ "prefilled": "Zapytanie jest gotowe. Naciśnij Enter lub zmień je, aby wyszukać.",
31
+ "error": "Nie udało się przygotować wyszukiwania. Spróbuj ponownie."
32
+ }
33
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "welcome": "Witamy w",
3
+ "experiment": "Eksperymentuj, dostosowuj i ciesz się szybkością i wydajnością!",
4
+ "poweredBy": "Zasilane przez",
5
+ "templateVersion": "Wersja szablonu",
6
+ "nuxtVersion": "Wersja Nuxt",
7
+ "toggleTheme": "Przełącz motyw"
8
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "browserNotSupported": "Twoja przeglądarka nie jest obsługiwana",
3
+ "useModernBrowser": "Aby strona działała prawidłowo, użyj jednej z nowszych przeglądarek",
4
+ "close": "Zamknąć"
5
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "day1": "dzień",
3
+ "day2": "dni",
4
+ "day5": "dni",
5
+ "hour1": "godzina",
6
+ "hour2": "godziny",
7
+ "hour5": "godzin",
8
+ "minute1": "minuta",
9
+ "minute2": "minuty",
10
+ "minute5": "minut",
11
+ "second1": "sekunda",
12
+ "second2": "sekundy",
13
+ "second5": "sekund",
14
+ "secondshort": "s",
15
+ "minuteshort": "m",
16
+ "hourshort": "g",
17
+ "dayshort": "d"
18
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "setDarkTheme": "Тёмная тема",
3
+ "setLightTheme": "Светлая тема",
4
+ "copyPageLink": "Скопировать ссылку страницы"
5
+ }
@@ -0,0 +1,182 @@
1
+ {
2
+ "weUseCookies": "Мы используем куки для работы сайта и аналитики.",
3
+ "cookies": "куки",
4
+ "accept": "Принять",
5
+ "acceptAll": "Принять все",
6
+ "decline": "Отказаться",
7
+ "declineAll": "Отклонить все",
8
+ "changePrefs": "Настроить предпочтения",
9
+ "setPrefs": "Настроить куки",
10
+ "savePrefs": "Сохранить предпочтения",
11
+ "contentHidden": "Контент скрыт",
12
+ "toUse": "Для использования",
13
+ "toSee": "Для просмотра этого контента необходимо разрешить",
14
+ "actionRequired": "Требуется действие",
15
+ "selectCategories": "Пожалуйста, примите необходимые категории куки в появившемся окне.",
16
+ "needToAccept": "необходимо разрешить",
17
+ "alwaysOn": "(Всегда включено)",
18
+ "necessary": "необходимые",
19
+ "analytics": "аналитические",
20
+ "marketing": "маркетинговые",
21
+ "preferences": "функциональные",
22
+ "functional": "функциональные",
23
+ "usedServices": "Используемые сервисы",
24
+ "usedNotServices": "Сервисы в этой категории сейчас не используются.",
25
+ "necessaryTitle": "Необходимые файлы cookie",
26
+ "necessaryDesc": "Эти файлы строго необходимы для работы сайта (авторизация, безопасность). Их нельзя отключить.",
27
+ "analyticsTitle": "Аналитика и статистика",
28
+ "analyticsDesc": "Помогают нам улучшать сайт, собирая информацию об использовании (посещаемость, популярные страницы).",
29
+ "marketingTitle": "Маркетинг и реклама",
30
+ "marketingDesc": "Используются для отображения персонализированной рекламы и интеграции с соцсетями (YouTube, Pixel).",
31
+ "functionalTitle": "Функциональные",
32
+ "functionalDesc": "Необходимы для работы дополнительных функций, таких как онлайн-чат или интерактивные карты.",
33
+ "cookieNote": "Мы используем файлы cookie для улучшения вашего опыта. Вы можете выбрать, какие категории вы хотите разрешить.",
34
+ "cookiesList": "Список сохраненных браузером файлов",
35
+ "emptyList": "Список пуст.",
36
+ "privacyTitle": "Настройки конфиденциальности",
37
+ "googleTagManager": {
38
+ "name": "Google Tag Manager",
39
+ "description": "Управляет аналитическими, рекламными и функциональными тегами с учетом согласия пользователя."
40
+ },
41
+ "googleAnalytics4": {
42
+ "name": "Google Analytics 4",
43
+ "description": "Измеряет посещаемость, события и поведение пользователей на сайте."
44
+ },
45
+ "googleAds": {
46
+ "name": "Google Ads",
47
+ "description": "Измеряет рекламные конверсии и включает ремаркетинг."
48
+ },
49
+ "metaPixel": {
50
+ "name": "Meta Pixel",
51
+ "description": "Измеряет рекламные конверсии и включает ретаргетинг на платформах Meta."
52
+ },
53
+ "yandexMetrica": {
54
+ "name": "Яндекс Метрика",
55
+ "description": "Измеряет посещаемость, конверсии и поведение пользователей на сайте."
56
+ },
57
+ "vkPixel": {
58
+ "name": "VK Pixel",
59
+ "description": "Измеряет конверсии VK Рекламы и включает аудитории ретаргетинга."
60
+ },
61
+ "myTargetTopMailRu": {
62
+ "name": "myTarget / Top.Mail.Ru",
63
+ "description": "Измеряет рекламные кампании и аудитории ремаркетинга в экосистемах VK и Mail.ru."
64
+ },
65
+ "baiduTongji": {
66
+ "name": "Baidu Tongji",
67
+ "description": "Измеряет посещаемость и поведение пользователей для сайтов, ориентированных на Китай."
68
+ },
69
+ "matomo": {
70
+ "name": "Matomo",
71
+ "description": "Измеряет веб-аналитику через собственный или облачный трекер Matomo."
72
+ },
73
+ "plausible": {
74
+ "name": "Plausible Analytics",
75
+ "description": "Предоставляет приватную веб-аналитику посещаемости сайта."
76
+ },
77
+ "umami": {
78
+ "name": "Umami",
79
+ "description": "Предоставляет приватную веб-аналитику без рекламного трекинга.",
80
+ "legal": {
81
+ "purpose": "Мы используем Umami, чтобы понимать агрегированное использование сайта и улучшать контент и удобство интерфейса.",
82
+ "cookiesDescription": "Umami настроен как cookieless-аналитика и в этой конфигурации не требует аналитических cookies.",
83
+ "notes": "Umami используется как privacy-friendly аналитика. Мы не используем его для рекламы, межсайтового профилирования или ретаргетинга.",
84
+ "data": {
85
+ "pageViews": "Просмотры страниц и посещённые URL",
86
+ "referrer": "Информация об источнике перехода",
87
+ "browser": "Информация о браузере и операционной системе",
88
+ "device": "Тип устройства и приблизительные технические данные"
89
+ }
90
+ }
91
+ },
92
+ "hotjar": {
93
+ "name": "Hotjar",
94
+ "description": "Записывает тепловые карты, воронки, опросы и аналитику поведения."
95
+ },
96
+ "microsoftClarity": {
97
+ "name": "Microsoft Clarity",
98
+ "description": "Измеряет поведение пользователей с помощью тепловых карт и записей сессий."
99
+ },
100
+ "adobeAnalytics": {
101
+ "name": "Adobe Analytics",
102
+ "description": "Измеряет корпоративную аналитику, атрибуцию и эффективность цифрового опыта."
103
+ },
104
+ "hubspot": {
105
+ "name": "HubSpot",
106
+ "description": "Загружает трекинг HubSpot, формы, чат и маркетинговые инструменты."
107
+ },
108
+ "bitrix24": {
109
+ "name": "Битрикс24",
110
+ "description": "Загружает CRM-виджеты Битрикс24, кнопки сайта, формы и чат."
111
+ },
112
+ "amoCrm": {
113
+ "name": "amoCRM",
114
+ "description": "Загружает виджеты amoCRM для чата и захвата лидов."
115
+ },
116
+ "salesforcePardot": {
117
+ "name": "Salesforce Account Engagement",
118
+ "description": "Измеряет маркетинговую автоматизацию и атрибуцию лидов."
119
+ },
120
+ "mailchimp": {
121
+ "name": "Mailchimp",
122
+ "description": "Загружает формы, попапы и маркетинговые виджеты Mailchimp."
123
+ },
124
+ "linkedInInsight": {
125
+ "name": "LinkedIn Insight Tag",
126
+ "description": "Измеряет эффективность рекламы LinkedIn и аудитории ретаргетинга."
127
+ },
128
+ "tiktokPixel": {
129
+ "name": "TikTok Pixel",
130
+ "description": "Измеряет конверсии рекламы TikTok и включает ретаргетинг."
131
+ },
132
+ "pinterestTag": {
133
+ "name": "Pinterest Tag",
134
+ "description": "Измеряет конверсии рекламы Pinterest и ретаргетинг."
135
+ },
136
+ "xPixel": {
137
+ "name": "X Pixel",
138
+ "description": "Измеряет рекламные конверсии и ретаргетинг X."
139
+ },
140
+ "intercom": {
141
+ "name": "Intercom",
142
+ "description": "Предоставляет чат, поддержку и сообщения для клиентов."
143
+ },
144
+ "zendeskChat": {
145
+ "name": "Zendesk Chat",
146
+ "description": "Предоставляет чат и инструменты клиентской поддержки."
147
+ },
148
+ "crispChat": {
149
+ "name": "Crisp Chat",
150
+ "description": "Предоставляет чат и инструменты клиентской поддержки."
151
+ },
152
+ "tawkTo": {
153
+ "name": "Tawk.to",
154
+ "description": "Предоставляет онлайн-чат, мониторинг посетителей и поддержку."
155
+ },
156
+ "jivoSite": {
157
+ "name": "JivoSite",
158
+ "description": "Предоставляет онлайн-чат, обратные звонки и инструменты коммуникации с клиентами."
159
+ },
160
+ "liveChat": {
161
+ "name": "LiveChat",
162
+ "description": "Предоставляет онлайн-чат и клиентскую поддержку."
163
+ },
164
+ "tidio": {
165
+ "name": "Tidio",
166
+ "description": "Предоставляет онлайн-чат, чат-бота и сообщения для клиентов."
167
+ },
168
+ "chatwoot": {
169
+ "name": "Chatwoot",
170
+ "description": "Предоставляет онлайн-чат, омниканальные inbox-разделы поддержки и сообщения для клиентов."
171
+ },
172
+ "chatra": {
173
+ "name": "Chatra",
174
+ "description": "Предоставляет онлайн-чат и сообщения для клиентов."
175
+ },
176
+ "recaptcha": {
177
+ "name": "Google reCAPTCHA",
178
+ "description": "Защищает формы от спама и злоупотреблений."
179
+ },
180
+ "cookielessTitle": "Privacy-friendly analytics",
181
+ "cookielessDesc": "We use the following GDPR-compatible cookieless analytics tools for aggregated statistics. They are not used for advertising, retargeting, or cross-site profiling."
182
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Ошибка",
3
+ "tryAgain": "Попробовать снова"
4
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "expand": "Раскрыть",
3
+ "collapse": "Свернуть",
4
+ "close": "Закрыть",
5
+ "readMore": "Читать далее",
6
+ "goMore": "Далее",
7
+ "goHome": "На главную",
8
+ "goHomeFull": "Вернуться на главную",
9
+ "pageNotFound": "Упс! Страница не найдена.",
10
+ "pageNotFoundText": "Страница, которую вы ищете, возможно, была удалена или никогда не существовала.",
11
+ "pageNotFoundDescription": "Страница не найдена. Проверьте правильность адреса.",
12
+ "serverError": "Ошибка сервера.",
13
+ "somethingWentWrong": "На сервере произошла непредвиденная ошибка. Мы уже работаем над исправлением этой проблемы. Пожалуйста, попробуйте еще раз позже.",
14
+ "updatePage": "Обновить страницу",
15
+ "noGoogleMapAddress": "Не указан адрес или Place ID",
16
+ "nodata": "Нет данных",
17
+ "siteSearch": {
18
+ "ariaLabel": "Поиск по сайту",
19
+ "placeholder": "Поиск по содержимому страниц…",
20
+ "clear": "Очистить поиск",
21
+ "indexing": "Подготавливаем поиск по сайту…",
22
+ "indexingProgress": "Подготавливаем поиск: {indexed} из {total} страниц",
23
+ "indexed": "Проиндексировано страниц: {count}",
24
+ "remote": "Поиск на сервере",
25
+ "searching": "Ищем…",
26
+ "operatorLabel": "Объединение поисковых слов через AND или OR",
27
+ "results": "Результаты поиска",
28
+ "noResults": "Ничего не найдено",
29
+ "typeMore": "Введите не менее {count} символов",
30
+ "prefilled": "Запрос подготовлен. Нажмите Enter или измените его, чтобы начать поиск.",
31
+ "error": "Не удалось подготовить поиск. Попробуйте ещё раз."
32
+ }
33
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "welcome": "Добро пожаловать в",
3
+ "experiment": "Экспериментируйте, настраивайте и наслаждайтесь скоростью и производительностью!",
4
+ "poweredBy": "Разработано",
5
+ "templateVersion": "Версия шаблона",
6
+ "nuxtVersion": "Версия Nuxt",
7
+ "toggleTheme": "Переключить тему"
8
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "browserNotSupported": "Ваш браузер не поддерживается",
3
+ "useModernBrowser": "Для корректной работы сайта используйте один из современных браузеров",
4
+ "close": "Закрыть"
5
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "day1": "день",
3
+ "day2": "дня",
4
+ "day5": "дней",
5
+ "hour1": "час",
6
+ "hour2": "часа",
7
+ "hour5": "часов",
8
+ "minute1": "минута",
9
+ "minute2": "минуты",
10
+ "minute5": "минут",
11
+ "second1": "секунда",
12
+ "second2": "секунды",
13
+ "second5": "секунд",
14
+ "secondshort": "с",
15
+ "minuteshort": "м",
16
+ "hourshort": "ч",
17
+ "dayshort": "д"
18
+ }
@@ -0,0 +1,28 @@
1
+ import { useMyelophoneConfig } from '~/utils/myelophoneConfig';
2
+
3
+ export default defineNuxtRouteMiddleware(async (to) => {
4
+ const config = useMyelophoneConfig();
5
+
6
+ type LayoutName = Parameters<typeof setPageLayout>[0];
7
+
8
+ const layout = (config.blog?.postsLayout || 'default') as LayoutName;
9
+
10
+ setPageLayout(layout);
11
+
12
+ if (!import.meta.server) {
13
+ return;
14
+ }
15
+
16
+ const param = 'slug' in to.params ? to.params.slug : undefined;
17
+ const slug = Array.isArray(param) ? param.join('/') : (param ?? '');
18
+ const { getPost } = await import('#myelophone/app/utils/blog.server');
19
+ const post = await getPost(String(slug));
20
+
21
+ if (!post) {
22
+ const event = useRequestEvent();
23
+
24
+ if (event) {
25
+ setResponseStatus(event, 404, 'Not Found');
26
+ }
27
+ }
28
+ });
@@ -0,0 +1,17 @@
1
+ import { useMyelophoneConfig } from '~/utils/myelophoneConfig';
2
+
3
+ export default defineNuxtRouteMiddleware((to) => {
4
+ const config = useMyelophoneConfig().multi18n;
5
+
6
+ const { defaultLocale, locales } = config;
7
+ const segments = to.path.split('/').filter(Boolean);
8
+
9
+ const firstSegment = segments[0] ?? '';
10
+
11
+ if (locales.includes(firstSegment)) {
12
+ if (firstSegment === defaultLocale) {
13
+ const cleanPath = `/${segments.slice(1).join('/')}`;
14
+ return navigateTo(cleanPath || '/', { redirectCode: 301 });
15
+ }
16
+ }
17
+ });
@@ -0,0 +1,11 @@
1
+ export default defineNuxtRouteMiddleware(() => {
2
+ if (!import.meta.server) {
3
+ return;
4
+ }
5
+
6
+ const event = useRequestEvent();
7
+
8
+ if (event) {
9
+ setResponseStatus(event, 404, 'Not Found');
10
+ }
11
+ });
@@ -0,0 +1,39 @@
1
+ import type { NuxtPage } from '@nuxt/schema';
2
+ import { defineNuxtModule, extendPages } from '@nuxt/kit';
3
+
4
+ export default defineNuxtModule({
5
+ meta: {
6
+ name: 'default-blog',
7
+ },
8
+
9
+ setup(_options, nuxt) {
10
+ extendPages((pages) => {
11
+ const blogEnabled =
12
+ nuxt.options.myelophone?.blog?.blogEnabled ?? true;
13
+
14
+ processPages(pages, blogEnabled);
15
+ });
16
+ },
17
+ });
18
+
19
+ function processPages(pages: NuxtPage[], blogEnabled: boolean) {
20
+ for (let i = pages.length - 1; i >= 0; i--) {
21
+ const page = pages[i]!;
22
+
23
+ if (page.file?.endsWith('/post/[...slug].vue')) {
24
+ if (!blogEnabled) {
25
+ pages.splice(i, 1);
26
+ continue;
27
+ }
28
+
29
+ page.meta ||= {};
30
+ page.meta.middleware = ['blog-layout'];
31
+
32
+ continue;
33
+ }
34
+
35
+ if (page.children?.length) {
36
+ processPages(page.children, blogEnabled);
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,80 @@
1
+ import { readdir } from 'node:fs/promises';
2
+ import type { Dirent } from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { join, relative, sep } from 'node:path';
5
+
6
+ import type { Nuxt } from '@nuxt/schema';
7
+
8
+ const defaultContentDir = fileURLToPath(
9
+ new URL('../../content', import.meta.url),
10
+ );
11
+ const postUrlsCache = new WeakMap<Nuxt, Promise<string[]>>();
12
+
13
+ async function collectMarkdownFiles(dir: string): Promise<string[]> {
14
+ let entries: Dirent[];
15
+
16
+ try {
17
+ entries = await readdir(dir, {
18
+ withFileTypes: true,
19
+ });
20
+ } catch {
21
+ return [];
22
+ }
23
+
24
+ const files = await Promise.all(
25
+ entries.map(async (entry) => {
26
+ const fullPath = join(dir, entry.name);
27
+
28
+ if (entry.isDirectory()) {
29
+ return collectMarkdownFiles(fullPath);
30
+ }
31
+
32
+ if (entry.isFile() && entry.name.endsWith('.md')) {
33
+ return [fullPath];
34
+ }
35
+
36
+ return [];
37
+ }),
38
+ );
39
+
40
+ return files.flat();
41
+ }
42
+
43
+ async function addContentDir(urls: Set<string>, contentDir: string) {
44
+ const files = await collectMarkdownFiles(contentDir);
45
+
46
+ for (const file of files) {
47
+ const slug = relative(contentDir, file)
48
+ .split(sep)
49
+ .map(encodeURIComponent)
50
+ .join('/')
51
+ .replace(/\.md$/, '');
52
+
53
+ urls.add(`/post/${slug}`);
54
+ }
55
+ }
56
+
57
+ export async function getBlogPostUrls(nuxt: Nuxt): Promise<string[]> {
58
+ const cached = postUrlsCache.get(nuxt);
59
+
60
+ if (cached) {
61
+ return cached;
62
+ }
63
+
64
+ const promise = collectBlogPostUrls(nuxt);
65
+ postUrlsCache.set(nuxt, promise);
66
+
67
+ return promise;
68
+ }
69
+
70
+ async function collectBlogPostUrls(nuxt: Nuxt): Promise<string[]> {
71
+ const urls = new Set<string>();
72
+ const contentDirs = new Set([
73
+ defaultContentDir,
74
+ join(nuxt.options.rootDir, 'content'),
75
+ ]);
76
+
77
+ await Promise.all([...contentDirs].map((dir) => addContentDir(urls, dir)));
78
+
79
+ return [...urls].sort();
80
+ }
@@ -0,0 +1,116 @@
1
+ import type { NuxtPage } from '@nuxt/schema';
2
+
3
+ import {
4
+ addPrerenderRoutes,
5
+ addServerHandler,
6
+ addServerTemplate,
7
+ createResolver,
8
+ defineNuxtModule,
9
+ } from '@nuxt/kit';
10
+
11
+ import { getBlogPostUrls } from '../default-blog/posts';
12
+
13
+ export default defineNuxtModule({
14
+ meta: {
15
+ name: 'default-sitemap',
16
+ },
17
+
18
+ async setup(_options, nuxt) {
19
+ const { resolve } = createResolver(import.meta.url);
20
+
21
+ const pageUrls = new Set<string>();
22
+ const postUrls = new Set<string>();
23
+
24
+ const blogEnabled = nuxt.options.myelophone?.blog?.blogEnabled ?? true;
25
+
26
+ if (blogEnabled) {
27
+ const urls = await getBlogPostUrls(nuxt);
28
+
29
+ for (const url of urls) {
30
+ postUrls.add(url);
31
+ }
32
+
33
+ addPrerenderRoutes(urls);
34
+ }
35
+
36
+ nuxt.hook('pages:resolved', (pages) => {
37
+ collectStaticPages(pages, pageUrls);
38
+ });
39
+
40
+ addServerTemplate({
41
+ filename: '#default-sitemap/urls.mjs',
42
+
43
+ getContents() {
44
+ const finalUrls = new Set<string>();
45
+
46
+ for (const url of pageUrls) {
47
+ finalUrls.add(url);
48
+ }
49
+
50
+ for (const url of postUrls) {
51
+ finalUrls.add(url);
52
+ }
53
+
54
+ const urls = [...finalUrls].sort();
55
+
56
+ return `export default ${JSON.stringify(urls)};`;
57
+ },
58
+ });
59
+
60
+ addServerHandler({
61
+ route: '/sitemap.xml',
62
+ method: 'get',
63
+ handler: resolve('./runtime/sitemap.get'),
64
+ });
65
+
66
+ addPrerenderRoutes('/sitemap.xml');
67
+ },
68
+ });
69
+
70
+ function collectStaticPages(
71
+ pages: NuxtPage[],
72
+ urls: Set<string>,
73
+ parentPath = '',
74
+ ) {
75
+ for (const page of pages) {
76
+ const path = resolvePagePath(parentPath, page.path);
77
+
78
+ if (isStaticPage(path)) {
79
+ urls.add(path);
80
+ }
81
+
82
+ if (page.children?.length) {
83
+ collectStaticPages(page.children, urls, path);
84
+ }
85
+ }
86
+ }
87
+
88
+ function isStaticPage(path: string): boolean {
89
+ if (!path) {
90
+ return false;
91
+ }
92
+
93
+ if (path.includes(':') || path.includes('*')) {
94
+ return false;
95
+ }
96
+
97
+ return true;
98
+ }
99
+
100
+ function resolvePagePath(parent: string, child: string): string {
101
+ if (child.startsWith('/')) {
102
+ return normalizePath(child);
103
+ }
104
+
105
+ if (!parent) {
106
+ return normalizePath(`/${child}`);
107
+ }
108
+
109
+ return normalizePath(`${parent}/${child}`);
110
+ }
111
+
112
+ function normalizePath(path: string): string {
113
+ const normalized = path.replace(/\/+/g, '/').replace(/\/$/, '');
114
+
115
+ return normalized || '/';
116
+ }