diff --git a/.gitignore b/.gitignore index e3e41bc4..e52da2f4 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,6 @@ .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent -.idea # Directories potentially created on remote AFP share .AppleDB @@ -99,6 +98,7 @@ dist/ # vitepress build output .vitepress/dist .vitepress/cache +.vitepress/.temp # Serverless directories .serverless/ @@ -112,5 +112,5 @@ src/examples/data.json src/tutorial/data.json draft.md -# IDEs +# folders created by IDE .idea diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..e941d13c --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-manager-strict=false diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..65a7d058 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +*.vue diff --git a/.vitepress/config.ts b/.vitepress/config.ts index a396863c..4ad5df83 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -1,10 +1,15 @@ +import fs from 'fs' +import path from 'path' +import { defineConfigWithTheme, type HeadConfig, type Plugin } from 'vitepress' import type { Config as ThemeConfig } from '@vue/theme' +import llmstxt from 'vitepress-plugin-llms' import baseConfig from '@vue/theme/config' import { defineConfigWithTheme } from 'vitepress' import fs from 'fs' import { headerPlugin } from './headerMdPlugin' import path from 'path' // import { textAdPlugin } from './textAdMdPlugin' +import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons' const nav: ThemeConfig['nav'] = [ { @@ -45,6 +50,7 @@ const nav: ThemeConfig['nav'] = [ text: '資源', items: [ { text: '合作伙伴', link: '/partners/' }, + { text: 'Developers', link: '/developers/' }, { text: '主題', link: '/ecosystem/themes' }, { text: 'UI 組件', link: 'https://ui-libs.vercel.app/' }, { @@ -125,9 +131,13 @@ const nav: ThemeConfig['nav'] = [ link: '/sponsor/' }, { - text: '合作伙伴', - link: '/partners/', - activeMatch: `^/partners/` + text: 'Experts', + badge: { text: 'NEW' }, + activeMatch: `^/(partners|developers)/`, + items: [ + { text: 'Partners', link: '/partners/' }, + { text: 'Developers', link: '/developers/', badge: { text: 'NEW' } } + ] } ] @@ -176,15 +186,15 @@ export const sidebar: ThemeConfig['sidebar'] = { link: '/guide/essentials/event-handling' }, { text: '表單輸入綁定', link: '/guide/essentials/forms' }, - { - text: '生命週期', - link: '/guide/essentials/lifecycle' - }, { text: '偵聽器', link: '/guide/essentials/watchers' }, { text: '模板引用', link: '/guide/essentials/template-refs' }, { text: '組件基礎', link: '/guide/essentials/component-basics' + }, + { + text: 'Lifecycle Hooks', + link: '/guide/essentials/lifecycle' } ] }, @@ -368,6 +378,10 @@ export const sidebar: ThemeConfig['sidebar'] = { { text: '依賴注入', link: '/api/composition-api-dependency-injection' + }, + { + text: 'Helpers', + link: '/api/composition-api-helpers' } ] }, @@ -417,6 +431,7 @@ export const sidebar: ThemeConfig['sidebar'] = { { text: '進階 API', items: [ + { text: 'Custom Elements', link: '/api/custom-elements' }, { text: '渲染函數', link: '/api/render-function' }, { text: '服務端渲染', link: '/api/ssr' }, { text: 'TypeScript 工具類型', link: '/api/utility-types' }, @@ -486,10 +501,6 @@ export const sidebar: ThemeConfig['sidebar'] = { text: '帶過渡動畫的列表', link: '/examples/#list-transition' }, - { - text: 'TodoMVC', - link: '/examples/#todomvc' - } ] }, { @@ -556,44 +567,29 @@ export const sidebar: ThemeConfig['sidebar'] = { ] } -const i18n: ThemeConfig['i18n'] = { - search: '搜索', - menu: '菜單', - toc: '本頁目錄', - returnToTop: '返回頂部', - appearance: '外觀', - previous: '前一篇', - next: '下一篇', - pageNotFound: '頁面未找到', - deadLink: { - before: '你打開了一個不存在的鏈接:', - after: '。' - }, - deadLinkReport: { - before: '不介意的話請提交到', - link: '這裡', - after: ',我們會跟進修復。' - }, - footerLicense: { - before: '', - after: '' - }, - ariaAnnouncer: { - before: '', - after: '已經加載完畢' - }, - ariaDarkMode: '切換深色模式', - ariaSkipToContent: '直接跳到內容', - ariaToC: '當前頁面的目錄', - ariaMainNav: '主導航', - ariaMobileNav: '移動版導航', - ariaSidebarNav: '側邊欄導航' +// Placeholder of the i18n config for @vuejs-translations. +// const i18n: ThemeConfig['i18n'] = { +// } + +function inlineScript(file: string): HeadConfig { + return [ + 'script', + {}, + fs.readFileSync( + path.resolve(__dirname, `./inlined-scripts/${file}`), + 'utf-8' + ) + ] } export default defineConfigWithTheme({ extends: baseConfig, - lang: 'zh-CN', + sitemap: { + hostname: 'https://zh-hk.vuejs.org' + }, + + lang: 'zh-HK', title: 'Vue.js', description: 'Vue.js - 漸進式的 JavaScript 框架', srcDir: 'src', @@ -624,17 +620,11 @@ export default defineConfigWithTheme({ 'link', { rel: 'preconnect', - href: 'https://sponsors.vuejs.org' + href: 'https://automation.vuejs.org' } ], - [ - 'script', - {}, - fs.readFileSync( - path.resolve(__dirname, './inlined-scripts/restorePreference.js'), - 'utf-8' - ) - ], + inlineScript('restorePreference.js'), + inlineScript('uwu.js'), [ 'script', { @@ -650,7 +640,8 @@ export default defineConfigWithTheme({ src: 'https://vueschool.io/banner.js?affiliate=vuejs&type=top', async: 'true' } - ] + ], + inlineScript('perfops.js') ], themeConfig: { @@ -666,7 +657,7 @@ export default defineConfigWithTheme({ }, { link: 'https://cn.vuejs.org', - text: '简体中文', + text: '簡體中文', repo: 'https://github.com/vuejs-translations/docs-zh-cn' }, { @@ -712,13 +703,23 @@ export default defineConfigWithTheme({ { link: 'https://ru.vuejs.org', text: 'Русский', - repo: 'https://github.com/translation-gang/docs-ru' + repo: 'https://github.com/vuejs-translations/docs-ru' }, { link: 'https://cs.vuejs.org', text: 'Čeština', repo: 'https://github.com/vuejs-translations/docs-cs' }, + { + link: 'https://zh-hk.vuejs.org', + text: '繁體中文', + repo: 'https://github.com/vuejs-translations/docs-zh-hk' + }, + { + link: 'https://pl.vuejs.org', + text: 'Polski', + repo: 'https://github.com/vuejs-translations/docs-pl', + }, { link: '/translations/', text: '幫助我們翻譯!', @@ -803,6 +804,7 @@ export default defineConfigWithTheme({ theme: 'github-dark', config(md) { md.use(headerPlugin) + .use(groupIconMdPlugin) // .use(textAdPlugin) } }, @@ -827,11 +829,40 @@ export default defineConfigWithTheme({ } }, build: { - minify: 'terser', chunkSizeWarningLimit: Infinity }, json: { stringify: true - } + }, + plugins: [ + llmstxt({ + ignoreFiles: [ + 'about/team/**/*', + 'about/team.md', + 'about/privacy.md', + 'about/coc.md', + 'developers/**/*', + 'ecosystem/themes.md', + 'examples/**/*', + 'partners/**/*', + 'sponsor/**/*', + 'index.md' + ], + customLLMsTxtTemplate: `\ +# Vue.js + +Vue.js - The Progressive JavaScript Framework + +## Table of Contents + +{toc}` + }) as Plugin, + groupIconVitePlugin({ + customIcon: { + cypress: 'vscode-icons:file-type-cypress', + 'testing library': 'logos:testing-library' + } + }) as Plugin + ] } }) diff --git a/.vitepress/inlined-scripts/perfops.js b/.vitepress/inlined-scripts/perfops.js new file mode 100644 index 00000000..381ff494 --- /dev/null +++ b/.vitepress/inlined-scripts/perfops.js @@ -0,0 +1,9 @@ +;((d) => { + window.rum = { key: 'a9efvfeu' } + var script = d.createElement('script') + script.src = '/rom3.min.js' + script.type = 'text/javascript' + script.defer = true + script.async = true + d.getElementsByTagName('head')[0].appendChild(script) +})(document) diff --git a/.vitepress/inlined-scripts/restorePreference.js b/.vitepress/inlined-scripts/restorePreference.js index 6eee492a..9338835a 100644 --- a/.vitepress/inlined-scripts/restorePreference.js +++ b/.vitepress/inlined-scripts/restorePreference.js @@ -8,6 +8,6 @@ restore('vue-docs-prefer-composition', 'prefer-composition', true) restore('vue-docs-prefer-sfc', 'prefer-sfc', true) - window.__VUE_BANNER_ID__ = 'vueconfus2024' + window.__VUE_BANNER_ID__ = 'viteconf2025' restore(`vue-docs-banner-${__VUE_BANNER_ID__}`, 'banner-dismissed') })() diff --git a/.vitepress/inlined-scripts/uwu.js b/.vitepress/inlined-scripts/uwu.js new file mode 100644 index 00000000..5b25107e --- /dev/null +++ b/.vitepress/inlined-scripts/uwu.js @@ -0,0 +1,3 @@ +if (location.search.includes('?uwu')) { + document.documentElement.classList.add('uwu') +} diff --git a/.vitepress/theme/components/Banner.vue b/.vitepress/theme/components/Banner.vue index f10b9e22..47242691 100644 --- a/.vitepress/theme/components/Banner.vue +++ b/.vitepress/theme/components/Banner.vue @@ -20,32 +20,54 @@ function dismiss() { } + @@ -60,29 +82,80 @@ html:not(.banner-dismissed) { height: var(--vt-banner-height); line-height: var(--vt-banner-height); text-align: center; - font-size: 13px; - font-weight: 600; - color: #fff; - background-color: var(--vt-c-green); - background: #11252b; + font-size: 12px; + color: white; + background: #262626; display: flex; justify-content: center; align-items: center; + overflow: hidden; } -.banner-dismissed .banner { - display: none; +.glow.glow--purple { + position: absolute; + bottom: -15%; + left: -75%; + width: 80%; + aspect-ratio: 1.5; + pointer-events: none; + border-radius: 100%; + background: linear-gradient(270deg, #7a23a1, #715ebde6 60% 80%, #bd34fe00); + filter: blur(15vw); + transform: none; + opacity: .6 +} + +.glow.glow--blue { + position: absolute; + bottom: -15%; + right: -40%; + width: 80%; + aspect-ratio: 1.5; + pointer-events: none; + border-radius: 100%; + background: linear-gradient(180deg, #61d9ff, #0000); + filter: blur(15vw); + transform: none; + opacity: .3 +} + +@media (min-width: 768px) { + .glow.glow--blue { + top: -15%; + right: -40%; + width: 80%; + } + + .glow.glow--purple { + bottom: -15%; + left: -40%; + width: 80%; + } +} + +@media (min-width: 1025px) { + .glow.glow--blue { + top: -15%; + right: -40%; + width: 80%; + } + + .glow.glow--purple { + bottom: -15%; + left: -40%; + width: 80%; + } } -a:hover { - text-decoration: underline; +.banner-dismissed .banner { + display: none; } button { position: absolute; right: 0; top: 0; - padding: 20px 10px; + padding: 5px 5px; } .close { @@ -94,32 +167,36 @@ button { .vt-banner-text { color: #fff; - font-size: 16px; + font-size: 12px; } -.vt-text-primary { - color: #75c05e; +.vt-main { + color: transparent; + background-image: linear-gradient(120deg, #b047ff 16%, #9499ff, #9499ff); + background-clip: text; } .vt-primary-action { - background: #75c05e; - color: #121c1a; - padding: 8px 15px; + background: radial-gradient(141.42% 141.42% at 100% 0%, #ffffff80, #fff0), radial-gradient(140.35% 140.35% at 100% 94.74%, #bd34fe, #bd34fe00), radial-gradient(89.94% 89.94% at 18.42% 15.79%, #41d1ff, #41d1ff00); + color: #fff; + padding: 4px 8px; border-radius: 5px; - font-size: 14px; - text-decoration: none; - margin: 0 20px; - font-weight: bold; -} -.vt-primary-action:hover { + font-size: 10px; text-decoration: none; - background: #5a9f45; + margin: 0 10px; + transition: all .2s ease-in-out; + + &:hover { + box-shadow: 0 1px #fffc inset; + } } + @media (max-width: 1280px) { .banner .vt-banner-text { font-size: 14px; } + .vt-tagline { display: none; } @@ -129,13 +206,16 @@ button { .vt-tagline { display: none; } + .vt-coupon { display: none; } + .vt-primary-action { margin: 0 10px; - padding: 7px 10px; + padding: 4px 8px; } + .vt-time-now { display: none; } @@ -146,4 +226,4 @@ button { display: none; } } - + \ No newline at end of file diff --git a/.vitepress/theme/components/CallToActionSection.vue b/.vitepress/theme/components/CallToActionSection.vue new file mode 100644 index 00000000..546d05fb --- /dev/null +++ b/.vitepress/theme/components/CallToActionSection.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/.vitepress/theme/components/CardList.vue b/.vitepress/theme/components/CardList.vue new file mode 100644 index 00000000..e29722bf --- /dev/null +++ b/.vitepress/theme/components/CardList.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/.vitepress/theme/components/Home.vue b/.vitepress/theme/components/Home.vue index f22e2879..2d387ff3 100644 --- a/.vitepress/theme/components/Home.vue +++ b/.vitepress/theme/components/Home.vue @@ -1,5 +1,5 @@ + + + + diff --git a/.vitepress/theme/components/ScrimbaLink.vue b/.vitepress/theme/components/ScrimbaLink.vue new file mode 100644 index 00000000..1fb60216 --- /dev/null +++ b/.vitepress/theme/components/ScrimbaLink.vue @@ -0,0 +1,59 @@ + + + \ No newline at end of file diff --git a/.vitepress/theme/components/SecurityUpdateBtn.vue b/.vitepress/theme/components/SecurityUpdateBtn.vue new file mode 100644 index 00000000..cfd8ceb2 --- /dev/null +++ b/.vitepress/theme/components/SecurityUpdateBtn.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/.vitepress/theme/components/SponsorsGroup.vue b/.vitepress/theme/components/SponsorsGroup.vue index aa5db5cd..03b1e66e 100644 --- a/.vitepress/theme/components/SponsorsGroup.vue +++ b/.vitepress/theme/components/SponsorsGroup.vue @@ -47,7 +47,7 @@ function track(interest?: boolean) { } function resolveList(data: SponsorData) { - let currentTier = data[props.tier] + let currentTier = data[props.tier] || [] // in aside, treat platinum+priority as special if (props.placement === 'aside') { if (props.tier === 'platinum') { @@ -66,13 +66,13 @@ function resolveList(data: SponsorData) {