itube-specs 0.0.1027 → 0.0.1029

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 (32) hide show
  1. package/SITE_UPDATES.md +7 -0
  2. package/assets/scss/themes/dark/_theme.scss +24 -2
  3. package/assets/scss/themes/light/_theme.scss +24 -2
  4. package/components/filter/main.vue +2 -2
  5. package/components/info/model.vue +16 -2
  6. package/components/layout/header.vue +0 -3
  7. package/components/layout/second-nav.vue +80 -0
  8. package/i18n/locales/de-DE.json +3 -1
  9. package/i18n/locales/en-US.json +2 -0
  10. package/i18n/locales/es-ES.json +3 -1
  11. package/i18n/locales/fr-FR.json +3 -1
  12. package/i18n/locales/it-IT.json +3 -1
  13. package/i18n/locales/ja-JP.json +3 -1
  14. package/i18n/locales/ko-KR.json +3 -1
  15. package/i18n/locales/ru-RU.json +3 -1
  16. package/i18n/locales/zh-CN.json +3 -1
  17. package/layouts/default.vue +2 -0
  18. package/package.json +1 -1
  19. package/public/img/model-placeholder/gay-light-portrait.avif +0 -0
  20. package/public/img/model-placeholder/gay-light-portrait.webp +0 -0
  21. package/public/img/model-placeholder/gay-light-square.avif +0 -0
  22. package/public/img/model-placeholder/gay-light-square.webp +0 -0
  23. package/public/img/model-placeholder/shemale-light-portrait.avif +0 -0
  24. package/public/img/model-placeholder/shemale-light-portrait.webp +0 -0
  25. package/public/img/model-placeholder/shemale-light-square.avif +0 -0
  26. package/public/img/model-placeholder/shemale-light-square.webp +0 -0
  27. package/public/img/model-placeholder/straight-light-portrait.avif +0 -0
  28. package/public/img/model-placeholder/straight-light-portrait.webp +0 -0
  29. package/public/img/model-placeholder/straight-light-square.avif +0 -0
  30. package/public/img/model-placeholder/straight-light-square.webp +0 -0
  31. package/types/index.d.ts +1 -0
  32. package/types/second-nav-item.d.ts +14 -0
package/SITE_UPDATES.md CHANGED
@@ -11,6 +11,13 @@
11
11
  коммиты до бампа); `npm run patch` сам проставит ей номер версии. Формат пункта: что сделать и где,
12
12
  кому именно (всем / сайтам с N), что будет, если не сделать.
13
13
 
14
+ ## 0.0.1029 — 2026-10-13
15
+
16
+ - **Сайтам с рекламными ссылками над контентом на мобиле (videosection-redesign):** новая вторая навигация —
17
+ `LayoutSecondNav` в `layouts/default.vue` (до 1024px, на всех страницах основного лэйаута). Пункты задаёт сайт в
18
+ `appConfig.secondNav`: `{ title, link, icon?, dot?, featureFlag? }`, `link` — строка или адреса по нишам. Пример —
19
+ `config/second-nav.config.ts` videosection-redesign. Остальным сайтам ничего не делать: без `secondNav` блока нет.
20
+
14
21
  ## 0.0.1023 — 2026-10-08
15
22
 
16
23
  - **Всем сайтам:** в `package.json` заменить скрипт `spec` на `"spec": "sh node_modules/itube-specs/scripts/spec.sh"`
@@ -450,8 +450,6 @@ $header-language-trigger: $color-zinc-400;
450
450
  $header-language-trigger--hover: $color-white;
451
451
  $header-language-trigger-bg: transparent;
452
452
  $header-language-trigger-bg--hover: transparent;
453
- $header-language-badge-bg: $color-zinc-800;
454
- $header-language-badge-text: $color-zinc-200;
455
453
  $header-language-menu-bg: $color-night;
456
454
  $header-language-menu-border: $color-zinc-800;
457
455
  $header-language-menu-shadow: 0 20px 25px -5px #{$mono-black-25}, 0 8px 10px -6px #{$mono-black-25};
@@ -1010,11 +1008,35 @@ $auth-register-link: $color-indigo-400;
1010
1008
  $auth-register-link--hover: $color-indigo-300;
1011
1009
  $auth-register-login: $color-zinc-500;
1012
1010
 
1011
+ // ─── Layout: second nav (рекламные ссылки над контентом на мобиле) ───
1012
+ $layout-second-nav-link-bg: $color-indigo-600;
1013
+ $layout-second-nav-link-text: $color-white;
1014
+ $layout-second-nav-dot-bg: $color-green-400;
1015
+
1013
1016
  // ─── Native card ───
1014
1017
  $native-card-bg: $color-black;
1015
1018
  $native-card-primary: $color-zinc-400;
