Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef904ca952 | |||
| df88416247 | |||
| b948e5a433 | |||
| e8dc61f5a1 | |||
| 62f12c8ce1 | |||
| a9cafb629c | |||
| abdc7bd70e | |||
| 7fd1755a7d |
Generated
+1589
-1150
File diff suppressed because it is too large
Load Diff
+11
-10
@@ -15,16 +15,17 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@beeline/design-tokens": "1.31.0",
|
||||
"vue": "3.4.7"
|
||||
"@beeline/design-tokens": "^1.31.6",
|
||||
"vue": "^3.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docsearch/css": "3.3.0",
|
||||
"@types/node": "20.10.7",
|
||||
"@vitejs/plugin-vue": "4.3.4",
|
||||
"sass": "1.69.7",
|
||||
"typescript": "^5.8.3",
|
||||
"vitepress": "1.0.0-rc.40",
|
||||
"vitepress-plugin-tabs": "0.5.0"
|
||||
"@docsearch/css": "4.1.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"sass": "^1.70.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite-plugin-static-copy": "^3.1.4",
|
||||
"vitepress": "^1.6.4",
|
||||
"vitepress-plugin-tabs": "^0.7.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
+108
-81
@@ -1,6 +1,9 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
||||
import { overrideComponents } from './override-components'
|
||||
import { resolve } from 'path'
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
const gitlab = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
@@ -38,82 +41,8 @@ const gitlab = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
`
|
||||
|
||||
const new_version = process.env?.VITE_NEW_VERSION;
|
||||
console.log({ base: typeof new_version !== 'undefined' ? '/' : '/docs/' })
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
srcDir: ".",
|
||||
title: " ",
|
||||
description: "Документация Beeline Cloud",
|
||||
head: [['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/bee-favicon.png' }]],
|
||||
base: typeof new_version !== 'undefined' ? '/' : '/docs/',
|
||||
markdown: {
|
||||
config(md) {
|
||||
md.use(tabsMarkdownPlugin)
|
||||
}
|
||||
},
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: overrideComponents(),
|
||||
}
|
||||
},
|
||||
locales: {
|
||||
root: {
|
||||
label: 'Русский',
|
||||
lang: 'ru',
|
||||
}
|
||||
},
|
||||
themeConfig: {
|
||||
logo: {
|
||||
light: '/img/logo-cloud.svg',
|
||||
dark: '/img/logo-cloud.svg',
|
||||
alt: '',
|
||||
},
|
||||
search: {
|
||||
provider: 'local',
|
||||
options: {
|
||||
locales: {
|
||||
root: {
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: 'Поиск',
|
||||
buttonAriaLabel: 'Поиск'
|
||||
},
|
||||
modal: {
|
||||
noResultsText: 'По вашему запросу ничего не найдено',
|
||||
resetButtonTitle: 'Сбросить',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
// nav: [
|
||||
// {
|
||||
// text: 'Документация',
|
||||
// link: '/guide/',
|
||||
// },
|
||||
// {
|
||||
// text: 'API',
|
||||
// link: '',
|
||||
// },
|
||||
// {
|
||||
// text: 'Terraform',
|
||||
// // link: '/terraform/',
|
||||
// link: '',
|
||||
// },
|
||||
// ],
|
||||
|
||||
docFooter: {
|
||||
next: 'Вперед',
|
||||
prev: 'Назад'
|
||||
},
|
||||
|
||||
outline: {
|
||||
label: 'Содержание'
|
||||
},
|
||||
sidebar: {
|
||||
const sidebarConfig = {
|
||||
'/platform/': [
|
||||
{
|
||||
text: 'Платформа Beeline Cloud', link: '/platform/index.md',
|
||||
@@ -140,11 +69,10 @@ export default defineConfig({
|
||||
text: 'Резервное копирование', link: '/backups/index.md',
|
||||
},
|
||||
{
|
||||
text: 'Обзор сервиса', link: '/backups/backups-overview.md',
|
||||
text: 'Обзор сервиса', link: '/backups/about.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'О сервисе', link: '/backups/about.md'},
|
||||
{text: 'Квоты и лимиты', link: '/backups/backup-quatos.md'},
|
||||
{text: 'Квоты и лимиты', link: '/backups/backup-quatos.md'},
|
||||
]
|
||||
},
|
||||
{text: 'Резервное копирование виртуальных машин Beeline Cloud', link: '/backups/backup-internal-infra.md'},
|
||||
@@ -216,7 +144,6 @@ export default defineConfig({
|
||||
{ text: 'Настройка site-to-site подключения с помощью IPSec', link: '/vdc/vdc-how-to/networks/how-to-setup-ipsec-vpn.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: 'Настройка IPSec VPN', link: '/vdc/vdc-how-to/networks/ipsec/setup-ipsec-vpn.md'},
|
||||
{text: 'Настройка ASAv', link: '/vdc/vdc-how-to/networks/ipsec/asav.md'},
|
||||
{text: 'Настройка CSR 1000v', link: '/vdc/vdc-how-to/networks/ipsec/csr1000v.md'},
|
||||
{text: 'Настройка Fortigate', link: '/vdc/vdc-how-to/networks/ipsec/fortigate.md'},
|
||||
@@ -255,7 +182,7 @@ export default defineConfig({
|
||||
{ text: 'Квоты и лимиты', link: '/compute/compute-quatos.md' },
|
||||
]
|
||||
},
|
||||
{text: 'Быстрый старт', link: '/compute/compute-getting-started.md'},
|
||||
{text: 'Быстрый старт', link: '/compute/compute-getting-started.md', excludeFromIndex: true },
|
||||
{ text: 'Виртуальные машины', link: '/compute/compute-how-to/compute-index.md',
|
||||
collapsed: true,
|
||||
items: [
|
||||
@@ -306,6 +233,7 @@ export default defineConfig({
|
||||
{ text: 'Настройка site-to-site VPN с помощью VyOS', link: '/compute/compute-how-to/compute-network/compute-vpn-vyos.md' },
|
||||
{ text: 'Подключение ВМ закрытого контура к интернету', link: '/compute/compute-how-to/compute-network/compute-network-inside.md' },
|
||||
],
|
||||
excludeFromIndex: true,
|
||||
},
|
||||
],
|
||||
'/admin/': [
|
||||
@@ -346,7 +274,106 @@ export default defineConfig({
|
||||
text: 'Подключение к виртуальному рабочему месту', link: '/vdi/vdi-how-to/vdi-connect.md'
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
srcDir: ".",
|
||||
title: " ",
|
||||
description: "Документация Beeline Cloud",
|
||||
head: [['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/bee-favicon.png' }]],
|
||||
base: typeof new_version !== 'undefined' ? '/' : '/docs/',
|
||||
appearance: false,
|
||||
markdown: {
|
||||
config(md) {
|
||||
md.use(tabsMarkdownPlugin)
|
||||
}
|
||||
},
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: [
|
||||
...overrideComponents(),
|
||||
{
|
||||
find: '@',
|
||||
replacement: fileURLToPath(new URL('./theme', import.meta.url))
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
viteStaticCopy({
|
||||
targets: [
|
||||
{
|
||||
src: resolve(__dirname, '../../node_modules/@beeline/design-tokens/assets/fonts'),
|
||||
dest: 'assets',
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
api: 'modern-compiler',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
locales: {
|
||||
root: {
|
||||
label: 'Русский',
|
||||
lang: 'ru',
|
||||
}
|
||||
},
|
||||
themeConfig: {
|
||||
logo: {
|
||||
light: '/img/logo-cloud.svg',
|
||||
dark: '/img/logo-cloud.svg',
|
||||
alt: '',
|
||||
},
|
||||
search: {
|
||||
provider: 'local',
|
||||
options: {
|
||||
locales: {
|
||||
root: {
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: 'Поиск',
|
||||
buttonAriaLabel: 'Поиск'
|
||||
},
|
||||
modal: {
|
||||
noResultsText: 'По вашему запросу ничего не найдено',
|
||||
resetButtonTitle: 'Сбросить',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
// nav: [
|
||||
// {
|
||||
// text: 'Документация',
|
||||
// link: '/guide/',
|
||||
// },
|
||||
// {
|
||||
// text: 'API',
|
||||
// link: '',
|
||||
// },
|
||||
// {
|
||||
// text: 'Terraform',
|
||||
// // link: '/terraform/',
|
||||
// link: '',
|
||||
// },
|
||||
// ],
|
||||
|
||||
docFooter: {
|
||||
next: 'Вперед',
|
||||
prev: 'Назад'
|
||||
},
|
||||
|
||||
outline: {
|
||||
label: 'Содержание'
|
||||
},
|
||||
sidebar: sidebarConfig,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { Plugin } from 'vite'
|
||||
import type { SidebarItem } from './utils/types'
|
||||
import { processAllFiles } from './hook/process-files'
|
||||
|
||||
export const autoSectionLinksPlugin = (
|
||||
srcDir: string,
|
||||
sidebarConfig: Record<string, SidebarItem[]>
|
||||
): Plugin => ({
|
||||
name: 'auto-section-links',
|
||||
buildStart: () => {
|
||||
processAllFiles(srcDir, sidebarConfig)
|
||||
},
|
||||
configureServer: () => {
|
||||
processAllFiles(srcDir, sidebarConfig)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
export const INDEX_FILE = 'index.md'
|
||||
export const INDEX_FILE_PATTERN = /-index\.md$|index\.md$/
|
||||
export const FRONTMATTER_REGEX = /^---\s*\n([\s\S]*?)\n---/
|
||||
export const SECTION_LINK_KEYS = ['title', 'link', 'description'] as const
|
||||
@@ -0,0 +1,36 @@
|
||||
import { existsSync } from 'fs'
|
||||
import { relative, resolve } from 'path'
|
||||
import type { SidebarItem } from '../utils/types'
|
||||
import { findMarkdownFiles } from '../utils/file-finder'
|
||||
import { processIndexFile, processPageWithItems } from '../utils/file-processor'
|
||||
import { INDEX_FILE, INDEX_FILE_PATTERN } from '../constants'
|
||||
|
||||
export const processAllFiles = (
|
||||
srcDir: string,
|
||||
sidebarConfig: Record<string, SidebarItem[]>
|
||||
) => {
|
||||
const srcPath = resolve(srcDir)
|
||||
|
||||
for (const [folderPath, items] of Object.entries(sidebarConfig)) {
|
||||
const normalizedFolder = folderPath.replace(/^\/+|\/+$/g, '')
|
||||
const folderFullPath = resolve(srcPath, normalizedFolder)
|
||||
|
||||
if (!existsSync(folderFullPath)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const mdFiles = findMarkdownFiles(folderFullPath)
|
||||
|
||||
for (const filePath of mdFiles) {
|
||||
const fileName = filePath.split(/[/\\]/).pop() || ''
|
||||
const relativePath = relative(srcPath, filePath).replace(/\\/g, '/')
|
||||
const normalizedPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`
|
||||
|
||||
if (fileName === INDEX_FILE) {
|
||||
processIndexFile(filePath, items, srcPath)
|
||||
} else if (INDEX_FILE_PATTERN.test(fileName) && fileName !== INDEX_FILE) {
|
||||
processPageWithItems(filePath, items, srcPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { readFileSync, existsSync } from 'fs'
|
||||
import { resolve } from 'path'
|
||||
import { parseFrontmatter } from './frontmatter'
|
||||
|
||||
export const getPageDescription = (filePath: string, srcDir: string) => {
|
||||
try {
|
||||
const fullPath = resolve(srcDir, filePath.replace(/^\//, ''))
|
||||
if (!existsSync(fullPath)) return undefined
|
||||
|
||||
const fileContent = readFileSync(fullPath, 'utf-8')
|
||||
const { frontmatter } = parseFrontmatter(fileContent)
|
||||
|
||||
return frontmatter.description as string | undefined
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { readdirSync, statSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
|
||||
export const findMarkdownFiles = (dir: string) => {
|
||||
const files: string[] = []
|
||||
|
||||
try {
|
||||
const entries = readdirSync(dir)
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = join(dir, entry)
|
||||
const stat = statSync(fullPath)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
files.push(...findMarkdownFiles(fullPath))
|
||||
} else if (entry.endsWith('.md')) {
|
||||
files.push(fullPath)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import { readFileSync, writeFileSync, existsSync } from 'fs'
|
||||
import { relative, resolve } from 'path'
|
||||
import type { SidebarItem } from './types'
|
||||
import { parseFrontmatter, stringifyFrontmatter } from './frontmatter'
|
||||
import { mergeSectionLinks, extractTopLevelLinks, extractItemsForPage } from './links'
|
||||
import { normalizeLink } from './path-utils'
|
||||
import { SectionLinkListItem } from '../../theme/components/SectionLinkList/SectionLinkList.types'
|
||||
|
||||
const removeDuplicates = (links: SectionLinkListItem[]) => {
|
||||
const result: SectionLinkListItem[] = []
|
||||
const seenLinks = new Set<string>()
|
||||
|
||||
for (const link of links) {
|
||||
if (link.link) {
|
||||
const normalized = normalizeLink(link.link)
|
||||
if (!seenLinks.has(normalized)) {
|
||||
seenLinks.add(normalized)
|
||||
result.push(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const hasChanges = (
|
||||
existingLinks: SectionLinkListItem[],
|
||||
mergedLinks: SectionLinkListItem[]
|
||||
) => {
|
||||
if (existingLinks.length !== mergedLinks.length) return true
|
||||
|
||||
const existingSet = new Set(
|
||||
existingLinks
|
||||
.filter(link => link.link)
|
||||
.map(link => `${normalizeLink(link.link!)}|${link.title}`)
|
||||
)
|
||||
|
||||
const mergedSet = new Set(
|
||||
mergedLinks
|
||||
.filter(link => link.link)
|
||||
.map(link => `${normalizeLink(link.link!)}|${link.title}`)
|
||||
)
|
||||
|
||||
if (existingSet.size !== mergedSet.size) return true
|
||||
|
||||
for (const item of existingSet) {
|
||||
if (!mergedSet.has(item)) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export const processIndexFile = (
|
||||
filePath: string,
|
||||
sidebarItems: SidebarItem[],
|
||||
srcDir: string
|
||||
) => {
|
||||
if (!existsSync(filePath)) return false
|
||||
|
||||
try {
|
||||
const fileContent = readFileSync(filePath, 'utf-8')
|
||||
const { frontmatter, content } = parseFrontmatter(fileContent)
|
||||
|
||||
const relativePath = relative(srcDir, filePath).replace(/\\/g, '/')
|
||||
const normalizedPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`
|
||||
|
||||
const existingLinks = removeDuplicates(
|
||||
Array.isArray(frontmatter.section_links) ? [...frontmatter.section_links] : []
|
||||
)
|
||||
const newLinks = extractTopLevelLinks(sidebarItems, normalizedPath, srcDir)
|
||||
|
||||
if (!newLinks.length) return false
|
||||
|
||||
const mergedLinks = removeDuplicates(mergeSectionLinks(existingLinks, newLinks))
|
||||
|
||||
if (!hasChanges(existingLinks, mergedLinks)) return false
|
||||
|
||||
frontmatter.section_links = mergedLinks
|
||||
writeFileSync(filePath, stringifyFrontmatter(frontmatter, content), 'utf-8')
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const processPageWithItems = (
|
||||
filePath: string,
|
||||
sidebarItems: SidebarItem[],
|
||||
srcDir: string
|
||||
) => {
|
||||
if (!existsSync(filePath)) return false
|
||||
|
||||
try {
|
||||
const fileContent = readFileSync(filePath, 'utf-8')
|
||||
const { frontmatter, content } = parseFrontmatter(fileContent)
|
||||
|
||||
const relativePath = relative(srcDir, filePath).replace(/\\/g, '/')
|
||||
const normalizedPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`
|
||||
|
||||
const existingLinks = removeDuplicates(
|
||||
Array.isArray(frontmatter.section_links) ? [...frontmatter.section_links] : []
|
||||
)
|
||||
const newLinks = extractItemsForPage(sidebarItems, normalizedPath, srcDir)
|
||||
|
||||
const mergedLinks = newLinks.length > 0
|
||||
? removeDuplicates(mergeSectionLinks(existingLinks, newLinks))
|
||||
: []
|
||||
|
||||
if (!hasChanges(existingLinks, mergedLinks)) return false
|
||||
|
||||
frontmatter.section_links = mergedLinks
|
||||
writeFileSync(filePath, stringifyFrontmatter(frontmatter, content), 'utf-8')
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import type { Frontmatter } from './types'
|
||||
import { SECTION_LINK_KEYS, FRONTMATTER_REGEX } from '../constants'
|
||||
import { SectionLinkListItem } from '../../theme/components/SectionLinkList/SectionLinkList.types'
|
||||
|
||||
export const parseFrontmatter = (content: string) => {
|
||||
const frontmatterMatch = content.match(FRONTMATTER_REGEX)
|
||||
if (!frontmatterMatch) {
|
||||
const cleanContent = content.replace(/^---[\s\S]*?---\s*\n*/g, '').trim()
|
||||
return { frontmatter: {}, content: cleanContent || content }
|
||||
}
|
||||
|
||||
const [, frontmatterText] = frontmatterMatch
|
||||
const frontmatterEnd = frontmatterMatch[0].length
|
||||
let cleanContent = content.slice(frontmatterEnd)
|
||||
cleanContent = cleanContent.replace(/^---[\s\S]*?---\s*\n*/g, '').trim()
|
||||
if (!cleanContent) {
|
||||
cleanContent = content.slice(frontmatterEnd).replace(/^---[\s\S]*?---\s*\n*/g, '').trim()
|
||||
}
|
||||
|
||||
const frontmatter: Frontmatter = {}
|
||||
const lines = frontmatterText.split('\n')
|
||||
let currentKey: string | undefined
|
||||
let currentValue: SectionLinkListItem[] = []
|
||||
let currentItem: Record<string, string> | undefined
|
||||
let inArray = false
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim()
|
||||
if (trimmed && !trimmed.startsWith('#')) {
|
||||
const lineIndent = line.match(/^(\s*)/)?.[1]?.length ?? 0
|
||||
|
||||
if (lineIndent === 0 && trimmed.includes(':')) {
|
||||
if (inArray && currentKey) {
|
||||
frontmatter[currentKey] = currentValue
|
||||
currentValue = []
|
||||
inArray = false
|
||||
}
|
||||
|
||||
const colonMatch = trimmed.match(/^([^:]+):\s*(.*)$/)
|
||||
if (colonMatch) {
|
||||
const [, key, value] = colonMatch
|
||||
currentKey = key.trim()
|
||||
const trimmedValue = value.trim()
|
||||
|
||||
if (trimmedValue === '') {
|
||||
inArray = true
|
||||
currentValue = []
|
||||
} else {
|
||||
frontmatter[currentKey] = trimmedValue.replace(/^["']|["']$/g, '')
|
||||
}
|
||||
}
|
||||
} else if (lineIndent === 2 && trimmed.startsWith('- ')) {
|
||||
if (!inArray && currentKey) {
|
||||
inArray = true
|
||||
currentValue = []
|
||||
}
|
||||
|
||||
if (currentItem && currentItem.title && currentItem.link !== undefined) {
|
||||
const link: SectionLinkListItem = {
|
||||
title: currentItem.title,
|
||||
link: currentItem.link,
|
||||
description: currentItem.description
|
||||
}
|
||||
if (!currentValue.some(item => item.link === link.link)) {
|
||||
currentValue.push(link)
|
||||
}
|
||||
}
|
||||
|
||||
const itemText = trimmed.slice(2).trim()
|
||||
if (itemText.includes(':')) {
|
||||
currentItem = {}
|
||||
const parts = itemText.split(',').map(p => p.trim())
|
||||
for (const part of parts) {
|
||||
const colonMatch = part.match(/^(\w+):\s*(.+)$/)
|
||||
if (colonMatch) {
|
||||
const [, key, value] = colonMatch
|
||||
if (SECTION_LINK_KEYS.includes(key as typeof SECTION_LINK_KEYS[number])) {
|
||||
currentItem[key] = value.replace(/^["']|["']$/g, '')
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
currentValue.push({ title: itemText, link: '' })
|
||||
}
|
||||
} else if (lineIndent >= 4 && currentItem && trimmed.includes(':')) {
|
||||
const colonIndex = trimmed.indexOf(':')
|
||||
if (colonIndex !== -1) {
|
||||
const key = trimmed.substring(0, colonIndex).trim()
|
||||
const value = trimmed.substring(colonIndex + 1).trim()
|
||||
if (SECTION_LINK_KEYS.includes(key as typeof SECTION_LINK_KEYS[number])) {
|
||||
currentItem[key] = value === '' ? '' : value.replace(/^["']|["']$/g, '')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentItem && inArray && currentItem.title && currentItem.link !== undefined) {
|
||||
const link: SectionLinkListItem = {
|
||||
title: currentItem.title,
|
||||
link: currentItem.link,
|
||||
description: currentItem.description
|
||||
}
|
||||
if (!currentValue.some(item => item.link === link.link)) {
|
||||
currentValue.push(link)
|
||||
}
|
||||
}
|
||||
if (inArray && currentKey) {
|
||||
frontmatter[currentKey] = currentValue
|
||||
}
|
||||
|
||||
return { frontmatter, content: cleanContent || content }
|
||||
}
|
||||
|
||||
export const stringifyFrontmatter = (frontmatter: Frontmatter, content: string) => {
|
||||
const lines: string[] = []
|
||||
|
||||
for (const [key, value] of Object.entries(frontmatter)) {
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item) {
|
||||
const link = item as SectionLinkListItem
|
||||
lines.push(` - title: ${link.title ?? ''}`)
|
||||
if (link.link) lines.push(` link: ${link.link}`)
|
||||
if ('description' in link && link.description !== undefined && link.description !== null && String(link.description).trim() !== '') {
|
||||
lines.push(` description: ${String(link.description)}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(` - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (value) {
|
||||
const strValue = String(value)
|
||||
const needsQuotes = strValue.includes(':') || (strValue.includes(' ') && !strValue.startsWith('"')) || strValue === ''
|
||||
const escaped = strValue.replace(/"/g, '\\"')
|
||||
lines.push(`${key}: ${needsQuotes ? `"${escaped}"` : strValue}`)
|
||||
}
|
||||
}
|
||||
|
||||
return `---\n${lines.join('\n')}\n---\n\n${content}`
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import type { SidebarItem } from './types'
|
||||
import { getPageDescription } from './descriptions'
|
||||
import { normalizeLink } from './path-utils'
|
||||
import { SectionLinkListItem } from '../../theme/components/SectionLinkList/SectionLinkList.types'
|
||||
|
||||
export const mergeSectionLinks = (
|
||||
existingLinks: SectionLinkListItem[],
|
||||
newLinks: SectionLinkListItem[]
|
||||
) => {
|
||||
const existingLinksMap = new Map<string, SectionLinkListItem>()
|
||||
const result: SectionLinkListItem[] = []
|
||||
const processedLinks = new Set<string>()
|
||||
|
||||
for (const existingLink of existingLinks) {
|
||||
if (existingLink.link) {
|
||||
const normalizedLink = normalizeLink(existingLink.link)
|
||||
if (!existingLinksMap.has(normalizedLink)) {
|
||||
existingLinksMap.set(normalizedLink, existingLink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const newLink of newLinks) {
|
||||
if (newLink.link) {
|
||||
const normalizedLink = normalizeLink(newLink.link)
|
||||
|
||||
if (!processedLinks.has(normalizedLink)) {
|
||||
processedLinks.add(normalizedLink)
|
||||
|
||||
if (existingLinksMap.has(normalizedLink)) {
|
||||
const existingLink = existingLinksMap.get(normalizedLink)!
|
||||
result.push({ ...existingLink, title: newLink.title })
|
||||
} else {
|
||||
result.push(newLink)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export const extractTopLevelLinks = (
|
||||
items: SidebarItem[],
|
||||
currentIndexPath: string,
|
||||
srcDir: string
|
||||
) => {
|
||||
const links: SectionLinkListItem[] = []
|
||||
const normalizedCurrentPath = normalizeLink(currentIndexPath)
|
||||
|
||||
for (const item of items) {
|
||||
if ('excludeFromIndex' in item && item.excludeFromIndex) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (item.link) {
|
||||
const normalizedItemPath = normalizeLink(item.link)
|
||||
if (normalizedItemPath === normalizedCurrentPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
links.push({
|
||||
title: item.text || '',
|
||||
link: item.link,
|
||||
description: getPageDescription(item.link, srcDir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
const findItemsRecursive = (
|
||||
itemsList: SidebarItem[],
|
||||
pagePath: string,
|
||||
srcDir: string
|
||||
): SectionLinkListItem[] => {
|
||||
const normalizedPagePath = normalizeLink(pagePath)
|
||||
const links: SectionLinkListItem[] = []
|
||||
|
||||
for (const item of itemsList) {
|
||||
if (item.link) {
|
||||
const normalizedItemPath = normalizeLink(item.link)
|
||||
|
||||
if (normalizedItemPath === normalizedPagePath && item.items && Array.isArray(item.items) && item.items.length > 0) {
|
||||
for (const subItem of item.items) {
|
||||
if ('excludeFromIndex' in subItem && subItem.excludeFromIndex) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (subItem.link) {
|
||||
links.push({
|
||||
title: subItem.text || '',
|
||||
link: subItem.link,
|
||||
description: getPageDescription(subItem.link, srcDir)
|
||||
})
|
||||
}
|
||||
}
|
||||
return links
|
||||
}
|
||||
}
|
||||
|
||||
if (item.items && Array.isArray(item.items)) {
|
||||
const found = findItemsRecursive(item.items, pagePath, srcDir)
|
||||
if (found.length > 0) {
|
||||
return found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
export const extractItemsForPage = (
|
||||
items: SidebarItem[],
|
||||
pagePath: string,
|
||||
srcDir: string
|
||||
) => findItemsRecursive(items, pagePath, srcDir)
|
||||
@@ -0,0 +1,6 @@
|
||||
export const normalizeLink = (link: string): string =>
|
||||
!link ? '' : link
|
||||
.replace(/^\/+/, '')
|
||||
.replace(/\.md$/, '')
|
||||
.replace(/\\/g, '/')
|
||||
.trim()
|
||||
@@ -0,0 +1,12 @@
|
||||
import { SectionLinkListItem } from "../../theme/components/SectionLinkList/SectionLinkList.types"
|
||||
|
||||
export type SidebarItem = {
|
||||
text: string
|
||||
link?: string
|
||||
items?: SidebarItem[]
|
||||
}
|
||||
|
||||
export type Frontmatter = {
|
||||
section_links?: SectionLinkListItem[]
|
||||
[key: string]: string | number | boolean | string[] | SectionLinkListItem[] | undefined
|
||||
}
|
||||
@@ -76,7 +76,7 @@ const pageName = computed(() =>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use 'src/assets/scss/app/helpers/media';
|
||||
@use '@/scss/helpers/media';
|
||||
|
||||
.VPDoc {
|
||||
padding: 32px 24px 96px;
|
||||
|
||||
@@ -2,6 +2,8 @@ export type SectionLinkListItem = {
|
||||
title: string,
|
||||
link: string,
|
||||
description?: string
|
||||
collapsed?: boolean
|
||||
excludeFromIndex?: boolean
|
||||
}
|
||||
|
||||
export type SectionLinkListProps = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/components/navigationDrawer';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/theme-variables' as theme;
|
||||
@use 'src/assets/scss/app/helpers/media';
|
||||
@use '@/scss/helpers/media';
|
||||
|
||||
.VPSidebar {
|
||||
--vp-sidebar-bg-color: var(--vp-c-bg);
|
||||
|
||||
+5
@@ -1,3 +1,8 @@
|
||||
@use '@beeline/design-tokens/scss/font-face';
|
||||
@use "@beeline/design-tokens/scss/iconfont/iconfont" with (
|
||||
$font-path-iconfont: '../assets/fonts/iconfont'
|
||||
);
|
||||
@use "@beeline/design-tokens/scss/iconfont/icons";
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/dark';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes';
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
@use "@beeline/design-tokens/scss/iconfont/iconfont" with (
|
||||
$font-path-iconfont: '/fonts/iconfont'
|
||||
);
|
||||
@use "@beeline/design-tokens/scss/iconfont/icons";
|
||||
|
||||
$font-path-beeline-sans: '/fonts/beeline-sans' !default;
|
||||
|
||||
@mixin beeline-sans-font($type, $weight, $style: normal) {
|
||||
@font-face {
|
||||
font-family: "Beeline Sans";
|
||||
src:url('#{$font-path-beeline-sans}/BeelineSans-#{$type}.woff2') format('woff2'),
|
||||
url('#{$font-path-beeline-sans}/BeelineSans-#{$type}.woff') format('woff'),
|
||||
url('#{$font-path-beeline-sans}/BeelineSans-#{$type}.ttf') format('truetype');
|
||||
font-weight: $weight;
|
||||
font-style: $style;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin beeline-sans-font-pair($type, $weight) {
|
||||
@include beeline-sans-font($type, $weight);
|
||||
|
||||
}
|
||||
|
||||
@include beeline-sans-font-pair(Regular, 400);
|
||||
|
||||
@include beeline-sans-font-pair(Medium, 500);
|
||||
|
||||
@include beeline-sans-font-pair(Bold, 700);
|
||||
|
||||
@include beeline-sans-font-pair(Black, 900);
|
||||
|
||||
$font-path-roboto-mono: '/fonts/roboto-mono' !default;
|
||||
|
||||
@mixin roboto-mono-font($type, $weight, $style: normal) {
|
||||
@font-face {
|
||||
font-family: "Roboto Mono";
|
||||
src:url('#{$font-path-roboto-mono}/RobotoMono-#{$type}.woff2') format('woff2'),
|
||||
url('#{$font-path-roboto-mono}/RobotoMono-#{$type}.woff') format('woff'),
|
||||
url('#{$font-path-roboto-mono}/RobotoMono-#{$type}.ttf') format('truetype');
|
||||
font-weight: $weight;
|
||||
font-style: $style;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin roboto-mono-font-pair($type, $weight) {
|
||||
@include roboto-mono-font($type, $weight);
|
||||
|
||||
}
|
||||
|
||||
@include roboto-mono-font-pair(Light, 300);
|
||||
|
||||
@include roboto-mono-font-pair(Regular, 400);
|
||||
|
||||
@include roboto-mono-font-pair(Medium, 500);
|
||||
|
||||
@include roboto-mono-font-pair(Bold, 700);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
@forward "media";
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
@use "sass:map";
|
||||
|
||||
// @deprecated
|
||||
@mixin media($minWidth, $maxWidth) {
|
||||
@media (min-width: $minWidth) and (max-width: $maxWidth) {
|
||||
@@ -27,7 +29,7 @@ $breakpoints: (
|
||||
);
|
||||
|
||||
@mixin max($breakpoint) {
|
||||
$value: map-get($breakpoints, $breakpoint);
|
||||
$value: map.get($breakpoints, $breakpoint);
|
||||
|
||||
@if $value {
|
||||
@media (max-width: $value) {
|
||||
@@ -1,4 +1,3 @@
|
||||
@use "fonts";
|
||||
@use "design-system";
|
||||
@use "design-tokens";
|
||||
@use "vars";
|
||||
@use "components";
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,38 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/themes' as *;
|
||||
|
||||
:root {
|
||||
--app-navbar-height: #{$app-navbar-height};
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
html.dark-theme {
|
||||
background-color: $color-background-base;
|
||||
color: $color-text-active;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: $color-text-active !important;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
@import 'variables';
|
||||
@import 'base';
|
||||
@import 'form';
|
||||
@import 'helpers';
|
||||
@import 'components';
|
||||
@@ -1,3 +0,0 @@
|
||||
// Navbar
|
||||
$app-navbar-height: 3.5rem;
|
||||
$app-header-height: 64px;
|
||||
@@ -1,122 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/themes' as *;
|
||||
@use '@beeline/design-tokens/scss/tokens/components/fab' as *;
|
||||
@use '@beeline/design-tokens/scss/tokens/components/button' as *;
|
||||
@use '@beeline/design-tokens/scss/tokens/globals' as *;
|
||||
@use '@beeline/design-tokens/scss/mixin';
|
||||
|
||||
@mixin fab_button {
|
||||
background-color: $button-overlay-background-color;
|
||||
padding-left: $button-only-text-small-padding-horizontal;
|
||||
padding-right: $button-only-text-small-padding-horizontal;
|
||||
letter-spacing: $button-small-text-font-letter-spacing;
|
||||
line-height: $button-small-text-font-line-height;
|
||||
font-size: $button-small-text-font-size;
|
||||
height: $button-small-height;
|
||||
color: $button-overlay-text-color;
|
||||
box-shadow: none;
|
||||
border: 1px solid $button-outline-border-color;
|
||||
border-radius: $button-border-radius;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: $button-overlay-background-color-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.app-fab {
|
||||
height: $fab-standard-height;
|
||||
width: $fab-standard-width;
|
||||
position: fixed;
|
||||
z-index: 50;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border: 0;
|
||||
padding: $fab-standard-padding;
|
||||
letter-spacing: $fab-text-font-letter-spacing;
|
||||
line-height: $fab-text-font-line-height;
|
||||
font-size: $fab-text-font-size;
|
||||
font-weight: $fab-text-font-weight;
|
||||
box-shadow: $fab-shadow;
|
||||
background-color: $color-background-inverse;
|
||||
color: $color-text-active-inverse;
|
||||
cursor: pointer;
|
||||
|
||||
&__icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
// background-color: $fab-hover-background-color;
|
||||
}
|
||||
|
||||
&--focused {
|
||||
border-color: $fab-focused-border-color;
|
||||
border-width: $fab-focused-border-width;
|
||||
}
|
||||
|
||||
&--mini {
|
||||
height: $fab-mini-height;
|
||||
width: $fab-mini-width;
|
||||
border-radius: $fab-mini-border-radius;
|
||||
padding: $fab-mini-padding;
|
||||
}
|
||||
|
||||
&--extended {
|
||||
height: $fab-extended-height;
|
||||
border-radius: $fab-extended-border-radius;
|
||||
padding-right: $fab-extended-padding-right;
|
||||
padding-left: $fab-extended-padding-left;
|
||||
|
||||
& &__icon {
|
||||
margin-right: $fab-extended-icon-spacing;
|
||||
}
|
||||
}
|
||||
|
||||
.app-fab--extended.app-fab--mini {
|
||||
height: $fab-mini-height;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
opacity: $fab-disabled-opacity;
|
||||
}
|
||||
}
|
||||
|
||||
.app-fab-dialog {
|
||||
position: fixed;
|
||||
bottom: 44px;
|
||||
right: 0;
|
||||
// background-color: $color-background-medium;
|
||||
background-color: transparent;
|
||||
// border-radius: 12px;
|
||||
padding: 1rem;
|
||||
// border: 1px solid $color-border;
|
||||
// min-width: 300px;
|
||||
// box-shadow: $elevation-medium;
|
||||
|
||||
.icon-contained {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: $color-status-neutral-background;
|
||||
color: $color-status-neutral;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
#feedback_button {
|
||||
@include fab_button();
|
||||
}
|
||||
|
||||
#bug_button {
|
||||
@include fab_button();
|
||||
line-height: 38px;
|
||||
}
|
||||
|
||||
a#bug_button,
|
||||
a#bug_button:visited,
|
||||
a#bug_button:active {
|
||||
color: $button-overlay-text-color !important;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
@import 'fab';
|
||||
@@ -1 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/components/formfield' as formfield;
|
||||
@@ -1,104 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/components/textarea' as *;
|
||||
@use '@beeline/design-tokens/scss/tokens/components/formfield';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes';
|
||||
|
||||
.textarea-field {
|
||||
$px: 16px;
|
||||
|
||||
display: block;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
padding: $textarea-without-label-medium-text-margin-top $px $textarea-text-margin-bottom;
|
||||
height: $textarea-without-label-medium-height;
|
||||
background: formfield.$formfield-background-color;
|
||||
border: formfield.$formfield-border-width formfield.$formfield-border-style formfield.$formfield-border-color;
|
||||
border-radius: formfield.$formfield-border-radius;
|
||||
|
||||
& &__label {
|
||||
position: absolute;
|
||||
top: $textarea-with-label-text-margin-top;
|
||||
left: $px;
|
||||
transition: font-size 300ms ease-out;
|
||||
color: formfield.$formfield-label-color;
|
||||
}
|
||||
|
||||
& textarea {
|
||||
height: $textarea-medium-text-height;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
resize: none;
|
||||
background-color: transparent;
|
||||
color: themes.$color-text-active;
|
||||
}
|
||||
|
||||
& &__resizer {
|
||||
position: absolute;
|
||||
bottom: $textarea-resizer-margin-bottom;
|
||||
right: $textarea-resizer-margin-right;
|
||||
rotate: -45deg;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
|
||||
&:hover {
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: flex;
|
||||
background-color: darkslategrey;
|
||||
width: 10px;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: flex;
|
||||
position: relative;
|
||||
right: -3px;
|
||||
bottom: -2px;
|
||||
background-color: darkslategrey;
|
||||
width: 4px;
|
||||
height: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&.textarea-field--with-label {
|
||||
height: $textarea-with-label-medium-height;
|
||||
padding-top: $textarea-with-label-text-margin-top;
|
||||
|
||||
&.textarea-field--floated .textarea-field__label {
|
||||
top: $textarea-medium-label-floated-margin-top;
|
||||
line-height: $textarea-small-label-floated-font-line-height;
|
||||
font-size: $textarea-small-label-floated-font-size;
|
||||
}
|
||||
|
||||
&.textarea-field--small {
|
||||
height: $textarea-with-label-small-height;
|
||||
padding-top: $textarea-small-label-margin-top;
|
||||
top: $textarea-small-label-floated-margin-top;
|
||||
|
||||
& .textarea-field__label {
|
||||
font-size: $textarea-small-label-font-size;
|
||||
font-weight: $textarea-small-label-font-weight;
|
||||
letter-spacing: $textarea-small-label-font-letter-spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.textarea-field--focused {
|
||||
border-color: formfield.$formfield-border-color-focus;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&.textarea-field--small {
|
||||
padding-top: $textarea-without-label-small-text-margin-top;
|
||||
height: $textarea-without-label-small-height;
|
||||
|
||||
& textarea {
|
||||
height: $textarea-small-text-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/components/textfield' as *;
|
||||
@use '@beeline/design-tokens/scss/tokens/globals';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes' as theme;
|
||||
|
||||
.textfield {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: globals.$size-control-height-medium;
|
||||
border: globals.$size-border-width-regular solid transparent;
|
||||
padding-top: $textfield-without-label-medium-text-margin-vertical;
|
||||
padding-bottom: $textfield-without-label-medium-text-margin-vertical;
|
||||
background-color: theme.$color-control-background;
|
||||
border-radius: 12px; // globals.$size-border-radius-x6;
|
||||
|
||||
&:hover {
|
||||
background-color: theme.$color-control-background-hover;
|
||||
}
|
||||
|
||||
// input
|
||||
input {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
// label
|
||||
&--with-label {
|
||||
// padding-top: $textfield-with-label-medium-text-margin-top;
|
||||
// padding-bottom: $textfield-with-label-medium-text-margin-bottom;
|
||||
}
|
||||
|
||||
&__label {
|
||||
// padding-top: $textfield-medium-label-margin-top;
|
||||
|
||||
&.textfield__label--floated {
|
||||
// padding-top: $textfield-medium-label-margin-top-floated;
|
||||
}
|
||||
}
|
||||
|
||||
// small
|
||||
&--small {
|
||||
height: globals.$size-control-height-small;
|
||||
}
|
||||
|
||||
// large
|
||||
&--large {
|
||||
height: globals.$size-control-height-large;
|
||||
padding-top: $textfield-without-label-large-text-margin-vertical;
|
||||
padding-bottom: $textfield-without-label-large-text-margin-vertical;
|
||||
|
||||
&.textfield--with-label {
|
||||
padding-top: $textfield-with-label-large-text-margin-top;
|
||||
padding-bottom: $textfield-with-label-large-text-margin-bottom;
|
||||
}
|
||||
|
||||
.textfield__label {
|
||||
padding-top: $textfield-large-label-margin-top;
|
||||
|
||||
&.textfield__label--floated {
|
||||
padding-top: $textfield-large-label-margin-top-floated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// states
|
||||
&--focused {
|
||||
border-color: theme.$color-border-focus;
|
||||
background-color: theme.$color-background-base;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/themes' as *;
|
||||
|
||||
.app-bg-status-error {
|
||||
background-color: $color-status-error-background;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.app-cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
@import 'text';
|
||||
@import 'color';
|
||||
@import 'media';
|
||||
@import 'common';
|
||||
@@ -1,80 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/themes' as theme;
|
||||
@use 'src/assets/scss/app/mixins' as mixins;
|
||||
|
||||
@mixin truncate {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@mixin truncate-lines($lines: 2) {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: $lines;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.app-text-active {
|
||||
color: theme.$color-text-active;
|
||||
}
|
||||
|
||||
.app-text-inactive {
|
||||
color: theme.$color-text-inactive;
|
||||
}
|
||||
|
||||
// styles for hyperlinks
|
||||
.app-link {
|
||||
color: theme.$color-text-active;
|
||||
|
||||
&:hover {
|
||||
color: theme.$color-text-active;
|
||||
}
|
||||
}
|
||||
|
||||
.app-text-caption {
|
||||
@include mixins.text-caption;
|
||||
}
|
||||
|
||||
.app-text-truncate {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
// Sizes
|
||||
.app-text-size-subtitle-3 {
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.app-text-size-body-3 {
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
letter-spacing: 0.2px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
// Colors
|
||||
.app-text-color-caption {
|
||||
color: theme.$color-text-inactive;
|
||||
}
|
||||
.app-text-warning {
|
||||
color: theme.$color-status-warning;
|
||||
}
|
||||
.app-text-info {
|
||||
color: theme.$color-status-info;
|
||||
}
|
||||
.app-text-error {
|
||||
color: theme.$color-status-error;
|
||||
}
|
||||
.app-text-success {
|
||||
color: theme.$color-status-success;
|
||||
}
|
||||
|
||||
// Breaks
|
||||
.app-break-keep-all {
|
||||
word-break: keep-all;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
@import 'spinner';
|
||||
@@ -1,6 +0,0 @@
|
||||
@use 'src/assets/scss/app/variables' as v;
|
||||
|
||||
.app-loading-screen {
|
||||
display: block;
|
||||
height: calc(100vh - v.$app-header-height);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
@forward 'text';
|
||||
@@ -1,29 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/themes' as theme;
|
||||
|
||||
%text_caption {
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
@mixin text_caption {
|
||||
@extend %text_caption;
|
||||
color: theme.$color-text-inactive;
|
||||
}
|
||||
|
||||
@mixin text_caption_error {
|
||||
@extend %text_caption;
|
||||
color: theme.$color-status-error;
|
||||
}
|
||||
|
||||
@mixin text_body_2 {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
@mixin truncate {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/themes';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/dark';
|
||||
|
||||
:root {
|
||||
@include themes.theme();
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
@include themes.theme(dark.$theme);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/font-face' with (
|
||||
$font-path-beeline-sans: '../fonts/beeline-sans'
|
||||
);
|
||||
@@ -1,40 +0,0 @@
|
||||
// @use "@beeline/design-tokens/scss/iconfont/iconfont" with (
|
||||
// $font-path-iconfont: '../fonts/iconfont'
|
||||
// );
|
||||
@use '@beeline/design-tokens/scss/iconfont/icons';
|
||||
|
||||
@font-face {
|
||||
font-display: block;
|
||||
font-family: 'BeelineIcons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src:
|
||||
url('../fonts/iconfont/BeelineIcons.woff2') format('woff2'),
|
||||
url('../fonts/iconfont/BeelineIcons.woff') format('woff'),
|
||||
url('../fonts/iconfont/BeelineIcons.ttf') format('ttf');
|
||||
}
|
||||
|
||||
.beeline-icons {
|
||||
font-family: 'BeelineIcons', sans-serif;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px; /* Preferred icon size */
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
white-space: nowrap;
|
||||
direction: ltr;
|
||||
|
||||
/* Support for all WebKit browsers. */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
/* Support for Safari and Chrome. */
|
||||
text-rendering: optimizeLegibility;
|
||||
|
||||
/* Support for Firefox. */
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Support for IE. */
|
||||
font-feature-settings: 'liga';
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
@import 'fonts';
|
||||
@import 'icons';
|
||||
@import 'base';
|
||||
@import 'scrollbar';
|
||||
@@ -1,29 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/themes';
|
||||
|
||||
html {
|
||||
scrollbar-color: themes.$color-text-inactive transparent;
|
||||
}
|
||||
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: themes.$color-text-inactive;
|
||||
border: 4px solid transparent;
|
||||
background-clip: content-box;
|
||||
border-radius: 100px;
|
||||
min-height: 28px;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
@use 'sass:map';
|
||||
@use '@beeline/design-tokens/scss/tokens/globals';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/light';
|
||||
@use '@beeline/design-tokens/scss/tokens/components/button';
|
||||
|
||||
// Colors
|
||||
$primary: globals.$color-background-brand;
|
||||
$background: map.get(light.$theme, 'color-background-base');
|
||||
$info: map.get(light.$theme, 'color-status-info');
|
||||
$success: map.get(light.$theme, 'color-status-success');
|
||||
$warning: map.get(light.$theme, 'color-status-warning');
|
||||
$danger: map.get(light.$theme, 'color-status-error');
|
||||
|
||||
// Typography
|
||||
$family-primary: globals.$font-family-text;
|
||||
$title-color: map.get(light.$theme, 'color-text-active');
|
||||
$subtitle-color: map.get(light.$theme, 'color-text-inactive');
|
||||
|
||||
// Radius
|
||||
$radius: globals.$size-border-radius-x6;
|
||||
|
||||
// Buttons
|
||||
$button-padding-vertical: globals.$size-spacing-x4;
|
||||
$button-padding-horizontal: globals.$size-spacing-x5;
|
||||
$button-background-color: button.$button-plain-background-color;
|
||||
$button-focus-border-color: map.get(light.$theme, 'color-background-base-focused');
|
||||
$button-disabled-opacity: button.$button-opacity-disabled;
|
||||
|
||||
// Box
|
||||
$box-radius: $radius;
|
||||
$box-shadow: globals.$elevation-low;
|
||||
$box-padding: globals.$size-spacing-x6;
|
||||
$box-link-hover-shadow: globals.$elevation-medium;
|
||||
$box-link-active-shadow: globals.$elevation-medium;
|
||||
|
||||
// Form
|
||||
$input-color: map.get(light.$theme, 'color-text-active');
|
||||
$input-background-color: map.get(light.$theme, 'color-control-background');
|
||||
$label-color: map.get(light.$theme, 'color-text-inactive');
|
||||
$label-weight: globals.$font-weight-caption;
|
||||
|
||||
// Table
|
||||
$table-color: map.get(light.$theme, 'color-text-active');
|
||||
$table-background-color: map.get(light.$theme, 'color-background-base');
|
||||
$table-cell-border: 1px solid map.get(light.$theme, 'color-control-background');
|
||||
$table-cell-padding: 16px 16px;
|
||||
$table-cell-heading-color: map.get(light.$theme, 'color-text-active');
|
||||
$table-head-cell-border-width: 1px;
|
||||
|
||||
@import 'bulma/bulma';
|
||||
@import 'overrides';
|
||||
@@ -1,12 +0,0 @@
|
||||
@use 'sass:map';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/dark';
|
||||
|
||||
.box {
|
||||
.dark-theme & {
|
||||
background-color: map.get(dark.$theme, 'color-background-low');
|
||||
|
||||
&:hover {
|
||||
background-color: map.get(dark.$theme, 'color-background-medium');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/components/button' as button;
|
||||
@use '@beeline/design-tokens/scss/tokens/themes';
|
||||
|
||||
.button {
|
||||
font-weight: button.$button-medium-text-font-weight;
|
||||
color: themes.$color-text-active;
|
||||
|
||||
&:hover {
|
||||
color: themes.$color-text-active;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/themes';
|
||||
|
||||
.dropdown {
|
||||
&-content {
|
||||
background-color: themes.$color-background-medium;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
color: themes.$color-text-active;
|
||||
|
||||
&:hover {
|
||||
background-color: themes.$color-background-base-hover;
|
||||
color: themes.$color-text-active;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
@import 'button';
|
||||
@import 'title';
|
||||
@import 'box';
|
||||
@import 'form';
|
||||
@import 'table';
|
||||
@import 'dropdown';
|
||||
@import 'text';
|
||||
@@ -1,42 +0,0 @@
|
||||
@use 'sass:map';
|
||||
@use '@beeline/design-tokens/scss/tokens/globals';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/light';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/dark';
|
||||
@use '@beeline/design-tokens/scss/tokens/components/button';
|
||||
|
||||
.table {
|
||||
.dark-theme & {
|
||||
color: map.get(dark.$theme, 'color-text-active');
|
||||
background-color: map.get(dark.$theme, 'color-background-base');
|
||||
|
||||
td,
|
||||
th {
|
||||
border: 1px solid map.get(dark.$theme, 'color-control-background');
|
||||
}
|
||||
|
||||
th {
|
||||
color: map.get(dark.$theme, 'color-text-active');
|
||||
}
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: globals.$font-weight-medium;
|
||||
}
|
||||
|
||||
tbody {
|
||||
tr {
|
||||
&:last-child {
|
||||
td,
|
||||
th {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/tokens/themes' as *;
|
||||
|
||||
code {
|
||||
background-color: $color-background-base;
|
||||
}
|
||||
|
||||
pre {
|
||||
color: $color-text-active;
|
||||
background-color: $color-background-base;
|
||||
}
|
||||
|
||||
.help {
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
@use '@beeline/design-tokens/scss/mixin' as bee;
|
||||
|
||||
.title {
|
||||
@include bee.h3();
|
||||
|
||||
&.is-1 {
|
||||
@include bee.h1();
|
||||
}
|
||||
&.is-2 {
|
||||
@include bee.h2();
|
||||
}
|
||||
&.is-3 {
|
||||
@include bee.h3();
|
||||
}
|
||||
&.is-4 {
|
||||
@include bee.h4();
|
||||
}
|
||||
&.is-5 {
|
||||
@include bee.h5();
|
||||
}
|
||||
&.is-6 {
|
||||
@include bee.h6();
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
@include bee.subtitle2();
|
||||
|
||||
&.is-1 {
|
||||
@include bee.subtitle1();
|
||||
}
|
||||
&.is-2 {
|
||||
@include bee.subtitle2();
|
||||
}
|
||||
&.is-3 {
|
||||
@include bee.subtitle3();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
@import 'shared';
|
||||
@import 'select';
|
||||
@import 'input-textarea';
|
||||
@import 'title';
|
||||
@@ -1,46 +0,0 @@
|
||||
@use 'sass:map';
|
||||
// @use "@beeline/design-tokens/scss/tokens/globals";
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/light';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/dark';
|
||||
|
||||
%input-textarea {
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
color: map.get(light.$theme, 'color-text-active');
|
||||
background: map.get(light.$theme, 'color-control-background');
|
||||
|
||||
&:hover {
|
||||
border-color: map.get(light.$theme, 'color-border-focus');
|
||||
}
|
||||
|
||||
.dark-theme & {
|
||||
color: map.get(dark.$theme, 'color-text-active');
|
||||
background: map.get(dark.$theme, 'color-control-background');
|
||||
|
||||
&:hover {
|
||||
border-color: map.get(dark.$theme, 'color-border-focus');
|
||||
}
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&.is-focused,
|
||||
&:active,
|
||||
&.is-active {
|
||||
box-shadow: none !important;
|
||||
border-color: map.get(light.$theme, 'color-border-focus');
|
||||
background: map.get(light.$theme, 'color-background-base');
|
||||
|
||||
.dark-theme & {
|
||||
background: map.get(dark.$theme, 'color-background-base');
|
||||
border-color: map.get(dark.$theme, 'color-border-focus');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
@extend %input-textarea;
|
||||
}
|
||||
|
||||
.textarea {
|
||||
@extend %input-textarea;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
@use 'sass:map';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/dark';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/light';
|
||||
|
||||
.select {
|
||||
select {
|
||||
border: 1px solid transparent;
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
border: 1px solid map.get(light.$theme, 'color-border-focus');
|
||||
background-color: map.get(light.$theme, 'color-background-base');
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.is-multiple):not(.is-loading)::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&.is-focused,
|
||||
&:active,
|
||||
&.is-active {
|
||||
select {
|
||||
border: 1px solid map.get(light.$theme, 'color-border-focus');
|
||||
background-color: map.get(light.$theme, 'color-background-base');
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.dark-theme & {
|
||||
select {
|
||||
border: 1px solid map.get(dark.$theme, 'color-border-focus');
|
||||
background-color: map.get(dark.$theme, 'color-background-base');
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark-theme & {
|
||||
color: map.get(dark.$theme, 'color-text-active');
|
||||
|
||||
& select {
|
||||
color: map.get(dark.$theme, 'color-text-active');
|
||||
background-color: map.get(dark.$theme, 'color-control-background');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
@use 'sass:map';
|
||||
@use '@beeline/design-tokens/scss/tokens/globals';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/dark';
|
||||
|
||||
.label {
|
||||
line-height: globals.$font-line-height-caption;
|
||||
|
||||
.dark-theme & {
|
||||
color: map.get(dark.$theme, 'color-text-inactive');
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
@use 'sass:map';
|
||||
@use '@beeline/design-tokens/scss/tokens/themes/dark';
|
||||
|
||||
.title {
|
||||
.dark-theme & {
|
||||
color: map.get(dark.$theme, 'color-text-active');
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
.dark-theme & {
|
||||
color: map.get(dark.$theme, 'color-text-inactive');
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
@use 'beeline';
|
||||
@use 'bulma';
|
||||
@use 'app';
|
||||
|
||||
.proto {
|
||||
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
|
||||
color: #f55;
|
||||
}
|
||||
@@ -7,8 +7,8 @@ section_links:
|
||||
link: /compute/compute-how-to/compute-connect-inside.md
|
||||
description: Подключиться к виртуальной машине по SSH с помощью ключевой пары по внутреннему IP-адресу через джамп-хост
|
||||
- title: Подключение по SSH по логину и паролю
|
||||
link: /compute/compute-how-to/compute-connect-inside.md
|
||||
link: /compute/compute-how-to/compute-connect-pwd.md
|
||||
description: Подключиться к виртуальной машине по SSH с помощью логина и пароля
|
||||
---
|
||||
---
|
||||
|
||||
# Подключение к ВМ
|
||||
# Подключение к ВМ
|
||||
@@ -14,9 +14,9 @@ section_links:
|
||||
description: Резервирование, назначение IP-адреса виртуальной машине, удаление IP-адресов
|
||||
- title: Группы размещения
|
||||
link: /compute/compute-how-to/compute-affinity.md
|
||||
description: Создание правил размещения виртуальных машин на физических хостах, управление группами размещения
|
||||
---
|
||||
description: Создание правил размещения виртуальных машин на физических хостах, управление группами размещения
|
||||
---
|
||||
|
||||
# Виртуальные машины
|
||||
|
||||
Сервис **Виртуальные машины** предоставляет пользователям виртуальные машины.
|
||||
Сервис **Виртуальные машины** предоставляет пользователям виртуальные машины.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user