Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE/upstream-sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
## Summary

-

## Upstream baseline

- Previous ref:
- Previous commit:
- Previous tag object:
- Target ref:
- Target commit:
- Target tag object:

## Frozen scope

- Included sync issues:
- Excluded pending or outside-target issues:

## Scope classification

| Handling | Count |
| ---------------------------- | ----: |
| Translated | 0 |
| Source-preserving | 0 |
| Structural sync | 0 |
| Intentional local divergence | 0 |
| Excluded | 0 |

## GitHub issues in scope

<details>
<summary>Issues in scope</summary>

| Issue | Title | Handling |
| ------------------ | ------------------ | ------------ |
| `Closed #<number>` | `<upstream title>` | `<handling>` |

</details>

## Intentional divergences and existing omissions

- Intentional local divergences:
- Existing omissions excluded from this sync:

## Validation

- [ ] `pnpm install --frozen-lockfile`
- [ ] Prettier checks for changed text files
- [ ] Strict line-parity checks for applicable files
- [ ] `pnpm run build`
- [ ] `git diff --check origin/main...HEAD`

## Checklist

- [ ] [번역 가이드](https://github.com/vitejs/docs-ko/blob/main/CONTRIBUTING.md)를 준수했습니다.
- [ ] 변경된 한국어 문장을 맞춤법 검사기로 확인했습니다.
- [ ] 모든 Target hunk를 분류했습니다.
- [ ] 직접 인용문, 코드 식별자, sample output을 의도치 않게 번역하지 않았습니다.
- [ ] 검증된 이슈에만 종료 키워드를 사용했습니다.
44 changes: 16 additions & 28 deletions .vitepress/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import path from 'node:path'
import fs from 'node:fs'
import type { DefaultTheme, HeadConfig } from 'vitepress'
import { defineConfig } from 'vitepress'
import { transformerTwoslash } from '@shikijs/vitepress-twoslash'
Expand Down Expand Up @@ -37,7 +36,7 @@ const deployType = (() => {
}
})()

const viteVersion = '8.0.13'
const viteVersion = '8.1.5'
const viteMajorVersion = 8