1016
1019
  $native-card-secondary: $color-zinc-500;
1017
1020
 
1021
+ // ─── Adv: плашка «Реклама» поверх креатива ───
1022
+ $adv-label-text: $color-white;
1023
+ $adv-label-bg: $color-black-50;
1024
+
1025
+ // ─── Adv: партнёрская карусель FapHouse ───
1026
+ $faphouse-item-text: $video-card-text;
1027
+ $faphouse-item-text--hover: $video-card-text--hover;
1028
+ $faphouse-poster-bg: $native-card-bg;
1029
+ $faphouse-ad-text: $color-white;
1030
+ $faphouse-ad-bg: $color-indigo-600;
1031
+ $faphouse-duration-text: $color-white;
1032
+ $faphouse-duration-bg: $color-black-75;
1033
+ $faphouse-promo-bg-from: $color-amber-400;
1034
+ $faphouse-promo-bg-to: $color-amber-500;
1035
+ $faphouse-promo-text: $color-black;
1036
+ $faphouse-promo-shadow--hover: 0 10px 15px -3px #{$color-black-25};
1037
+ $faphouse-promo-action-text: $color-white;
1038
+ $faphouse-promo-action-bg: $color-red-700;
1039
+
1018
1040
  // ─── Page: grouped categories ───
1019
1041
  $grouped-highlight-border: $color-zinc-800;
1020
1042
  $grouped-category-icon: $color-zinc-500;
@@ -506,8 +506,6 @@ $header-language-trigger: $color-white;
506
506
  $header-language-trigger--hover: $color-white;
507
507
  $header-language-trigger-bg: $color-slate-750;
508
508
  $header-language-trigger-bg--hover: $color-slate-700;
509
- $header-language-badge-bg: $color-slate-850;
510
- $header-language-badge-text: $color-white;
511
509
  $header-language-menu-bg: $color-slate-850;
512
510
  $header-language-menu-border: $color-white-10;
513
511
  $header-language-menu-shadow: 0 20px 25px -5px #{$mono-black-25}, 0 8px 10px -6px #{$mono-black-25};
@@ -1037,11 +1035,35 @@ $auth-register-link: $color-indigo-soft;
1037
1035
  $auth-register-link--hover: $color-indigo-blue;
1038
1036
  $auth-register-login: $color-gray-400;
1039
1037
 
1038
+ // ─── Layout: second nav (рекламные ссылки над контентом на мобиле) ───
1039
+ $layout-second-nav-link-bg: $color-indigo-600;
1040
+ $layout-second-nav-link-text: $color-white;
1041
+ $layout-second-nav-dot-bg: $color-green-400;
1042
+
1040
1043
  // ─── Native card ───
1041
1044
  $native-card-bg: $color-slate-800;
1042
1045
  $native-card-primary: $color-gray-400;
1043
1046
  $native-card-secondary: $color-gray-500;
1044
1047
 
1048
+ // ─── Adv: плашка «Реклама» поверх креатива ───
1049
+ $adv-label-text: $color-white;
1050
+ $adv-label-bg: $color-black-50;
1051
+
1052
+ // ─── Adv: партнёрская карусель FapHouse ───
1053
+ $faphouse-item-text: $video-card-text;
1054
+ $faphouse-item-text--hover: $color-indigo-600;
1055
+ $faphouse-poster-bg: $color-gray-200;
1056
+ $faphouse-ad-text: $color-white;
1057
+ $faphouse-ad-bg: $color-indigo-600;
1058
+ $faphouse-duration-text: $color-white;
1059
+ $faphouse-duration-bg: $color-black-75;
1060
+ $faphouse-promo-bg-from: $color-amber-400;
1061
+ $faphouse-promo-bg-to: $color-amber-500;
1062
+ $faphouse-promo-text: $color-black;
1063
+ $faphouse-promo-shadow--hover: 0 10px 15px -3px #{$color-black-15};
1064
+ $faphouse-promo-action-text: $color-white;
1065
+ $faphouse-promo-action-bg: $color-red-700;
1066
+
1045
1067
  // ─── Page: grouped categories ───
1046
1068
  $grouped-highlight-border: transparent;
1047
1069
  $grouped-category-icon: $color-gray-500;
@@ -9,7 +9,7 @@
9
9
  />
10
10
 
11
11
  <transition>
12
- <LazyFilterPopup
12
+ <FilterPopup
13
13
  v-if="filterOpen"
14
14
  v-model="filterOpen"
15
15
  :title="$t('filter')"
@@ -37,7 +37,7 @@
37
37
  </UiBtn>
38
38
  </div>
39
39
  </template>
40
- </LazyFilterPopup>
40
+ </FilterPopup>
41
41
  </transition>
42
42
  </div>
43
43
  </template>
@@ -17,7 +17,7 @@
17
17
  v-if="akaNames.length"
