@ampernic/vitepress-theme-alt-docs 0.1.6 → 0.1.11
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/README.md +42 -8
- package/dist/components/ADGlobalSearch.vue +228 -33
- package/dist/components/ADProducts.vue +19 -2
- package/dist/config/shared.d.ts +13 -0
- package/dist/config/shared.js +3 -1
- package/dist/config/shared.mjs +3 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -21,16 +21,37 @@ export default Theme
|
|
|
21
21
|
### Config helpers
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
|
-
// .vitepress/config.
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
24
|
+
// .vitepress/config/index.mts
|
|
25
|
+
import { defineConfigWithTheme } from 'vitepress'
|
|
26
|
+
import { createSharedConfig } from '@ampernic/vitepress-theme-alt-docs/config'
|
|
27
|
+
import { ru } from './ru'
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const shared = createSharedConfig({
|
|
30
|
+
distroName: 'alt-domain',
|
|
31
|
+
allDistros: ['alt-domain', 'alt-workstation', 'alt-virtualization-pve', 'group-policy'],
|
|
32
|
+
// Группировка дистрибутивов в подменю (необязательно):
|
|
33
|
+
sections: [
|
|
34
|
+
{ name: 'Альт Виртуализация', distros: ['alt-virtualization-pve'] },
|
|
35
|
+
{ name: 'Групповые политики', distros: ['group-policy'] },
|
|
36
|
+
],
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
export default defineConfigWithTheme({
|
|
40
|
+
...shared,
|
|
41
|
+
locales: { root: { label: 'Русский', ...ru } },
|
|
31
42
|
})
|
|
32
43
|
```
|
|
33
44
|
|
|
45
|
+
#### Опции `createSharedConfig`
|
|
46
|
+
|
|
47
|
+
| Опция | Тип | Описание |
|
|
48
|
+
|-------|-----|----------|
|
|
49
|
+
| `distroName` | `string` | Slug текущего дистрибутива |
|
|
50
|
+
| `allDistros` | `string[]` | Полный список дистрибутивов для переключателя |
|
|
51
|
+
| `sections` | `SectionInfo[]` | Именованные группы дистрибутивов в переключателе продуктов |
|
|
52
|
+
| `hostname` | `string` | Hostname для sitemap |
|
|
53
|
+
| `editLinkRepo` | `string` | URL репозитория для «Предложить правки» |
|
|
54
|
+
|
|
34
55
|
## Компоненты
|
|
35
56
|
|
|
36
57
|
| Компонент | Описание |
|
|
@@ -39,11 +60,24 @@ export default defineConfig({
|
|
|
39
60
|
| `ADProducts` | Переключатель продуктов/дистрибутивов |
|
|
40
61
|
| `ADProductsSidebar` | Боковая панель с выбором продукта |
|
|
41
62
|
| `ADVersioning` | Переключатель версий документации |
|
|
42
|
-
| `ADNavBarSearch` |
|
|
43
|
-
| `
|
|
63
|
+
| `ADNavBarSearch` | Кнопка открытия глобального поиска в навбаре |
|
|
64
|
+
| `ADGlobalSearch` | Глобальный поиск по всем дистрибутивам через Pagefind `mergeIndex` |
|
|
65
|
+
| `ADSearch` | Компонент поиска для одного сайта (Pagefind) |
|
|
44
66
|
| `ADAssetLink` | Ссылка на файл-ассет |
|
|
45
67
|
| `ADInlineImage` | Встроенное изображение с нормализацией пути |
|
|
46
68
|
|
|
69
|
+
### ADGlobalSearch
|
|
70
|
+
|
|
71
|
+
Поисковая модалка для индексного сайта, объединяющая индексы всех задеплоенных дистрибутивов.
|
|
72
|
+
|
|
73
|
+
**Как работает:**
|
|
74
|
+
1. Последовательно пробует `import(/{distro}/pagefind/pagefind.js)` для каждого дистрибутива из списка
|
|
75
|
+
2. HEAD-проверяет `pagefind-entry.json` для остальных дистрибутивов — только подтверждённые идут в `mergeIndex`
|
|
76
|
+
> Важно: вызов `mergeIndex()` для несуществующего пути необратимо ломает SharedWorker Pagefind, все последующие запросы тоже падают
|
|
77
|
+
3. Загружает `pagefind/distro-meta.json` с каждого сайта — получает точный список версий по дистрибутиву
|
|
78
|
+
> `pagefind.filters()` при объединённых индексах возвращает значения со всех сайтов без изоляции, поэтому для dropdown версий используется manifest, а не API Pagefind
|
|
79
|
+
4. Отображает фильтры по дистрибутиву и версии; при смене дистрибутива версии обновляются из manifest
|
|
80
|
+
|
|
47
81
|
## Включённые плагины
|
|
48
82
|
|
|
49
83
|
Тема автоматически подключает:
|
|
@@ -1,7 +1,36 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { onKeyStroke, useScrollLock } from '@vueuse/core'
|
|
3
3
|
import { useRouter } from 'vitepress'
|
|
4
|
-
import {
|
|
4
|
+
import { nextTick, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'
|
|
5
|
+
|
|
6
|
+
// ── Dropdown ──────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
interface DropdownRect { top: number; left: number; width: number }
|
|
9
|
+
|
|
10
|
+
function useDropdown() {
|
|
11
|
+
const open = ref(false)
|
|
12
|
+
const rect = ref<DropdownRect | null>(null)
|
|
13
|
+
const triggerRef = ref<HTMLElement | null>(null)
|
|
14
|
+
|
|
15
|
+
function toggle() {
|
|
16
|
+
if (!open.value && triggerRef.value) {
|
|
17
|
+
const r = triggerRef.value.getBoundingClientRect()
|
|
18
|
+
rect.value = { top: r.bottom + 4, left: r.left, width: r.width }
|
|
19
|
+
}
|
|
20
|
+
open.value = !open.value
|
|
21
|
+
}
|
|
22
|
+
function close() { open.value = false }
|
|
23
|
+
function onOutside(e: MouseEvent) {
|
|
24
|
+
if (triggerRef.value && !triggerRef.value.contains(e.target as Node)) close()
|
|
25
|
+
}
|
|
26
|
+
onMounted(() => document.addEventListener('mousedown', onOutside))
|
|
27
|
+
onUnmounted(() => document.removeEventListener('mousedown', onOutside))
|
|
28
|
+
|
|
29
|
+
return { open, rect, triggerRef, toggle, close }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const distroDropdown = useDropdown()
|
|
33
|
+
const versionDropdown = useDropdown()
|
|
5
34
|
|
|
6
35
|
const emit = defineEmits<{ (e: 'close'): void }>()
|
|
7
36
|
const router = useRouter()
|
|
@@ -40,18 +69,20 @@ const indexLoading = ref(true)
|
|
|
40
69
|
const loadError = ref(false)
|
|
41
70
|
const availableDistros = ref<string[]>([])
|
|
42
71
|
const availableVersions = ref<string[]>([])
|
|
72
|
+
// Per-distro version map from distro-meta.json — pagefind's merged-index filter API
|
|
73
|
+
// leaks version values across distros, so we use this static manifest instead.
|
|
74
|
+
const distroVersionMap = ref<Record<string, string[]>>({})
|
|
43
75
|
|
|
44
76
|
async function loadIndexes() {
|
|
45
77
|
// Always use absolute paths from origin root — current site base is irrelevant
|
|
46
|
-
// (on /alt-platform/ we still need to load /alt-domain/pagefind/pagefind.js)
|
|
47
78
|
|
|
48
79
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
80
|
let pf: any = null
|
|
50
81
|
let firstIdx = -1
|
|
51
82
|
|
|
83
|
+
// Find first available distro to get the pagefind module
|
|
52
84
|
for (let i = 0; i < ALL_DISTROS.length; i++) {
|
|
53
85
|
try {
|
|
54
|
-
// @vite-ignore
|
|
55
86
|
pf = await import(/* @vite-ignore */ `/${ALL_DISTROS[i]}/pagefind/pagefind.js`)
|
|
56
87
|
firstIdx = i
|
|
57
88
|
break
|
|
@@ -60,23 +91,60 @@ async function loadIndexes() {
|
|
|
60
91
|
|
|
61
92
|
if (!pf) throw new Error('no pagefind index found')
|
|
62
93
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
94
|
+
// init() must be called before mergeIndex()
|
|
95
|
+
await pf.init()
|
|
96
|
+
|
|
97
|
+
// Pre-check which distros have a pagefind index before calling mergeIndex.
|
|
98
|
+
// A failed mergeIndex() permanently corrupts pagefind's SharedWorker state,
|
|
99
|
+
// making subsequent filters()/search() calls fail with "Failed to load Pagefind metadata".
|
|
100
|
+
const allDistros = [ALL_DISTROS[firstIdx], ...ALL_DISTROS.slice(firstIdx + 1)]
|
|
101
|
+
const metaResults = await Promise.all(
|
|
102
|
+
allDistros.map(async distro => {
|
|
103
|
+
try {
|
|
104
|
+
const r = await fetch(`/${distro}/pagefind/pagefind-entry.json`, { method: 'HEAD' })
|
|
105
|
+
if (!r.ok) return null
|
|
106
|
+
// Fetch distro-meta.json for accurate per-distro filter values.
|
|
107
|
+
// pagefind's merged-index filter API leaks values across distros.
|
|
108
|
+
const meta = await fetch(`/${distro}/pagefind/distro-meta.json`)
|
|
109
|
+
.then(r => r.ok ? r.json() : null)
|
|
110
|
+
.catch(() => null) as Record<string, string[]> | null
|
|
111
|
+
return { distro, meta }
|
|
112
|
+
} catch { return null }
|
|
113
|
+
}),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const confirmed = metaResults.filter((m): m is { distro: string; meta: Record<string, string[]> | null } => m !== null)
|
|
117
|
+
const available = confirmed.slice(1).map(m => m.distro)
|
|
118
|
+
|
|
119
|
+
// Build per-distro version map from distro-meta.json manifests
|
|
120
|
+
const versionMap: Record<string, string[]> = {}
|
|
121
|
+
const allVersionsSet = new Set<string>()
|
|
122
|
+
for (const { distro, meta } of confirmed) {
|
|
123
|
+
const versions = sortVersions(meta?.version ?? [])
|
|
124
|
+
versionMap[distro] = versions
|
|
125
|
+
for (const v of versions) allVersionsSet.add(v)
|
|
67
126
|
}
|
|
127
|
+
distroVersionMap.value = versionMap
|
|
68
128
|
|
|
69
|
-
|
|
129
|
+
// Merge only confirmed indexes in parallel
|
|
130
|
+
await Promise.allSettled(
|
|
131
|
+
available.map(distro => pf.mergeIndex(`/${distro}/pagefind`)),
|
|
132
|
+
)
|
|
70
133
|
|
|
71
134
|
const filters = await pf.filters() as Record<string, Record<string, number>>
|
|
72
135
|
availableDistros.value = Object.keys(filters.distro ?? {}).sort()
|
|
73
|
-
|
|
136
|
+
// Use versions from manifests (accurate) rather than pagefind filters (leaky across indexes)
|
|
137
|
+
availableVersions.value = sortVersions([...allVersionsSet])
|
|
138
|
+
|
|
139
|
+
return pf
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function sortVersions(versions: string[]): string[] {
|
|
143
|
+
return versions.sort((a, b) => {
|
|
74
144
|
const [aMaj, aMin] = a.split('.').map(Number)
|
|
75
145
|
const [bMaj, bMin] = b.split('.').map(Number)
|
|
76
146
|
return bMaj !== aMaj ? bMaj - aMaj : bMin - aMin
|
|
77
147
|
})
|
|
78
|
-
|
|
79
|
-
return pf
|
|
80
148
|
}
|
|
81
149
|
|
|
82
150
|
// ── Filters ───────────────────────────────────────────────────────────────────
|
|
@@ -84,7 +152,20 @@ async function loadIndexes() {
|
|
|
84
152
|
const selectedDistro = ref('')
|
|
85
153
|
const selectedVersion = ref('')
|
|
86
154
|
|
|
87
|
-
|
|
155
|
+
// When distro changes: reset version and update available versions from the manifest
|
|
156
|
+
watch(selectedDistro, () => {
|
|
157
|
+
selectedVersion.value = ''
|
|
158
|
+
if (!selectedDistro.value) {
|
|
159
|
+
// Show union of all versions across all distros
|
|
160
|
+
const all = new Set<string>()
|
|
161
|
+
for (const versions of Object.values(distroVersionMap.value)) {
|
|
162
|
+
for (const v of versions) all.add(v)
|
|
163
|
+
}
|
|
164
|
+
availableVersions.value = sortVersions([...all])
|
|
165
|
+
} else {
|
|
166
|
+
availableVersions.value = distroVersionMap.value[selectedDistro.value] ?? []
|
|
167
|
+
}
|
|
168
|
+
})
|
|
88
169
|
|
|
89
170
|
// ── Search ────────────────────────────────────────────────────────────────────
|
|
90
171
|
|
|
@@ -159,6 +240,13 @@ onMounted(async () => {
|
|
|
159
240
|
inputEl.value?.focus()
|
|
160
241
|
try {
|
|
161
242
|
pagefind.value = await loadIndexes()
|
|
243
|
+
// Re-trigger search if user already typed while indexes were loading
|
|
244
|
+
if (query.value.trim()) {
|
|
245
|
+
const saved = query.value
|
|
246
|
+
query.value = ''
|
|
247
|
+
await nextTick()
|
|
248
|
+
query.value = saved
|
|
249
|
+
}
|
|
162
250
|
} catch {
|
|
163
251
|
loadError.value = true
|
|
164
252
|
} finally {
|
|
@@ -198,16 +286,41 @@ function onOverlayClick(e: MouseEvent) { if (e.target === e.currentTarget) close
|
|
|
198
286
|
|
|
199
287
|
<!-- Filters row (shown once indexes loaded and have data) -->
|
|
200
288
|
<div v-if="!indexLoading && (availableDistros.length || availableVersions.length)" class="gs-filters">
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
|
|
289
|
+
|
|
290
|
+
<!-- Distro dropdown -->
|
|
291
|
+
<div class="gs-dd" :class="{ 'is-open': distroDropdown.open.value }">
|
|
292
|
+
<button :ref="(el) => { distroDropdown.triggerRef.value = el as HTMLElement | null }" class="gs-dd-trigger" :class="{ 'is-active': selectedDistro }" @click="versionDropdown.close(); distroDropdown.toggle()">
|
|
293
|
+
<span>{{ selectedDistro ? (DISTRO_NAMES[selectedDistro] ?? selectedDistro) : 'Все дистрибутивы' }}</span>
|
|
294
|
+
<svg class="gs-dd-chevron" width="12" height="12" viewBox="0 0 24 24"><path fill="currentColor" d="M7 10l5 5 5-5z"/></svg>
|
|
295
|
+
</button>
|
|
296
|
+
<Teleport to="body">
|
|
297
|
+
<ul v-if="distroDropdown.open.value && distroDropdown.rect.value" class="gs-dd-list gs-dd-list--fixed"
|
|
298
|
+
:style="{ top: distroDropdown.rect.value.top + 'px', left: distroDropdown.rect.value.left + 'px', width: distroDropdown.rect.value.width + 'px' }">
|
|
299
|
+
<li class="gs-dd-item" :class="{ 'is-selected': selectedDistro === '' }"
|
|
300
|
+
@click="selectedDistro = ''; distroDropdown.close()">Все дистрибутивы</li>
|
|
301
|
+
<li v-for="d in availableDistros" :key="d" class="gs-dd-item" :class="{ 'is-selected': selectedDistro === d }"
|
|
302
|
+
@click="selectedDistro = d; distroDropdown.close()">{{ DISTRO_NAMES[d] ?? d }}</li>
|
|
303
|
+
</ul>
|
|
304
|
+
</Teleport>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<!-- Version dropdown -->
|
|
308
|
+
<div class="gs-dd" :class="{ 'is-open': versionDropdown.open.value }">
|
|
309
|
+
<button :ref="(el) => { versionDropdown.triggerRef.value = el as HTMLElement | null }" class="gs-dd-trigger" :class="{ 'is-active': selectedVersion }" @click="distroDropdown.close(); versionDropdown.toggle()">
|
|
310
|
+
<span>{{ selectedVersion || 'Все версии' }}</span>
|
|
311
|
+
<svg class="gs-dd-chevron" width="12" height="12" viewBox="0 0 24 24"><path fill="currentColor" d="M7 10l5 5 5-5z"/></svg>
|
|
312
|
+
</button>
|
|
313
|
+
<Teleport to="body">
|
|
314
|
+
<ul v-if="versionDropdown.open.value && versionDropdown.rect.value" class="gs-dd-list gs-dd-list--fixed"
|
|
315
|
+
:style="{ top: versionDropdown.rect.value.top + 'px', left: versionDropdown.rect.value.left + 'px', width: versionDropdown.rect.value.width + 'px' }">
|
|
316
|
+
<li class="gs-dd-item" :class="{ 'is-selected': selectedVersion === '' }"
|
|
317
|
+
@click="selectedVersion = ''; versionDropdown.close()">Все версии</li>
|
|
318
|
+
<li v-for="v in availableVersions" :key="v" class="gs-dd-item" :class="{ 'is-selected': selectedVersion === v }"
|
|
319
|
+
@click="selectedVersion = v; versionDropdown.close()">{{ v }}</li>
|
|
320
|
+
</ul>
|
|
321
|
+
</Teleport>
|
|
322
|
+
</div>
|
|
323
|
+
|
|
211
324
|
</div>
|
|
212
325
|
|
|
213
326
|
<!-- Results -->
|
|
@@ -325,26 +438,107 @@ function onOverlayClick(e: MouseEvent) { if (e.target === e.currentTarget) close
|
|
|
325
438
|
.gs-filters {
|
|
326
439
|
display: flex;
|
|
327
440
|
gap: 8px;
|
|
328
|
-
padding:
|
|
441
|
+
padding: 10px 16px;
|
|
329
442
|
border-bottom: 1px solid var(--vp-c-divider);
|
|
330
|
-
background: var(--vp-c-default-soft);
|
|
331
443
|
}
|
|
332
444
|
|
|
333
|
-
|
|
445
|
+
/* ── Custom dropdown ── */
|
|
446
|
+
.gs-dd {
|
|
447
|
+
position: relative;
|
|
334
448
|
flex: 1;
|
|
335
449
|
min-width: 0;
|
|
336
|
-
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.gs-dd-trigger {
|
|
453
|
+
width: 100%;
|
|
454
|
+
display: flex;
|
|
455
|
+
align-items: center;
|
|
456
|
+
justify-content: space-between;
|
|
457
|
+
gap: 6px;
|
|
458
|
+
padding: 6px 10px;
|
|
337
459
|
font-size: 13px;
|
|
338
|
-
|
|
460
|
+
font-family: inherit;
|
|
461
|
+
background: var(--vp-c-bg-soft);
|
|
339
462
|
border: 1px solid var(--vp-c-divider);
|
|
340
|
-
border-radius:
|
|
341
|
-
color: var(--vp-c-text-
|
|
463
|
+
border-radius: 8px;
|
|
464
|
+
color: var(--vp-c-text-2);
|
|
342
465
|
cursor: pointer;
|
|
343
466
|
outline: none;
|
|
467
|
+
text-align: left;
|
|
468
|
+
transition: border-color 0.15s, color 0.15s;
|
|
469
|
+
white-space: nowrap;
|
|
470
|
+
overflow: hidden;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.gs-dd-trigger span {
|
|
474
|
+
overflow: hidden;
|
|
475
|
+
text-overflow: ellipsis;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.gs-dd-trigger.is-active {
|
|
479
|
+
color: var(--vp-c-text-1);
|
|
480
|
+
border-color: var(--vp-c-brand-1);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.gs-dd-trigger:hover {
|
|
484
|
+
border-color: var(--vp-c-text-2);
|
|
485
|
+
color: var(--vp-c-text-1);
|
|
344
486
|
}
|
|
345
487
|
|
|
346
|
-
.gs-
|
|
488
|
+
.gs-dd.is-open .gs-dd-trigger {
|
|
347
489
|
border-color: var(--vp-c-brand-1);
|
|
490
|
+
box-shadow: 0 0 0 2px var(--vp-c-brand-soft);
|
|
491
|
+
color: var(--vp-c-text-1);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.gs-dd-chevron {
|
|
495
|
+
flex-shrink: 0;
|
|
496
|
+
color: var(--vp-c-text-3);
|
|
497
|
+
transition: transform 0.15s;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.gs-dd.is-open .gs-dd-chevron {
|
|
501
|
+
transform: rotate(180deg);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.gs-dd-list {
|
|
505
|
+
list-style: none;
|
|
506
|
+
margin: 0;
|
|
507
|
+
padding: 4px;
|
|
508
|
+
background: var(--vp-c-bg-elv);
|
|
509
|
+
border: 1px solid var(--vp-c-divider);
|
|
510
|
+
border-radius: 8px;
|
|
511
|
+
box-shadow: var(--vp-shadow-3);
|
|
512
|
+
max-height: 220px;
|
|
513
|
+
overflow-y: auto;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.gs-dd-list--fixed {
|
|
517
|
+
position: fixed;
|
|
518
|
+
z-index: 200;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.gs-dd-item {
|
|
522
|
+
padding: 6px 10px;
|
|
523
|
+
font-size: 13px;
|
|
524
|
+
border-radius: 6px;
|
|
525
|
+
cursor: pointer;
|
|
526
|
+
color: var(--vp-c-text-2);
|
|
527
|
+
transition: background 0.1s, color 0.1s;
|
|
528
|
+
white-space: nowrap;
|
|
529
|
+
overflow: hidden;
|
|
530
|
+
text-overflow: ellipsis;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.gs-dd-item:hover {
|
|
534
|
+
background: var(--vp-c-default-soft);
|
|
535
|
+
color: var(--vp-c-text-1);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.gs-dd-item.is-selected {
|
|
539
|
+
background: var(--vp-c-brand-soft);
|
|
540
|
+
color: var(--vp-c-brand-1);
|
|
541
|
+
font-weight: 500;
|
|
348
542
|
}
|
|
349
543
|
|
|
350
544
|
.gs-results {
|
|
@@ -455,16 +649,17 @@ function onOverlayClick(e: MouseEvent) { if (e.target === e.currentTarget) close
|
|
|
455
649
|
padding: 8px 16px;
|
|
456
650
|
border-top: 1px solid var(--vp-c-divider);
|
|
457
651
|
font-size: 12px;
|
|
458
|
-
color: var(--vp-c-text-
|
|
652
|
+
color: var(--vp-c-text-2);
|
|
459
653
|
}
|
|
460
654
|
|
|
461
655
|
kbd {
|
|
462
|
-
background: var(--vp-c-
|
|
463
|
-
border: 1px solid var(--vp-c-
|
|
656
|
+
background: var(--vp-c-bg-soft);
|
|
657
|
+
border: 1px solid var(--vp-c-text-3);
|
|
464
658
|
border-radius: 4px;
|
|
465
659
|
padding: 1px 5px;
|
|
466
660
|
font-size: 11px;
|
|
467
661
|
font-family: inherit;
|
|
662
|
+
color: var(--vp-c-text-1);
|
|
468
663
|
}
|
|
469
664
|
</style>
|
|
470
665
|
|
|
@@ -18,8 +18,14 @@ interface DistroInfo {
|
|
|
18
18
|
title?: string
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
interface SectionInfo {
|
|
22
|
+
name: string
|
|
23
|
+
distros: string[]
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
declare const __VERSIONS_DATA__: {
|
|
22
27
|
distros: { [distroName: string]: DistroInfo }
|
|
28
|
+
sections?: SectionInfo[]
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
const getSafeVersionsData = (): { distros: { [k: string]: DistroInfo } } => {
|
|
@@ -162,11 +168,22 @@ const makeProductItem = (product: string) => {
|
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
const menuGroupItem = computed((): DefaultTheme.NavItemWithChildren => {
|
|
165
|
-
const
|
|
166
|
-
const
|
|
171
|
+
const sections = versionsData.sections ?? []
|
|
172
|
+
const sectionedDistros = new Set(sections.flatMap((s) => s.distros))
|
|
173
|
+
|
|
174
|
+
const unsectioned = availableProducts.value.filter((p) => !sectionedDistros.has(p))
|
|
175
|
+
const regular = unsectioned.filter((p) => !p.endsWith('-e2k'))
|
|
176
|
+
const e2k = unsectioned.filter((p) => p.endsWith('-e2k'))
|
|
167
177
|
|
|
168
178
|
const items: DefaultTheme.NavItemWithChildren['items'] = regular.map(makeProductItem)
|
|
169
179
|
|
|
180
|
+
for (const section of sections) {
|
|
181
|
+
const sectionDistros = section.distros.filter((d) => availableProducts.value.includes(d))
|
|
182
|
+
if (sectionDistros.length > 0) {
|
|
183
|
+
items.push({ text: section.name, items: sectionDistros.map(makeProductItem) })
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
170
187
|
if (e2k.length > 0) {
|
|
171
188
|
items.push({ text: 'Для Эльбрус', items: e2k.map(makeProductItem) })
|
|
172
189
|
}
|
package/dist/config/shared.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { DefaultTheme } from 'vitepress';
|
|
2
|
+
import type { SectionInfo } from '@ampernic/vitepress-plugin-alt-docs-versioning';
|
|
3
|
+
export type { SectionInfo };
|
|
2
4
|
export interface SharedConfigOptions {
|
|
3
5
|
/** Distro slug for this VitePress instance, e.g. `'alt-domain'`. */
|
|
4
6
|
distroName: string;
|
|
@@ -7,6 +9,17 @@ export interface SharedConfigOptions {
|
|
|
7
9
|
* When omitted only the current distro appears in the menu — fine for local dev.
|
|
8
10
|
*/
|
|
9
11
|
allDistros?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Optional nav groupings for the ADProducts switcher.
|
|
14
|
+
* Each entry renders as a sub-menu with a display name.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* sections: [
|
|
18
|
+
* { name: 'Альт Виртуализация', distros: ['alt-virtualization-pve', 'alt-virtualisation-one'] },
|
|
19
|
+
* { name: 'Групповые политики', distros: ['group-policy'] },
|
|
20
|
+
* ]
|
|
21
|
+
*/
|
|
22
|
+
sections?: SectionInfo[];
|
|
10
23
|
/** Sitemap hostname, e.g. `'https://docs.altlinux.org'`. */
|
|
11
24
|
hostname?: string;
|
|
12
25
|
/**
|
package/dist/config/shared.js
CHANGED
|
@@ -49,7 +49,8 @@ function createSharedConfig(options) {
|
|
|
49
49
|
},
|
|
50
50
|
plugins: [...pagefind.vite.plugins, (0, _vitepressPluginAltDocsVersioning.VersioningPlugin)({
|
|
51
51
|
distroName: options.distroName,
|
|
52
|
-
allDistros: options.allDistros
|
|
52
|
+
allDistros: options.allDistros,
|
|
53
|
+
sections: options.sections
|
|
53
54
|
})]
|
|
54
55
|
},
|
|
55
56
|
srcDir: "docs",
|
|
@@ -74,6 +75,7 @@ function createSharedConfig(options) {
|
|
|
74
75
|
light: "/branding/basealt/logo-col.svg",
|
|
75
76
|
alt: "ALT Linux Documentation"
|
|
76
77
|
},
|
|
78
|
+
logoLink: "/",
|
|
77
79
|
siteTitle: "ALT Linux Docs",
|
|
78
80
|
outline: {
|
|
79
81
|
level: [2, 3]
|
package/dist/config/shared.mjs
CHANGED
|
@@ -52,7 +52,8 @@ export function createSharedConfig(options) {
|
|
|
52
52
|
...pagefind.vite.plugins,
|
|
53
53
|
VersioningPlugin({
|
|
54
54
|
distroName: options.distroName,
|
|
55
|
-
allDistros: options.allDistros
|
|
55
|
+
allDistros: options.allDistros,
|
|
56
|
+
sections: options.sections
|
|
56
57
|
})
|
|
57
58
|
]
|
|
58
59
|
},
|
|
@@ -72,6 +73,7 @@ export function createSharedConfig(options) {
|
|
|
72
73
|
light: "/branding/basealt/logo-col.svg",
|
|
73
74
|
alt: "ALT Linux Documentation"
|
|
74
75
|
},
|
|
76
|
+
logoLink: "/",
|
|
75
77
|
siteTitle: "ALT Linux Docs",
|
|
76
78
|
outline: { level: [2, 3] },
|
|
77
79
|
socialLinks: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ampernic/vitepress-theme-alt-docs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Shared VitePress theme for ALT Linux documentation",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"author": "Ampernic",
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"@nolebase/vitepress-plugin-enhanced-readabilities": "^2.14.0",
|
|
34
34
|
"markdown-it-kbd": "^1.0.0",
|
|
35
35
|
"vitepress-plugin-tabs": "^0.6.0",
|
|
36
|
-
"@ampernic/vitepress-plugin-alt-docs-versioning": "0.1.
|
|
36
|
+
"@ampernic/vitepress-plugin-alt-docs-versioning": "0.1.3",
|
|
37
37
|
"@ampernic/vitepress-plugin-html-image": "0.1.2",
|
|
38
|
-
"@ampernic/vitepress-plugin-
|
|
39
|
-
"@ampernic/vitepress-plugin-
|
|
38
|
+
"@ampernic/vitepress-plugin-pagefind": "0.1.8",
|
|
39
|
+
"@ampernic/vitepress-plugin-cross-site-router": "0.1.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"builtin-modules": "^3.3.0",
|