const versionLinks = ((): DefaultTheme.NavItemWithLink[] => {
Expand Down Expand Up @@ -83,17 +82,6 @@ const versionLinks = ((): DefaultTheme.NavItemWithLink[] => {
}
})()

function inlineScript(file: string): HeadConfig {
return [
'script',
{},
fs.readFileSync(
path.resolve(import.meta.dirname, `./inlined-scripts/${file}`),
'utf-8',
),
]
}

const config = defineConfig({
title: 'Vite',
lang: 'ko',
Expand All @@ -113,7 +101,6 @@ const config = defineConfig({
{ rel: 'alternate', type: 'application/rss+xml', href: '/blog.rss' },
],
['link', { rel: 'preconnect', href: 'https://fonts.googleapis.com' }],
inlineScript('banner.js'),
[
'link',
{
Expand Down Expand Up @@ -147,15 +134,6 @@ const config = defineConfig({
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
['meta', { name: 'twitter:site', content: '@vite_js' }],
['meta', { name: 'theme-color', content: '#646cff' }],
[
'script',
{
src: 'https://cdn.usefathom.com/script.js',
'data-site': 'CBDFBSLI',
'data-spa': 'auto',
defer: '',
},
],
],

locales: {
Expand All @@ -171,12 +149,10 @@ const config = defineConfig({

themeConfig: {
variant: 'vite',
logo: '/logo.svg',

banner: {
id: 'viteplus-alpha',
text: 'Vite+ Alpha 발표: 오픈 소스. 통합. 차세대.',
url: 'https://voidzero.dev/posts/announcing-vite-plus-alpha?utm_source=vite&utm_content=top_banner',
id: 'cloudflare-supports-vite',
text: `Cloudflare가 Vite의 사명을 지원합니다`,
url: '/blog/cloudflare-supports-vite',
},

editLink: {
Expand Down Expand Up @@ -266,6 +242,10 @@ const config = defineConfig({
{ text: '블로그', link: '/blog' },
{ text: '릴리스', link: '/releases' },
{ text: '감사의 말', link: '/acknowledgements' },
{
text: '행동 강령',
link: 'https://github.com/vitejs/.github/blob/main/CODE_OF_CONDUCT.md',
},
{
text: '플러그인 레지스트리',
link: 'https://registry.vite.dev/plugins',
Expand Down Expand Up @@ -619,6 +599,14 @@ const config = defineConfig({
},
},
vite: {
resolve: {
alias: {
'@components/oss/TopBanner.vue': path.resolve(
import.meta.dirname,
'theme/components/TopBanner.vue',
),
},
},
plugins: [
groupIconVitePlugin({
customIcon: {
Expand Down
186 changes: 186 additions & 0 deletions .vitepress/theme/components/TopBanner.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
<script setup lang="ts">
import { computed, inject } from 'vue'
import { useData } from 'vitepress'
import { Icon } from '@iconify/vue'
import {
themeContextKey,
type BannerConfig,
} from '@voidzero-dev/vitepress-theme'

const { theme, frontmatter } = useData()

const isMarketingPage = computed(() => {
const layout = frontmatter.value.layout
return layout === 'home' || (layout && layout !== 'doc' && layout !== 'page')
})

const bannerConfig = computed<BannerConfig | undefined>(
() => theme.value.banner,
)
const showBanner = computed(
() => bannerConfig.value?.text && bannerConfig.value?.url,
)
const bannerText = computed(() => bannerConfig.value?.text || '')
const bannerUrl = computed(() => bannerConfig.value?.url || '#')
const { footerBg, monoIcon, logoAlt } = inject(themeContextKey)!

const linkTarget = computed(() => {
if (bannerConfig.value?.target) {
return bannerConfig.value.target
}

const url = bannerConfig.value?.url || ''
return url.startsWith('http') ? '_blank' : '_self'
})

const linkRel = computed(() => {
return linkTarget.value === '_blank' ? 'noopener noreferrer' : undefined
})

function dismiss() {
document.documentElement.classList.add('banner-dismissed')
localStorage.setItem(
`banner-dismissed-${bannerConfig.value?.id || ''}`,
'true',
)
}
</script>

<template>
<div
v-if="showBanner"
:class="
isMarketingPage
? 'top-banner hidden md:block relative w-full overflow-hidden'
: 'top-banner hidden md:block relative w-full overflow-hidden lg:fixed lg:top-0 lg:left-0 lg:right-0 lg:z-60'
"
data-theme="dark"
>
<a
:href="bannerUrl"
:target="linkTarget"
:rel="linkRel"
class="group block relative w-full no-underline text-white"
>
<img
:src="footerBg"
alt=""
aria-hidden="true"
class="banner-background absolute inset-0 size-full object-cover"
loading="eager"
/>
<div class="banner-gradient absolute inset-0" aria-hidden="true" />
<div class="banner-shine absolute inset-0" aria-hidden="true" />

<div class="relative z-10 w-full h-10 flex px-4">
<div class="flex items-center gap-2 w-full max-w-360 mx-auto px-4">
<img
:src="monoIcon"
:alt="`${logoAlt} icon`"
class="size-5 shrink-0 hidden sm:block drop-shadow-md/70"
/>

<span
class="text-xs translate-y-px font-medium font-mono leading-snug tracking-wide uppercase whitespace-nowrap overflow-hidden text-ellipsis text-shadow-md/50"
>
{{ bannerText }}
</span>

<svg
class="shrink-0 transition-transform duration-200 group-hover:translate-x-1"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<rect width="20" height="20" rx="4" fill="#08060D" />
<rect
x="0.5"
y="0.5"
width="19"
height="19"
rx="3.5"
stroke="white"
stroke-opacity="0.15"
/>
<path
d="M10 6L14 10L10 14"
stroke="white"
stroke-width="1.2"
stroke-linejoin="round"
/>
<path
d="M14 10L6 10"
stroke="white"
stroke-width="1.2"
stroke-linejoin="round"
/>
</svg>
</div>
</div>
</a>

<button
@click="dismiss"
aria-label="배너 닫기"
class="absolute right-2 top-1/2 -translate-y-1/2 z-20 p-2 text-white hover:opacity-70 transition-opacity"
type="button"
>
<Icon icon="lucide:x" class="size-5 cursor-pointer" aria-hidden="true" />
</button>
</div>
</template>

<style>
:root.banner-dismissed .top-banner {
display: none;
}

.banner-gradient {
background:
linear-gradient(
90deg,
rgba(140, 0, 255, 0.54) 0%,
rgba(140, 0, 255, 0.54) 38%,
rgba(186, 24, 154, 0.58) 48%,
rgba(244, 86, 58, 0.86) 60%,
rgba(250, 116, 68, 0.98) 64%,
rgba(250, 116, 68, 0.98) 100%
),
radial-gradient(circle at 9% 50%, rgba(162, 0, 255, 0.7), transparent 24%),
radial-gradient(circle at 74% 50%, rgba(250, 116, 68, 0.9), transparent 36%);
mix-blend-mode: color;
}

.banner-shine {
background:
linear-gradient(
90deg,
rgba(8, 6, 13, 0.68),
rgba(8, 6, 13, 0.18) 44%,
rgba(8, 6, 13, 0.24)
),
linear-gradient(
180deg,
rgba(255, 255, 255, 0.18),
rgba(255, 255, 255, 0.02)
);
}

.banner-background {
filter: saturate(1.22) contrast(1.08);
object-position: center 52%;
}

@media (min-width: 768px) {
.top-banner {
--vp-banner-height: 40px;
}

:root:has(.top-banner):not(.banner-dismissed) {
--vp-banner-height: 40px;
}
}
</style>
Loading