18
18
  class="info-model__also"
19
19
  >
20
- <span class="info-model__also-title">{{ t('also_known_as') }}</span>
20
+ <span class="info-model__also-title">{{ akaTitle }}</span>
21
21
  <template
22
22
  v-for="name in akaNames"
23
23
  :key="name"
@@ -206,6 +206,15 @@ const currentAvifSrc = computed(() => (isFallback.value ? placeholder.value.avif
206
206
 
207
207
  const akaNames = computed(() => props.card.akaNames.split(', ').filter(Boolean));
208
208
 
209
+ const akaTitle = computed(() => {
210
+ const gender = (props.card.gender || '').toLowerCase();
211
+
212
+ if (gender === 'male' || /trans ?man/.test(gender)) return t('also_known_as_male');
213
+ if (gender === 'female' || /trans|shemale/.test(gender)) return t('also_known_as_female');
214
+
215
+ return t('also_known_as');
216
+ });
217
+
209
218
  const socialItems = computed<ILinkItem[]>(() => (
210
219
  Object.entries(props.card.social || {}).map(([key, value]) => ({
211
220
  name: value || '',
@@ -342,8 +351,13 @@ const shareModelCard = computed<IModelCard>(() => ({
342
351
  }
343
352
 
344
353
  .info-model__also-name {
345
- color: $info-model-also;
354
+ display: inline-block;
355
+ max-width: 100%;
356
+ overflow: hidden;
357
+ text-overflow: ellipsis;
346
358
  white-space: nowrap;
359
+ vertical-align: top;
360
+ color: $info-model-also;
347
361
  }
348
362
 
349
363
  .info-model__also-name:not(:last-of-type)::after {
@@ -287,9 +287,6 @@ function closeLanguage() {
287
287
 
288
288
  .layout-header__language-badge {
289
289
  @include locale-badge;
290
-
291
- background-color: $header-language-badge-bg;
292
- color: $header-language-badge-text;
293
290
  }
294
291
 
295
292
  .layout-header__language-angle {
@@ -0,0 +1,80 @@
1
+ <!-- Вторая навигация: ряд рекламных ссылок над контентом, только на мобиле (до бургера), на всех страницах
2
+ основного лэйаута. Пункты — `appConfig.secondNav` сайта; пусто — блока нет. -->
3
+ <template>
4
+ <nav
5
+ v-if="items.length"
6
+ class="layout-second-nav"
7
+ >
8
+ <a
9
+ v-for="item in items"
10
+ :key="item.title"
11
+ :href="item.href"
12
+ :class="{'--dot': item.dot}"
13
+ class="layout-second-nav__link"
14
+ target="_blank"
15
+ rel="nofollow noopener sponsored"
16
+ >
17
+ {{ t(`navigation.${item.title}`) }}
18
+ <UiIcon
19
+ v-if="item.icon"
20
+ :name="item.icon"
21
+ size="14"
22
+ />
23
+ </a>
24
+ </nav>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import type { ISecondNavItem } from '../../types';
29
+
30
+ const { t } = useI18n();
31
+ const { featureFlags } = useRuntimeConfig().public;
32
+ const secondNav = (useAppConfig().secondNav || []) as ISecondNavItem[];
33
+ const nicheState = useState<string>('niche');
34
+ const defaultNiche = NicheHelper.getSiteDefaultNiche();
35
+
36
+ const items = computed(() => {
37
+ const niche = nicheState.value || defaultNiche;
38
+
39
+ return secondNav
40
+ .filter(item => !item.featureFlag || featureFlags[item.featureFlag])
41
+ .map(item => ({ ...item, href: typeof item.link === 'string' ? item.link : item.link[niche] }))
42
+ .filter(item => !!item.href);
43
+ });
44
+ </script>
45
+
46
+ <style lang="scss">
47
+ .layout-second-nav {
48
+ display: flex;
49
+ justify-content: center;
50
+ gap: 12px;
51
+ padding: 8px var(--container-padding-inline);
52
+ }
53
+
54
+ .layout-second-nav__link {
55
+ @include font-sm;
56
+
57
+ position: relative;
58
+ display: inline-flex;
59
+ align-items: center;
60
+ gap: 4px;
61
+ padding: 4px 12px;
62
+ font-weight: 600;
63
+ white-space: nowrap;
64
+ color: $layout-second-nav-link-text;
65
+ background-color: $layout-second-nav-link-bg;
66
+ border-radius: $radius-md;
67
+ }
68
+
69
+ .layout-second-nav__link.--dot::after {
70
+ content: '';
71
+ position: absolute;
72
+ top: 0;
73
+ right: 0;
74
+ width: 10px;
75
+ height: 10px;
76
+ background-color: $layout-second-nav-dot-bg;
77
+ border-radius: $radius-full;
78
+ transform: translate(40%, -40%);
79
+ }
80
+ </style>
@@ -510,5 +510,7 @@
510
510
  "last_updated": "Zuletzt aktualisiert",
511
511
  "longest": "Längste",
512
512
  "longest_short": "Längste"
513
- }
513
+ },
514
+ "also_known_as_male": "Auch bekannt als:",
515
+ "also_known_as_female": "Auch bekannt als:"
514
516
  }
@@ -1,4 +1,6 @@
1
1
  {
2
+ "also_known_as_male": "Also known as:",
3
+ "also_known_as_female": "Also known as:",
2
4
  "model_about": "About",
3
5
  "top_models": "Top models",
4
6
  "playlists_empty": {
@@ -510,5 +510,7 @@
510
510
  "last_updated": "Última actualización",
511
511
  "longest": "Más largo",
512
512
  "longest_short": "Largos"
513
- }
513
+ },
514
+ "also_known_as_male": "También conocido como:",
515
+ "also_known_as_female": "También conocida como:"
514
516
  }
@@ -510,5 +510,7 @@
510
510
  "last_updated": "Dernière mise à jour",
511
511
  "longest": "Les plus longues",
512
512
  "longest_short": "Longues"
513
- }
513
+ },
514
+ "also_known_as_male": "Également connu sous le nom de :",
515
+ "also_known_as_female": "Également connue sous le nom de :"
514
516
  }
@@ -510,5 +510,7 @@
510
510
  "last_updated": "Ultimo aggiornamento",
511
511
  "longest": "Più lungo",
512
512
  "longest_short": "Lunghi"
513
- }
513
+ },
514
+ "also_known_as_male": "Conosciuto anche come:",
515
+ "also_known_as_female": "Conosciuta anche come:"
514
516
  }
@@ -510,5 +510,7 @@
510
510
  "last_updated": "最終更新",
511
511
  "longest": "最長",
512
512
  "longest_short": "最長"
513
- }
513
+ },
514
+ "also_known_as_male": "別名:",
515
+ "also_known_as_female": "別名:"
514
516
  }
@@ -510,5 +510,7 @@
510
510
  "last_updated": "마지막 업데이트",
511
511
  "longest": "가장 긴",
512
512
  "longest_short": "가장 긴"
513
- }
513
+ },
514
+ "also_known_as_male": "다른 이름:",
515
+ "also_known_as_female": "다른 이름:"
514
516
  }
@@ -510,5 +510,7 @@
510
510
  "last_updated": "Последние",
511
511
  "longest": "Самые длинные",
512
512
  "longest_short": "Длинные"
513
- }
513
+ },
514
+ "also_known_as_male": "Также известен как:",
515
+ "also_known_as_female": "Также известна как:"
514
516
  }
@@ -510,5 +510,7 @@
510
510
  "last_updated": "最后更新",
511
511
  "longest": "最长",
512
512
  "longest_short": "最长"
513
- }
513
+ },
514
+ "also_known_as_male": "又名:",
515
+ "also_known_as_female": "又名:"
514
516
  }
@@ -10,6 +10,8 @@
10
10
  hydrate-on-idle
11
11
  />
12
12
 
13
+ <LayoutSecondNav class="_to-sd"/>
14
+
13
15
  <main
14
16
  id="main"
15
17
  class="main"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "itube-specs",
3
3
  "type": "module",
4
- "version": "0.0.1027",
4
+ "version": "0.0.1029",
5
5
  "main": "./nuxt.config.ts",
6
6
  "types": "./types/index.d.ts",
7
7
  "scripts": {
package/types/index.d.ts CHANGED
@@ -47,6 +47,7 @@ export * from './model-filter.d.ts';
47
47
  export * from './model-filter-payload.d.ts';
48
48
  export * from './multi-suggest.d.ts';
49
49
  export * from './navigation-items.d.ts';
50
+ export * from './second-nav-item.d.ts';
50
51
  export * from './link-item.d.ts';
51
52
  export * from './fluid-player.d.ts';
52
53
  export * from './chips-item.d.ts';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Пункт второй навигации — ряд рекламных ссылок над контентом на мобиле (`appConfig.secondNav`).
3
+ * `link` — один адрес или адреса по нишам (`{ straight, gay, shemale }`); нет адреса для ниши — пункт
4
+ * не показывается. `title` — ключ `navigation.<title>`.
5
+ */
6
+ export interface ISecondNavItem {
7
+ title: string
8
+ link: string | Record<string, string>
9
+ icon?: string
10
+ /** Зелёная точка «онлайн» в углу пункта. */
11
+ dot?: boolean
12
+ /** Имя фича-флага сайта: флаг выключен — пункта нет. */
13
+ featureFlag?: string
14
+ }