Initial commit

This commit is contained in:
Thehrz 2023-02-05 18:11:34 +08:00 committed by GitHub
commit 37b3f75f36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 12109 additions and 0 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
node_modules
dist

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

3
.eslintrc Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "@antfu"
}

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: antfu

96
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,96 @@
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3
with:
node-version: 16.x
cache: pnpm
- name: Install
run: pnpm install
- name: Lint
run: pnpm run lint
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3
with:
node-version: 16.x
cache: pnpm
- name: Install
run: pnpm install
- name: Typecheck
run: pnpm run typecheck
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [14.x, 16.x]
os: [ubuntu-latest]
fail-fast: false
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/
cache: pnpm
- run: pnpm install
- run: pnpm run test:unit
test-e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache
key: cypress-cache-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- uses: pnpm/action-setup@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/
cache: pnpm
- run: pnpm install
- name: Cypress PNPM Patch
run: cp pnpm-lock.yaml package-lock.json
- name: Cypress
uses: cypress-io/github-action@v4
with:
install-command: echo
build: pnpm run build
start: npx vite --port 3333

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
.DS_Store
.vite-ssg-dist
.vite-ssg-temp
*.local
dist
dist-ssr
node_modules
.idea/
*.log
cypress/downloads

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
shamefully-hoist=true
strict-peer-dependencies=false

13
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,13 @@
{
"recommendations": [
"antfu.iconify",
"antfu.unocss",
"antfu.vite",
"antfu.goto-alias",
"csstools.postcss",
"dbaeumer.vscode-eslint",
"vue.volar",
"lokalise.i18n-ally",
"streetsidesoftware.code-spell-checker"
]
}

15
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
"cSpell.words": ["Vitesse", "Vite", "unocss", "vitest", "vueuse", "pinia", "demi", "antfu", "iconify", "intlify", "vitejs", "unplugin", "pnpm"],
"i18n-ally.sourceLanguage": "en",
"i18n-ally.keystyle": "nested",
"i18n-ally.localesPaths": "locales",
"i18n-ally.sortKeys": true,
"prettier.enable": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"files.associations": {
"*.css": "postcss"
},
"editor.formatOnSave": false
}

18
Dockerfile Normal file
View File

@ -0,0 +1,18 @@
FROM node:16-alpine as build-stage
WORKDIR /app
RUN corepack enable
COPY .npmrc package.json pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \
pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-2021 Anthony Fu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

216
README.md Normal file
View File

@ -0,0 +1,216 @@
<p align='center'>
<img src='https://user-images.githubusercontent.com/11247099/154486817-f86b8f20-5463-4122-b6e9-930622e757f2.png' alt='Vitesse - Opinionated Vite Starter Template' width='600'/>
</p>
<p align='center'>
Mocking up web app with <b>Vitesse</b><sup><em>(speed)</em></sup><br>
</p>
<br>
<p align='center'>
<a href="https://vitesse.netlify.app/">Live Demo</a>
</p>
<br>
<p align='center'>
<b>English</b> | <a href="https://github.com/antfu/vitesse/blob/main/README.zh-CN.md">简体中文</a>
<!-- Contributors: Thanks for getting interested, however we DON'T accept new transitions to the README, thanks. -->
</p>
<br>
## Features
- ⚡️ [Vue 3](https://github.com/vuejs/core), [Vite](https://github.com/vitejs/vite), [pnpm](https://pnpm.io/), [esbuild](https://github.com/evanw/esbuild) - born with fastness
- 🗂 [File based routing](./src/pages)
- 📦 [Components auto importing](./src/components)
- 🍍 [State Management via Pinia](https://pinia.vuejs.org/)
- 📑 [Layout system](./src/layouts)
- 📲 [PWA](https://github.com/antfu/vite-plugin-pwa)
- 🎨 [UnoCSS](https://github.com/antfu/unocss) - the instant on-demand atomic CSS engine
- 😃 [Use icons from any icon sets with classes](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
- 🌍 [I18n ready](./locales)
- 🔎 [Component Preview](https://github.com/johnsoncodehk/vite-plugin-vue-component-preview)
- 🗒 [Markdown Support](https://github.com/antfu/vite-plugin-vue-markdown)
- 🔥 Use the [new `<script setup>` syntax](https://github.com/vuejs/rfcs/pull/227)
- 🤙🏻 [Reactivity Transform](https://vuejs.org/guide/extras/reactivity-transform.html) enabled
- 📥 [APIs auto importing](https://github.com/antfu/unplugin-auto-import) - use Composition API and others directly
- 🖨 Static-site generation (SSG) via [vite-ssg](https://github.com/antfu/vite-ssg)
- 🦔 Critical CSS via [critters](https://github.com/GoogleChromeLabs/critters)
- 🦾 TypeScript, of course
- ⚙️ Unit Testing with [Vitest](https://github.com/vitest-dev/vitest), E2E Testing with [Cypress](https://cypress.io/) on [GitHub Actions](https://github.com/features/actions)
- ☁️ Deploy on Netlify, zero-config
<br>
## Pre-packed
### UI Frameworks
- [UnoCSS](https://github.com/antfu/unocss) - The instant on-demand atomic CSS engine.
### Icons
- [Iconify](https://iconify.design) - use icons from any icon sets [🔍Icônes](https://icones.netlify.app/)
- [Pure CSS Icons via UnoCSS](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
### Plugins
- [Vue Router](https://github.com/vuejs/router)
- [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) - file system based routing
- [`vite-plugin-vue-layouts`](https://github.com/JohnCampionJr/vite-plugin-vue-layouts) - layouts for pages
- [Pinia](https://pinia.vuejs.org) - Intuitive, type safe, light and flexible Store for Vue using the composition api
- [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components) - components auto import
- [`unplugin-auto-import`](https://github.com/antfu/unplugin-auto-import) - Directly use Vue Composition API and others without importing
- [`unplugin-vue-macros`](https://github.com/sxzz/unplugin-vue-macros) - Explore and extend more macros and syntax sugar to Vue.
- [`vite-plugin-pwa`](https://github.com/antfu/vite-plugin-pwa) - PWA
- [`vite-plugin-vue-component-preview`](https://github.com/johnsoncodehk/vite-plugin-vue-component-preview) - Preview single component in VSCode
- [`vite-plugin-vue-markdown`](https://github.com/antfu/vite-plugin-vue-markdown) - Markdown as components / components in Markdown
- [`markdown-it-shiki`](https://github.com/antfu/markdown-it-shiki) - [Shiki](https://github.com/shikijs/shiki) for syntax highlighting
- [Vue I18n](https://github.com/intlify/vue-i18n-next) - Internationalization
- [`unplugin-vue-i18n`](https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n) - unplugin for Vue I18n
- [VueUse](https://github.com/antfu/vueuse) - collection of useful composition APIs
- [`vite-ssg-sitemap`](https://github.com/jbaubree/vite-ssg-sitemap) - Sitemap generator
- [`@vueuse/head`](https://github.com/vueuse/head) - manipulate document head reactively
- [`vite-plugin-vue-inspector`](https://github.com/webfansplz/vite-plugin-vue-inspector) - jump to local IDE source code while click the element of browser automatically
### Coding Style
- Use Composition API with [`<script setup>` SFC syntax](https://github.com/vuejs/rfcs/pull/227)
- [ESLint](https://eslint.org/) with [@antfu/eslint-config](https://github.com/antfu/eslint-config), single quotes, no semi.
### Dev tools
- [TypeScript](https://www.typescriptlang.org/)
- [Vitest](https://github.com/vitest-dev/vitest) - Unit testing powered by Vite
- [Cypress](https://cypress.io/) - E2E testing
- [pnpm](https://pnpm.js.org/) - fast, disk space efficient package manager
- [`vite-ssg`](https://github.com/antfu/vite-ssg) - Static-site generation
- [critters](https://github.com/GoogleChromeLabs/critters) - Critical CSS
- [Netlify](https://www.netlify.com/) - zero-config deployment
- [VS Code Extensions](./.vscode/extensions.json)
- [Vite](https://marketplace.visualstudio.com/items?itemName=antfu.vite) - Fire up Vite server automatically
- [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) - Vue 3 `<script setup>` IDE support
- [Iconify IntelliSense](https://marketplace.visualstudio.com/items?itemName=antfu.iconify) - Icon inline display and autocomplete
- [i18n Ally](https://marketplace.visualstudio.com/items?itemName=lokalise.i18n-ally) - All in one i18n support
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
## Variations
As this template is strongly opinionated, the following provides a curated list for community-maintained variations with different preferences and feature sets. Check them out as well. PR to add yours is also welcome!
###### Official
- [vitesse-lite](https://github.com/antfu/vitesse-lite) - Lightweight version of Vitesse
- [vitesse-nuxt3](https://github.com/antfu/vitesse-nuxt3) - Vitesse for Nuxt 3
- [vitesse-nuxt-bridge](https://github.com/antfu/vitesse-nuxt-bridge) - Vitesse for Nuxt 2 with Bridge
- [vitesse-webext](https://github.com/antfu/vitesse-webext) - WebExtension Vite starter template
###### Community
- [vitesse-ssr-template](https://github.com/frandiox/vitesse-ssr-template) by [@frandiox](https://github.com/frandiox) - Vitesse with SSR
- [vitailse](https://github.com/zynth17/vitailse) by [@zynth17](https://github.com/zynth17) - Like Vitesse but with TailwindCSS
- [vitesse-modernized-chrome-ext](https://github.com/xiaoluoboding/vitesse-modernized-chrome-ext) by [@xiaoluoboding](https://github.com/xiaoluoboding) - ⚡️ Modernized Chrome Extension Manifest V3 Vite Starter Template
- [vitesse-stackter-clean-architect](https://github.com/shamscorner/vitesse-stackter-clean-architect) by [@shamscorner](https://github.com/shamscorner) - A modular clean architecture pattern in vitesse template
- [vitesse-enterprise](https://github.com/FranciscoKloganB/vitesse-enterprise) by [@FranciscoKloganB](https://github.com/FranciscoKloganB) - Consistent coding styles regardless of team-size.
- [vitecamp](https://github.com/nekobc1998923/vitecamp) by [@nekobc1998923](https://github.com/nekobc1998923) - Like Vitesse but without SSG/SSR/File based routing, includes Element Plus
- [vitesse-h5](https://github.com/YunYouJun/vitesse-h5) by [@YunYouJun](https://github.com/YunYouJun) - Vitesse for Mobile
- [bat](https://github.com/olgam4/bat) by [@olgam4](https://github.com/olgam4) - Vitesse for SolidJS
- [vitesse-solid](https://github.com/xbmlz/vitesse-solid) by [@xbmlz](https://github.com/xbmlz) - Vitesse for SolidJS, build with [`SolidStart`](https://start.solidjs.com/), includes [UnoCSS](https://github.com/unocss/unocss) and [HopeUI](https://hope-ui.com/).
## Try it now!
> Vitesse requires Node >=14.18
### GitHub Template
[Create a repo from this template on GitHub](https://github.com/antfu/vitesse/generate).
### Clone to local
If you prefer to do it manually with the cleaner git history
```bash
npx degit antfu/vitesse my-vitesse-app
cd my-vitesse-app
pnpm i # If you don't have pnpm installed, run: npm install -g pnpm
```
## Checklist
When you use this template, try follow the checklist to update your info properly
- [ ] Change the author name in `LICENSE`
- [ ] Change the title in `App.vue`
- [ ] Change the hostname in `vite.config.ts`
- [ ] Change the favicon in `public`
- [ ] Remove the `.github` folder which contains the funding info
- [ ] Clean up the READMEs and remove routes
And, enjoy :)
## Usage
### Development
Just run and visit http://localhost:3333
```bash
pnpm dev
```
### Build
To build the App, run
```bash
pnpm build
```
And you will see the generated file in `dist` that ready to be served.
### Deploy on Netlify
Go to [Netlify](https://app.netlify.com/start) and select your clone, `OK` along the way, and your App will be live in a minute.
### Docker Production Build
First, build the vitesse image by opening the terminal in the project's root directory.
```bash
docker buildx build . -t vitesse:latest
```
Run the image and specify port mapping with the `-p` flag.
```bash
docker run --rm -it -p 8080:80 vitesse:latest
```
## Why
I have created several Vite apps recently. Setting the configs up is kinda the bottleneck for me to make the ideas simply come true within a very short time.
So I made this starter template for myself to create apps more easily, along with some good practices that I have learned from making those apps. It's strongly opinionated, but feel free to tweak it or even maintain your own forks. [(see community maintained variation forks)](#variations)

182
README.zh-CN.md Normal file
View File

@ -0,0 +1,182 @@
<p align='center'>
<img src='https://user-images.githubusercontent.com/11247099/154486817-f86b8f20-5463-4122-b6e9-930622e757f2.png' alt='Vitesse - Opinionated Vite Starter Template' width='600'/>
</p>
<p align='center'>
快速地<sup><em>Vitesse</em></sup> 创建 Web 应用
<br>
</p>
<br>
<p align='center'>
<a href="https://vitesse.netlify.app/">在线 Demo</a>
</p>
<br>
<p align='center'>
<a href="https://github.com/antfu/vitesse/blob/main/README.md">English</a> | <b>简体中文</b>
</p>
<br>
## 特性
- ⚡️ [Vue 3](https://github.com/vuejs/core), [Vite](https://github.com/vitejs/vite), [pnpm](https://pnpm.io/), [esbuild](https://github.com/evanw/esbuild) - 就是快!
- 🗂 [基于文件的路由](./src/pages)
- 📦 [组件自动化加载](./src/components)
- 🍍 [使用 Pinia 的状态管理](https://pinia.vuejs.org)
- 📑 [布局系统](./src/layouts)
- 📲 [PWA](https://github.com/antfu/vite-plugin-pwa)
- 🎨 [UnoCSS](https://github.com/unocss/unocss) - 高性能且极具灵活性的即时原子化 CSS 引擎
- 😃 [各种图标集为你所用](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
- 🌍 [I18n 国际化开箱即用](./locales)
- 🗒 [Markdown 支持](https://github.com/antfu/vite-plugin-vue-markdown)
- 🔥 使用 [新的 `<script setup>` 语法](https://github.com/vuejs/rfcs/pull/227)
- 🤙🏻 默认开启 [响应性语法糖](https://vuejs.org/guide/extras/reactivity-transform.html)
- 📥 [API 自动加载](https://github.com/antfu/unplugin-auto-import) - 直接使用 Composition API 无需引入
- 🖨 使用 [vite-ssg](https://github.com/antfu/vite-ssg) 进行服务端生成 (SSG)
- 🦔 使用 [critters](https://github.com/GoogleChromeLabs/critters) 的生成关键 CSS
- 🦾 TypeScript, 当然
- ⚙️ 结合 [GitHub Actions](https://github.com/features/actions),使用 [Vitest](https://github.com/vitest-dev/vitest) 进行单元测试, [Cypress](https://cypress.io/) 进行 E2E 测试
- ☁️ 零配置部署 Netlify
<br>
## 预配置
### UI 框架
- [UnoCSS](https://github.com/antfu/unocss) - 高性能且极具灵活性的即时原子化 CSS 引擎
### Icons
- [Iconify](https://iconify.design) - 使用任意的图标集,浏览:[🔍Icônes](https://icones.netlify.app/)
- [UnoCSS 的纯 CSS 图标方案](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
### 插件
- [Vue Router](https://github.com/vuejs/router)
- [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) - 以文件系统为基础的路由
- [`vite-plugin-vue-layouts`](https://github.com/JohnCampionJr/vite-plugin-vue-layouts) - 页面布局系统
- [Pinia](https://pinia.vuejs.org) - 直接的, 类型安全的, 使用 Composition API 的轻便灵活的 Vue 状态管理
- [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components) - 自动加载组件
- [`unplugin-auto-import`](https://github.com/antfu/unplugin-auto-import) - 直接使用 Composition API 等,无需导入
- [`vite-plugin-pwa`](https://github.com/antfu/vite-plugin-pwa) - PWA
- [`vite-plugin-vue-markdown`](https://github.com/antfu/vite-plugin-vue-markdown) - Markdown 作为组件,也可以让组件在 Markdown 中使用
- [`markdown-it-prism`](https://github.com/jGleitz/markdown-it-prism) - [Prism](https://prismjs.com/) 的语法高亮
- [`prism-theme-vars`](https://github.com/antfu/prism-theme-vars) - 利用 CSS 变量自定义 Prism.js 的主题
- [Vue I18n](https://github.com/intlify/vue-i18n-next) - 国际化
- [`unplugin-vue-i18n`](https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n) - Vue I18n 的 Vite 插件
- [VueUse](https://github.com/antfu/vueuse) - 实用的 Composition API 工具合集
- [`vite-ssg-sitemap`](https://github.com/jbaubree/vite-ssg-sitemap) - 站点地图生成器
- [`@vueuse/head`](https://github.com/vueuse/head) - 响应式地操作文档头信息
- [`vite-plugin-vue-inspector`](https://github.com/webfansplz/vite-plugin-vue-inspector) - 点击页面元素自动跳转到本地IDE对应的 Vue 组件
### 编码风格
- 使用 Composition API 地 [`<script setup>` SFC 语法](https://github.com/vuejs/rfcs/pull/227)
- [ESLint](https://eslint.org/) 配置为 [@antfu/eslint-config](https://github.com/antfu/eslint-config), 单引号, 无分号.
### 开发工具
- [TypeScript](https://www.typescriptlang.org/)
- [Vitest](https://github.com/vitest-dev/vitest) - 基于 Vite 的单元测试框架
- [Cypress](https://cypress.io/) - E2E 测试
- [pnpm](https://pnpm.js.org/) - 快, 节省磁盘空间的包管理器
- [`vite-ssg`](https://github.com/antfu/vite-ssg) - 服务端生成
- [critters](https://github.com/GoogleChromeLabs/critters) - 关键 CSS 生成器
- [Netlify](https://www.netlify.com/) - 零配置的部署
- [VS Code 扩展](./.vscode/extensions.json)
- [Vite](https://marketplace.visualstudio.com/items?itemName=antfu.vite) - 自动启动 Vite 服务器
- [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) - Vue 3 `<script setup>` IDE 支持
- [Iconify IntelliSense](https://marketplace.visualstudio.com/items?itemName=antfu.iconify) - 图标内联显示和自动补全
- [i18n Ally](https://marketplace.visualstudio.com/items?itemName=lokalise.i18n-ally) - 多合一的 I18n 支持
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
## 衍生项目
由于这个模板的业务场景非常的局限,下面提供了一个精心策划的列表,列出了社区维护的具有不同偏好和功能集的衍生项目。也可以看看他们。当然也欢迎你 PR 提供自己的项目!
###### 官方
- [vitesse-lite](https://github.com/antfu/vitesse-lite) - Vitesse 的轻量版本
- [vitesse-nuxt3](https://github.com/antfu/vitesse-nuxt3) - Vitesse 的 Nuxt 3 版本
- [vitesse-nuxt-bridge](https://github.com/antfu/vitesse-nuxt-bridge) - Vitesse 的 Nuxt2 桥接版本
- [vitesse-webext](https://github.com/antfu/vitesse-webext) - 开箱即用的浏览器扩展 vite 模板
###### 社区
[查看英文版](./README.md#community)
## 现在可以试试!
> Vitesse 需要 Node 版本 >=14.18
### GitHub 模板
[使用这个模板创建仓库](https://github.com/antfu/vitesse/generate).
### 克隆到本地
如果您更喜欢使用更干净的 git 历史记录手动执行此操作
```bash
npx degit antfu/vitesse my-vitesse-app
cd my-vitesse-app
pnpm i # 如果你没装过 pnpm, 可以先运行: npm install -g pnpm
```
## 清单
使用此模板时,请尝试按照清单正确更新您自己的信息
- [ ] 在 `LICENSE` 中改变作者名
- [ ] 在 `App.vue` 中改变标题
- [ ] 在 `vite.config.ts` 更改主机名
- [ ] 在 `public` 目录下改变favicon
- [ ] 移除 `.github` 文件夹中包含资助的信息
- [ ] 整理 README 并删除路由
紧接着, 享受吧 :)
## 使用
### 开发
只需要执行以下命令就可以在 http://localhost:3333 中看到
```bash
pnpm dev
```
### 构建
构建该应用只需要执行以下命令
```bash
pnpm build
```
然后你会看到用于发布的 `dist` 文件夹被生成。
### 部署到 Netlify
前往 [Netlify](https://app.netlify.com/start) 并选择你的仓库, 一路 `OK` 下去,稍等一下后,你的应用将被创建.

10
cypress.config.ts Normal file
View File

@ -0,0 +1,10 @@
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3333',
chromeWebSecurity: false,
specPattern: 'cypress/e2e/**/*.spec.*',
supportFile: false,
},
})

36
cypress/e2e/basic.spec.ts Normal file
View File

@ -0,0 +1,36 @@
context('Basic', () => {
beforeEach(() => {
cy.visit('/')
})
it('basic nav', () => {
cy.url()
.should('eq', 'http://localhost:3333/')
cy.contains('[Home Layout]')
.should('exist')
cy.get('#input')
.type('Vitesse{Enter}')
.url()
.should('eq', 'http://localhost:3333/hi/Vitesse')
cy.contains('[Default Layout]')
.should('exist')
cy.get('[btn]')
.click()
.url()
.should('eq', 'http://localhost:3333/')
})
it('markdown', () => {
cy.get('[title="About"]')
.click()
.url()
.should('eq', 'http://localhost:3333/about')
cy.get('.shiki')
.should('exist')
})
})

12
cypress/tsconfig.json Normal file
View File

@ -0,0 +1,12 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"types": [
"cypress"
]
},
"exclude": [],
"include": [
"**/*.ts"
]
}

22
index.html Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="apple-touch-icon" href="/pwa-192x192.png">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#00aba9">
<meta name="msapplication-TileColor" content="#00aba9">
<script>
(function () {
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
const setting = localStorage.getItem('vueuse-color-scheme') || 'auto'
if (setting === 'dark' || (prefersDark && setting !== 'light'))
document.documentElement.classList.toggle('dark', true)
})()
</script>
</head>
<body class="font-sans">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

7
locales/README.md Normal file
View File

@ -0,0 +1,7 @@
## i18n
This directory is to serve your locale translation files. YAML under this folder would be loaded automatically and register with their filenames as locale code.
Check out [`vue-i18n`](https://github.com/intlify/vue-i18n-next) for more details.
If you are using VS Code, [`i18n Ally`](https://github.com/lokalise/i18n-ally) is recommended to make the i18n experience better.

14
locales/ar.yml Normal file
View File

@ -0,0 +1,14 @@
button:
about: حول
back: رجوع
go: تجربة
home: الرئيسية
toggle_dark: التغيير إلى الوضع المظلم
toggle_langs: تغيير اللغة
intro:
desc: vite مثال لتطبيق
dynamic-route: عرض لتوجيهات ديناميكية
hi: مرحبا {name}
aka: معروف أيضا تحت مسمى
whats-your-name: ما إسمك؟
not-found: صفحة غير موجودة

14
locales/de.yml Normal file
View File

@ -0,0 +1,14 @@
button:
about: Über
back: Zurück
go: Los
home: Startseite
toggle_dark: Dunkelmodus umschalten
toggle_langs: Sprachen ändern
intro:
desc: Vite Startvorlage mit Vorlieben
dynamic-route: Demo einer dynamischen Route
hi: Hi, {name}!
aka: Auch bekannt als
whats-your-name: Wie heißt du?
not-found: Nicht gefunden

14
locales/en.yml Normal file
View File

@ -0,0 +1,14 @@
button:
about: About
back: Back
go: GO
home: Home
toggle_dark: Toggle dark mode
toggle_langs: Change languages
intro:
desc: Opinionated Vite Starter Template
dynamic-route: Demo of dynamic route
hi: Hi, {name}!
aka: Also known as
whats-your-name: What's your name?
not-found: Not found

14
locales/es.yml Normal file
View File

@ -0,0 +1,14 @@
button:
about: Acerca de
back: Atrás
go: Ir
home: Inicio
toggle_dark: Alternar modo oscuro
toggle_langs: Cambiar idiomas
intro:
desc: Plantilla de Inicio de Vite Dogmática
dynamic-route: Demo de ruta dinámica
hi: ¡Hola, {name}!
aka: También conocido como
whats-your-name: ¿Cómo te llamas?
not-found: No se ha encontrado

14
locales/fr.yml Normal file
View File

@ -0,0 +1,14 @@
button:
about: À propos
back: Retour
go: Essayer
home: Accueil
toggle_dark: Basculer en mode sombre
toggle_langs: Changer de langue
intro:
desc: Exemple d'application Vite
dynamic-route: Démo de route dynamique
hi: Salut, {name}!
aka: Aussi connu sous le nom de
whats-your-name: Comment t'appelles-tu ?
not-found: Page non trouvée

14
locales/id.yml Normal file
View File

@ -0,0 +1,14 @@
button:
about: Tentang
back: Kembali
go: Pergi
home: Beranda
toggle_dark: Ubah ke mode gelap
toggle_langs: Ubah bahasa
intro:
desc: Template awal vite
dynamic-route: Contoh rute dinamik
hi: Halo, {name}!
aka: Juga diketahui sebagai
whats-your-name: Siapa nama anda?
not-found: Tidak ditemukan

13
locales/it.yml Normal file
View File

@ -0,0 +1,13 @@
button:
about: Su di me
back: Indietro
go: Vai
home: Home
toggle_dark: Attiva/disattiva modalità scura
toggle_langs: Cambia lingua
intro:
desc: Modello per una Applicazione Vite
dynamic-route: Demo di rotta dinamica
hi: Ciao, {name}!
whats-your-name: Come ti chiami?
not-found: Non trovato

13
locales/ja.yml Normal file
View File

@ -0,0 +1,13 @@
button:
about: これは?
back: 戻る
go: 進む
home: ホーム
toggle_dark: ダークモード切り替え
toggle_langs: 言語切り替え
intro:
desc: 固執された Vite スターターテンプレート
dynamic-route: 動的ルートのデモ
hi: こんにちは、{name}!
whats-your-name: 君の名は。
not-found: 見つかりませんでした

14
locales/ka.yml Normal file
View File

@ -0,0 +1,14 @@
button:
about: შესახებ
back: უკან
go: დაწყება
home: მთავარი
toggle_dark: გადართე მუქი რეჟიმი
toggle_langs: ენის შეცვლა
intro:
desc: Opinionated Vite Starter Template
dynamic-route: დინამიური როუტინგის დემო
hi: გამარჯობა, {name}!
aka: ასევე ცნობილი როგორც
whats-your-name: რა გქვია?
not-found: ვერ მოიძებნა

13
locales/ko.yml Normal file
View File

@ -0,0 +1,13 @@
button:
about: 소개
back: 뒤로가기
go: 이동
home:
toggle_dark: 다크모드 토글
toggle_langs: 언어 변경
intro:
desc: Vite 애플리케이션 템플릿
dynamic-route: 다이나믹 라우트 데모
hi: 안녕, {name}!
whats-your-name: 이름이 뭐예요?
not-found: 찾을 수 없습니다

14
locales/pl.yml Normal file
View File

@ -0,0 +1,14 @@
button:
about: O nas
back: Wróć
go: WEJDŹ
home: Strona główna
toggle_dark: Ustaw tryb nocny
toggle_langs: Zmień język
intro:
desc: Opiniowany szablon startowy Vite
dynamic-route: Demonstracja dynamicznego route
hi: Cześć, {name}!
aka: Znany też jako
whats-your-name: Jak masz na imię?
not-found: Nie znaleziono

14
locales/pt-BR.yml Normal file
View File

@ -0,0 +1,14 @@
button:
about: Sobre
back: Voltar
go: Ir
home: Início
toggle_dark: Alternar modo escuro
toggle_langs: Mudar de idioma
intro:
desc: Modelo Opinativo de Partida de Vite
dynamic-route: Demonstração de rota dinâmica
hi: Olá, {name}!
aka: Também conhecido como
whats-your-name: Qual é o seu nome?
not-found: Não encontrado

13
locales/ru.yml Normal file
View File

@ -0,0 +1,13 @@
button:
about: О шаблоне
back: Назад
go: Перейти
home: Главная
toggle_dark: Включить темный режим
toggle_langs: Сменить язык
intro:
desc: Самостоятельный начальный шаблон Vite
dynamic-route: Демо динамического маршрута
hi: Привет, {name}!
whats-your-name: Как тебя зовут?
not-found: Не найден

14
locales/tr.yml Normal file
View File

@ -0,0 +1,14 @@
button:
about: Hakkımda
back: Geri
go: İLERİ
home: Anasayfa
toggle_dark: Karanlık modu değiştir
toggle_langs: Dilleri değiştir
intro:
desc: Görüşlü Vite Başlangıç Şablonu
dynamic-route: Dinamik rota demosu
hi: Merhaba, {name}!
aka: Ayrıca şöyle bilinir
whats-your-name: Adınız nedir?
not-found: Bulunamadı

13
locales/uk.yml Normal file
View File

@ -0,0 +1,13 @@
button:
about: Про шаблон
back: Назад
go: Перейти
home: Головна
toggle_dark: Переключити темний режим
toggle_langs: Змінити мову
intro:
desc: Самостійний початковий шаблон Vite
dynamic-route: Демо динамічного маршруту
hi: Привіт, {name}!
whats-your-name: Як тебе звати?
not-found: Не знайдено

13
locales/vi.yml Normal file
View File

@ -0,0 +1,13 @@
button:
about: Về
back: Quay lại
go: Đi
home: Khởi đầu
toggle_dark: Chuyển đổi chế độ tối
toggle_langs: Thay đổi ngôn ngữ
intro:
desc: Ý kiến cá nhân Vite Template để bắt đầu
dynamic-route: Bản giới thiệu về dynamic route
hi: Hi, {name}!
whats-your-name: Tên bạn là gì?
not-found: Không tìm thấy

14
locales/zh-CN.yml Normal file
View File

@ -0,0 +1,14 @@
button:
about: 关于
back: 返回
go: 确定
home: 首页
toggle_dark: 切换深色模式
toggle_langs: 切换语言
intro:
desc: 固执己见的 Vite 项目模板
dynamic-route: 动态路由演示
hi: 你好,{name}
aka: 也叫
whats-your-name: 输入你的名字
not-found: 未找到页面

16
netlify.toml Executable file
View File

@ -0,0 +1,16 @@
[build.environment]
NODE_VERSION = "16"
[build]
publish = "dist"
command = "pnpm run build"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[headers]]
for = "/manifest.webmanifest"
[headers.values]
Content-Type = "application/manifest+json"

66
package.json Normal file
View File

@ -0,0 +1,66 @@
{
"type": "module",
"private": true,
"packageManager": "pnpm@7.17.1",
"scripts": {
"build": "vite-ssg build",
"dev": "vite --port 3333 --open",
"lint": "eslint .",
"preview": "vite preview",
"preview-https": "serve dist",
"test": "vitest",
"test:e2e": "cypress open",
"test:unit": "vitest",
"typecheck": "vue-tsc --noEmit",
"up": "taze major -I"
},
"dependencies": {
"@unocss/reset": "^0.48.4",
"@vueuse/core": "^9.11.0",
"@vueuse/head": "^1.0.22",
"nprogress": "^0.2.0",
"pinia": "^2.0.29",
"vue": "^3.2.45",
"vue-demi": "^0.13.11",
"vue-i18n": "^9.2.2",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@antfu/eslint-config": "^0.34.1",
"@iconify-json/carbon": "^1.1.13",
"@intlify/unplugin-vue-i18n": "^0.8.1",
"@types/markdown-it-link-attributes": "^3.0.1",
"@types/nprogress": "^0.2.0",
"@vitejs/plugin-vue": "^4.0.0",
"@vue-macros/volar": "^0.5.15",
"@vue/test-utils": "^2.2.7",
"critters": "^0.0.16",
"cross-env": "^7.0.3",
"cypress": "^12.3.0",
"eslint": "^8.32.0",
"eslint-plugin-cypress": "^2.12.1",
"https-localhost": "^4.7.1",
"markdown-it-link-attributes": "^4.0.1",
"markdown-it-shiki": "^0.7.2",
"pnpm": "^7.25.1",
"shiki": "^0.12.1",
"taze": "^0.8.5",
"typescript": "^4.9.4",
"unocss": "^0.48.4",
"unplugin-auto-import": "^0.12.1",
"unplugin-vue-components": "^0.22.12",
"unplugin-vue-macros": "^1.4.2",
"vite": "^4.0.4",
"vite-plugin-inspect": "^0.7.14",
"vite-plugin-pages": "^0.28.0",
"vite-plugin-pwa": "^0.14.1",
"vite-plugin-vue-component-preview": "^0.3.4",
"vite-plugin-vue-inspector": "^3.2.1",
"vite-plugin-vue-layouts": "^0.7.0",
"vite-plugin-vue-markdown": "^0.22.2",
"vite-ssg": "^0.22.1",
"vite-ssg-sitemap": "^0.4.3",
"vitest": "^0.27.2",
"vue-tsc": "^1.0.24"
}
}

9676
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

3
public/_headers Normal file
View File

@ -0,0 +1,3 @@
/assets/*
cache-control: max-age=31536000
cache-control: immutable

3
public/favicon-dark.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.562 26L17.17 8.928l2.366-3.888L17.828 4L16 7.005L14.17 4l-1.708 1.04l2.366 3.888L4.438 26H2v2h28v-2zM16 10.85L25.22 26H17v-8h-2v8H6.78z" fill="white" />
</svg>

After

Width:  |  Height:  |  Size: 311 B

3
public/favicon.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.562 26L17.17 8.928l2.366-3.888L17.828 4L16 7.005L14.17 4l-1.708 1.04l2.366 3.888L4.438 26H2v2h28v-2zM16 10.85L25.22 26H17v-8h-2v8H6.78z" fill="#222" />
</svg>

After

Width:  |  Height:  |  Size: 310 B

BIN
public/pwa-192x192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
public/pwa-512x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,41 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2916 6015 c-93 -57 -173 -108 -178 -113 -6 -6 7 -36 33 -78 23 -38
86 -141 139 -229 54 -88 135 -221 180 -295 46 -74 94 -155 108 -180 14 -25 29
-52 35 -60 7 -12 -9 -45 -62 -130 -39 -63 -85 -140 -103 -170 -18 -30 -117
-194 -222 -365 -104 -170 -199 -326 -210 -346 -12 -19 -61 -102 -111 -183 -49
-81 -101 -166 -115 -189 -14 -23 -39 -64 -55 -90 -17 -27 -77 -126 -134 -220
-57 -95 -127 -210 -156 -257 -194 -315 -325 -533 -325 -541 0 -5 -4 -9 -10 -9
-5 0 -10 -4 -10 -9 0 -5 -55 -98 -121 -207 -247 -404 -403 -660 -416 -684 -8
-14 -58 -97 -112 -185 l-98 -160 -189 -2 c-104 -1 -225 -2 -269 -2 l-80 -1 1
-210 c0 -116 4 -213 8 -218 11 -11 6107 -9 6114 2 8 13 8 406 0 419 -4 7 -88
10 -265 9 l-259 -2 -50 77 c-27 43 -54 87 -60 98 -6 11 -62 103 -124 205 -62
102 -120 197 -129 212 -9 16 -85 142 -170 280 -85 139 -160 262 -165 273 -6
11 -13 22 -16 25 -3 3 -30 46 -59 95 -30 50 -102 169 -161 265 -59 96 -240
393 -402 660 -163 267 -371 609 -463 760 -92 151 -194 318 -225 370 -31 52
-101 167 -155 255 l-97 160 27 50 c16 27 32 55 36 61 5 5 38 59 74 120 36 60
69 116 74 124 5 8 75 122 155 253 81 131 144 242 141 247 -4 7 -114 76 -183
115 -10 6 -52 32 -95 58 -42 27 -81 46 -87 42 -8 -5 -94 -140 -140 -219 -19
-33 -221 -365 -246 -404 -15 -22 -18 -18 -111 135 -52 87 -123 203 -157 258
-67 108 -67 110 -111 184 -16 28 -34 51 -40 50 -5 0 -86 -47 -179 -104z m739
-1642 c319 -526 519 -854 637 -1046 43 -70 78 -130 78 -133 0 -2 5 -10 10 -17
6 -7 69 -109 140 -227 72 -118 134 -222 139 -230 5 -8 55 -89 111 -180 56 -91
105 -172 110 -180 9 -14 52 -84 270 -445 54 -88 135 -221 180 -295 46 -74 91
-148 100 -165 9 -16 31 -53 48 -81 18 -28 32 -54 32 -57 0 -3 -403 -6 -895 -5
l-895 0 0 81 c-1 45 -1 439 -1 875 l0 792 -37 1 c-57 1 -344 1 -374 0 l-27 -1
0 -832 c0 -458 0 -852 0 -875 l-1 -42 -895 1 c-492 0 -895 3 -895 5 0 9 115
198 122 201 5 2 8 7 8 12 0 5 23 46 51 92 28 46 78 128 112 183 33 55 70 116
82 135 12 19 132 215 265 435 133 220 266 438 295 485 65 105 206 338 220 362
6 10 172 284 370 608 198 325 387 635 420 690 33 55 62 100 65 100 3 0 73
-111 155 -247z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

26
src/App.vue Normal file
View File

@ -0,0 +1,26 @@
<script setup lang="ts">
// https://github.com/vueuse/head
// you can use this to manipulate the document head in any components,
// they will be rendered correctly in the html results with vite-ssg
useHead({
title: 'Vitesse',
meta: [
{ name: 'description', content: 'Opinionated Vite Starter Template' },
{
name: 'theme-color',
content: computed(() => isDark.value ? '#00aba9' : '#ffffff'),
},
],
link: [
{
rel: 'icon',
type: 'image/svg+xml',
href: computed(() => preferredDark.value ? '/favicon-dark.svg' : '/favicon.svg'),
},
],
})
</script>
<template>
<RouterView />
</template>

560
src/auto-imports.d.ts vendored Normal file
View File

@ -0,0 +1,560 @@
// Generated by 'unplugin-auto-import'
export {}
declare global {
const $$: typeof import('vue/macros')['$$']
const $: typeof import('vue/macros')['$']
const $computed: typeof import('vue/macros')['$computed']
const $customRef: typeof import('vue/macros')['$customRef']
const $ref: typeof import('vue/macros')['$ref']
const $shallowRef: typeof import('vue/macros')['$shallowRef']
const $toRef: typeof import('vue/macros')['$toRef']
const EffectScope: typeof import('vue')['EffectScope']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const computed: typeof import('vue')['computed']
const computedAsync: typeof import('@vueuse/core')['computedAsync']
const computedEager: typeof import('@vueuse/core')['computedEager']
const computedInject: typeof import('@vueuse/core')['computedInject']
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
const controlledRef: typeof import('@vueuse/core')['controlledRef']
const createApp: typeof import('vue')['createApp']
const createEventHook: typeof import('@vueuse/core')['createEventHook']
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
const customRef: typeof import('vue')['customRef']
const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
const effectScope: typeof import('vue')['effectScope']
const extendRef: typeof import('@vueuse/core')['extendRef']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
const inject: typeof import('vue')['inject']
const isDark: typeof import('./composables/dark')['isDark']
const isDefined: typeof import('@vueuse/core')['isDefined']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
const onLongPress: typeof import('@vueuse/core')['onLongPress']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
const preferredDark: typeof import('./composables/dark')['preferredDark']
const provide: typeof import('vue')['provide']
const reactify: typeof import('@vueuse/core')['reactify']
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
const reactive: typeof import('vue')['reactive']
const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
const reactivePick: typeof import('@vueuse/core')['reactivePick']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
const refDebounced: typeof import('@vueuse/core')['refDebounced']
const refDefault: typeof import('@vueuse/core')['refDefault']
const refThrottled: typeof import('@vueuse/core')['refThrottled']
const refWithControl: typeof import('@vueuse/core')['refWithControl']
const resolveComponent: typeof import('vue')['resolveComponent']
const resolveDirective: typeof import('vue')['resolveDirective']
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const syncRef: typeof import('@vueuse/core')['syncRef']
const syncRefs: typeof import('@vueuse/core')['syncRefs']
const templateRef: typeof import('@vueuse/core')['templateRef']
const throttledRef: typeof import('@vueuse/core')['throttledRef']
const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
const toRaw: typeof import('vue')['toRaw']
const toReactive: typeof import('@vueuse/core')['toReactive']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toggleDark: typeof import('./composables/dark')['toggleDark']
const triggerRef: typeof import('vue')['triggerRef']
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
const unref: typeof import('vue')['unref']
const unrefElement: typeof import('@vueuse/core')['unrefElement']
const until: typeof import('@vueuse/core')['until']
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery']
const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
const useArrayFind: typeof import('@vueuse/core')['useArrayFind']
const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex']
const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin']
const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
const useArraySome: typeof import('@vueuse/core')['useArraySome']
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
const useAttrs: typeof import('vue')['useAttrs']
const useBase64: typeof import('@vueuse/core')['useBase64']
const useBattery: typeof import('@vueuse/core')['useBattery']
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
const useCached: typeof import('@vueuse/core')['useCached']
const useClipboard: typeof import('@vueuse/core')['useClipboard']
const useCloned: typeof import('@vueuse/core')['useCloned']
const useColorMode: typeof import('@vueuse/core')['useColorMode']
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
const useCounter: typeof import('@vueuse/core')['useCounter']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVar: typeof import('@vueuse/core')['useCssVar']
const useCssVars: typeof import('vue')['useCssVars']
const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement']
const useCycleList: typeof import('@vueuse/core')['useCycleList']
const useDark: typeof import('@vueuse/core')['useDark']
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
const useDebounce: typeof import('@vueuse/core')['useDebounce']
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
const useDraggable: typeof import('@vueuse/core')['useDraggable']
const useDropZone: typeof import('@vueuse/core')['useDropZone']
const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
const useElementHover: typeof import('@vueuse/core')['useElementHover']
const useElementSize: typeof import('@vueuse/core')['useElementSize']
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
const useEventBus: typeof import('@vueuse/core')['useEventBus']
const useEventListener: typeof import('@vueuse/core')['useEventListener']
const useEventSource: typeof import('@vueuse/core')['useEventSource']
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
const useFavicon: typeof import('@vueuse/core')['useFavicon']
const useFetch: typeof import('@vueuse/core')['useFetch']
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
const useFocus: typeof import('@vueuse/core')['useFocus']
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
const useFps: typeof import('@vueuse/core')['useFps']
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useHead: typeof import('@vueuse/head')['useHead']
const useI18n: typeof import('vue-i18n')['useI18n']
const useIdle: typeof import('@vueuse/core')['useIdle']
const useImage: typeof import('@vueuse/core')['useImage']
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
const useInterval: typeof import('@vueuse/core')['useInterval']
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
const useLink: typeof import('vue-router')['useLink']
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
const useMemoize: typeof import('@vueuse/core')['useMemoize']
const useMemory: typeof import('@vueuse/core')['useMemory']
const useMounted: typeof import('@vueuse/core')['useMounted']
const useMouse: typeof import('@vueuse/core')['useMouse']
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
const useNetwork: typeof import('@vueuse/core')['useNetwork']
const useNow: typeof import('@vueuse/core')['useNow']
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
const useOnline: typeof import('@vueuse/core')['useOnline']
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
const useParallax: typeof import('@vueuse/core')['useParallax']
const usePermission: typeof import('@vueuse/core')['usePermission']
const usePointer: typeof import('@vueuse/core')['usePointer']
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
const useRafFn: typeof import('@vueuse/core')['useRafFn']
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
const useScroll: typeof import('@vueuse/core')['useScroll']
const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
const useShare: typeof import('@vueuse/core')['useShare']
const useSlots: typeof import('vue')['useSlots']
const useSorted: typeof import('@vueuse/core')['useSorted']
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
const useStepper: typeof import('@vueuse/core')['useStepper']
const useStorage: typeof import('@vueuse/core')['useStorage']
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
const useSupported: typeof import('@vueuse/core')['useSupported']
const useSwipe: typeof import('@vueuse/core')['useSwipe']
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
const useThrottle: typeof import('@vueuse/core')['useThrottle']
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
const useTimeout: typeof import('@vueuse/core')['useTimeout']
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
const useTitle: typeof import('@vueuse/core')['useTitle']
const useToNumber: typeof import('@vueuse/core')['useToNumber']
const useToString: typeof import('@vueuse/core')['useToString']
const useToggle: typeof import('@vueuse/core')['useToggle']
const useTransition: typeof import('@vueuse/core')['useTransition']
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
const useUserStore: typeof import('./stores/user')['useUserStore']
const useVModel: typeof import('@vueuse/core')['useVModel']
const useVModels: typeof import('@vueuse/core')['useVModels']
const useVibrate: typeof import('@vueuse/core')['useVibrate']
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
const watch: typeof import('vue')['watch']
const watchArray: typeof import('@vueuse/core')['watchArray']
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
const watchEffect: typeof import('vue')['watchEffect']
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
const watchOnce: typeof import('@vueuse/core')['watchOnce']
const watchPausable: typeof import('@vueuse/core')['watchPausable']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever']
}
// for vue template auto import
import { UnwrapRef } from 'vue'
declare module 'vue' {
interface ComponentCustomProperties {
readonly $$: UnwrapRef<typeof import('vue/macros')['$$']>
readonly $: UnwrapRef<typeof import('vue/macros')['$']>
readonly $computed: UnwrapRef<typeof import('vue/macros')['$computed']>
readonly $customRef: UnwrapRef<typeof import('vue/macros')['$customRef']>
readonly $ref: UnwrapRef<typeof import('vue/macros')['$ref']>
readonly $shallowRef: UnwrapRef<typeof import('vue/macros')['$shallowRef']>
readonly $toRef: UnwrapRef<typeof import('vue/macros')['$toRef']>
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>
readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
readonly computedEager: UnwrapRef<typeof import('@vueuse/core')['computedEager']>
readonly computedInject: UnwrapRef<typeof import('@vueuse/core')['computedInject']>
readonly computedWithControl: UnwrapRef<typeof import('@vueuse/core')['computedWithControl']>
readonly controlledComputed: UnwrapRef<typeof import('@vueuse/core')['controlledComputed']>
readonly controlledRef: UnwrapRef<typeof import('@vueuse/core')['controlledRef']>
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
readonly createEventHook: UnwrapRef<typeof import('@vueuse/core')['createEventHook']>
readonly createGlobalState: UnwrapRef<typeof import('@vueuse/core')['createGlobalState']>
readonly createInjectionState: UnwrapRef<typeof import('@vueuse/core')['createInjectionState']>
readonly createReactiveFn: UnwrapRef<typeof import('@vueuse/core')['createReactiveFn']>
readonly createSharedComposable: UnwrapRef<typeof import('@vueuse/core')['createSharedComposable']>
readonly createUnrefFn: UnwrapRef<typeof import('@vueuse/core')['createUnrefFn']>
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
readonly debouncedRef: UnwrapRef<typeof import('@vueuse/core')['debouncedRef']>
readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']>
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly h: UnwrapRef<typeof import('vue')['h']>
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
readonly inject: UnwrapRef<typeof import('vue')['inject']>
readonly isDark: UnwrapRef<typeof import('./composables/dark')['isDark']>
readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']>
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
readonly onBeforeRouteLeave: UnwrapRef<typeof import('vue-router')['onBeforeRouteLeave']>
readonly onBeforeRouteUpdate: UnwrapRef<typeof import('vue-router')['onBeforeRouteUpdate']>
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
readonly onClickOutside: UnwrapRef<typeof import('@vueuse/core')['onClickOutside']>
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
readonly onKeyStroke: UnwrapRef<typeof import('@vueuse/core')['onKeyStroke']>
readonly onLongPress: UnwrapRef<typeof import('@vueuse/core')['onLongPress']>
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
readonly onStartTyping: UnwrapRef<typeof import('@vueuse/core')['onStartTyping']>
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
readonly preferredDark: UnwrapRef<typeof import('./composables/dark')['preferredDark']>
readonly provide: UnwrapRef<typeof import('vue')['provide']>
readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
readonly reactiveComputed: UnwrapRef<typeof import('@vueuse/core')['reactiveComputed']>
readonly reactiveOmit: UnwrapRef<typeof import('@vueuse/core')['reactiveOmit']>
readonly reactivePick: UnwrapRef<typeof import('@vueuse/core')['reactivePick']>
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
readonly ref: UnwrapRef<typeof import('vue')['ref']>
readonly refAutoReset: UnwrapRef<typeof import('@vueuse/core')['refAutoReset']>
readonly refDebounced: UnwrapRef<typeof import('@vueuse/core')['refDebounced']>
readonly refDefault: UnwrapRef<typeof import('@vueuse/core')['refDefault']>
readonly refThrottled: UnwrapRef<typeof import('@vueuse/core')['refThrottled']>
readonly refWithControl: UnwrapRef<typeof import('@vueuse/core')['refWithControl']>
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
readonly resolveDirective: UnwrapRef<typeof import('vue')['resolveDirective']>
readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
readonly syncRef: UnwrapRef<typeof import('@vueuse/core')['syncRef']>
readonly syncRefs: UnwrapRef<typeof import('@vueuse/core')['syncRefs']>
readonly templateRef: UnwrapRef<typeof import('@vueuse/core')['templateRef']>
readonly throttledRef: UnwrapRef<typeof import('@vueuse/core')['throttledRef']>
readonly throttledWatch: UnwrapRef<typeof import('@vueuse/core')['throttledWatch']>
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
readonly toReactive: UnwrapRef<typeof import('@vueuse/core')['toReactive']>
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
readonly toggleDark: UnwrapRef<typeof import('./composables/dark')['toggleDark']>
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
readonly tryOnBeforeMount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeMount']>
readonly tryOnBeforeUnmount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeUnmount']>
readonly tryOnMounted: UnwrapRef<typeof import('@vueuse/core')['tryOnMounted']>
readonly tryOnScopeDispose: UnwrapRef<typeof import('@vueuse/core')['tryOnScopeDispose']>
readonly tryOnUnmounted: UnwrapRef<typeof import('@vueuse/core')['tryOnUnmounted']>
readonly unref: UnwrapRef<typeof import('vue')['unref']>
readonly unrefElement: UnwrapRef<typeof import('@vueuse/core')['unrefElement']>
readonly until: UnwrapRef<typeof import('@vueuse/core')['until']>
readonly useActiveElement: UnwrapRef<typeof import('@vueuse/core')['useActiveElement']>
readonly useArrayEvery: UnwrapRef<typeof import('@vueuse/core')['useArrayEvery']>
readonly useArrayFilter: UnwrapRef<typeof import('@vueuse/core')['useArrayFilter']>
readonly useArrayFind: UnwrapRef<typeof import('@vueuse/core')['useArrayFind']>
readonly useArrayFindIndex: UnwrapRef<typeof import('@vueuse/core')['useArrayFindIndex']>
readonly useArrayJoin: UnwrapRef<typeof import('@vueuse/core')['useArrayJoin']>
readonly useArrayMap: UnwrapRef<typeof import('@vueuse/core')['useArrayMap']>
readonly useArrayReduce: UnwrapRef<typeof import('@vueuse/core')['useArrayReduce']>
readonly useArraySome: UnwrapRef<typeof import('@vueuse/core')['useArraySome']>
readonly useAsyncQueue: UnwrapRef<typeof import('@vueuse/core')['useAsyncQueue']>
readonly useAsyncState: UnwrapRef<typeof import('@vueuse/core')['useAsyncState']>
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
readonly useBase64: UnwrapRef<typeof import('@vueuse/core')['useBase64']>
readonly useBattery: UnwrapRef<typeof import('@vueuse/core')['useBattery']>
readonly useBluetooth: UnwrapRef<typeof import('@vueuse/core')['useBluetooth']>
readonly useBreakpoints: UnwrapRef<typeof import('@vueuse/core')['useBreakpoints']>
readonly useBroadcastChannel: UnwrapRef<typeof import('@vueuse/core')['useBroadcastChannel']>
readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
readonly useCssVar: UnwrapRef<typeof import('@vueuse/core')['useCssVar']>
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
readonly useCurrentElement: UnwrapRef<typeof import('@vueuse/core')['useCurrentElement']>
readonly useCycleList: UnwrapRef<typeof import('@vueuse/core')['useCycleList']>
readonly useDark: UnwrapRef<typeof import('@vueuse/core')['useDark']>
readonly useDateFormat: UnwrapRef<typeof import('@vueuse/core')['useDateFormat']>
readonly useDebounce: UnwrapRef<typeof import('@vueuse/core')['useDebounce']>
readonly useDebounceFn: UnwrapRef<typeof import('@vueuse/core')['useDebounceFn']>
readonly useDebouncedRefHistory: UnwrapRef<typeof import('@vueuse/core')['useDebouncedRefHistory']>
readonly useDeviceMotion: UnwrapRef<typeof import('@vueuse/core')['useDeviceMotion']>
readonly useDeviceOrientation: UnwrapRef<typeof import('@vueuse/core')['useDeviceOrientation']>
readonly useDevicePixelRatio: UnwrapRef<typeof import('@vueuse/core')['useDevicePixelRatio']>
readonly useDevicesList: UnwrapRef<typeof import('@vueuse/core')['useDevicesList']>
readonly useDisplayMedia: UnwrapRef<typeof import('@vueuse/core')['useDisplayMedia']>
readonly useDocumentVisibility: UnwrapRef<typeof import('@vueuse/core')['useDocumentVisibility']>
readonly useDraggable: UnwrapRef<typeof import('@vueuse/core')['useDraggable']>
readonly useDropZone: UnwrapRef<typeof import('@vueuse/core')['useDropZone']>
readonly useElementBounding: UnwrapRef<typeof import('@vueuse/core')['useElementBounding']>
readonly useElementByPoint: UnwrapRef<typeof import('@vueuse/core')['useElementByPoint']>
readonly useElementHover: UnwrapRef<typeof import('@vueuse/core')['useElementHover']>
readonly useElementSize: UnwrapRef<typeof import('@vueuse/core')['useElementSize']>
readonly useElementVisibility: UnwrapRef<typeof import('@vueuse/core')['useElementVisibility']>
readonly useEventBus: UnwrapRef<typeof import('@vueuse/core')['useEventBus']>
readonly useEventListener: UnwrapRef<typeof import('@vueuse/core')['useEventListener']>
readonly useEventSource: UnwrapRef<typeof import('@vueuse/core')['useEventSource']>
readonly useEyeDropper: UnwrapRef<typeof import('@vueuse/core')['useEyeDropper']>
readonly useFavicon: UnwrapRef<typeof import('@vueuse/core')['useFavicon']>
readonly useFetch: UnwrapRef<typeof import('@vueuse/core')['useFetch']>
readonly useFileDialog: UnwrapRef<typeof import('@vueuse/core')['useFileDialog']>
readonly useFileSystemAccess: UnwrapRef<typeof import('@vueuse/core')['useFileSystemAccess']>
readonly useFocus: UnwrapRef<typeof import('@vueuse/core')['useFocus']>
readonly useFocusWithin: UnwrapRef<typeof import('@vueuse/core')['useFocusWithin']>
readonly useFps: UnwrapRef<typeof import('@vueuse/core')['useFps']>
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
readonly useHead: UnwrapRef<typeof import('@vueuse/head')['useHead']>
readonly useI18n: UnwrapRef<typeof import('vue-i18n')['useI18n']>
readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']>
readonly useInfiniteScroll: UnwrapRef<typeof import('@vueuse/core')['useInfiniteScroll']>
readonly useIntersectionObserver: UnwrapRef<typeof import('@vueuse/core')['useIntersectionObserver']>
readonly useInterval: UnwrapRef<typeof import('@vueuse/core')['useInterval']>
readonly useIntervalFn: UnwrapRef<typeof import('@vueuse/core')['useIntervalFn']>
readonly useKeyModifier: UnwrapRef<typeof import('@vueuse/core')['useKeyModifier']>
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
readonly useMediaControls: UnwrapRef<typeof import('@vueuse/core')['useMediaControls']>
readonly useMediaQuery: UnwrapRef<typeof import('@vueuse/core')['useMediaQuery']>
readonly useMemoize: UnwrapRef<typeof import('@vueuse/core')['useMemoize']>
readonly useMemory: UnwrapRef<typeof import('@vueuse/core')['useMemory']>
readonly useMounted: UnwrapRef<typeof import('@vueuse/core')['useMounted']>
readonly useMouse: UnwrapRef<typeof import('@vueuse/core')['useMouse']>
readonly useMouseInElement: UnwrapRef<typeof import('@vueuse/core')['useMouseInElement']>
readonly useMousePressed: UnwrapRef<typeof import('@vueuse/core')['useMousePressed']>
readonly useMutationObserver: UnwrapRef<typeof import('@vueuse/core')['useMutationObserver']>
readonly useNavigatorLanguage: UnwrapRef<typeof import('@vueuse/core')['useNavigatorLanguage']>
readonly useNetwork: UnwrapRef<typeof import('@vueuse/core')['useNetwork']>
readonly useNow: UnwrapRef<typeof import('@vueuse/core')['useNow']>
readonly useObjectUrl: UnwrapRef<typeof import('@vueuse/core')['useObjectUrl']>
readonly useOffsetPagination: UnwrapRef<typeof import('@vueuse/core')['useOffsetPagination']>
readonly useOnline: UnwrapRef<typeof import('@vueuse/core')['useOnline']>
readonly usePageLeave: UnwrapRef<typeof import('@vueuse/core')['usePageLeave']>
readonly useParallax: UnwrapRef<typeof import('@vueuse/core')['useParallax']>
readonly usePermission: UnwrapRef<typeof import('@vueuse/core')['usePermission']>
readonly usePointer: UnwrapRef<typeof import('@vueuse/core')['usePointer']>
readonly usePointerSwipe: UnwrapRef<typeof import('@vueuse/core')['usePointerSwipe']>
readonly usePreferredColorScheme: UnwrapRef<typeof import('@vueuse/core')['usePreferredColorScheme']>
readonly usePreferredContrast: UnwrapRef<typeof import('@vueuse/core')['usePreferredContrast']>
readonly usePreferredDark: UnwrapRef<typeof import('@vueuse/core')['usePreferredDark']>
readonly usePreferredLanguages: UnwrapRef<typeof import('@vueuse/core')['usePreferredLanguages']>
readonly usePreferredReducedMotion: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedMotion']>
readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
readonly useResizeObserver: UnwrapRef<typeof import('@vueuse/core')['useResizeObserver']>
readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
readonly useScreenOrientation: UnwrapRef<typeof import('@vueuse/core')['useScreenOrientation']>
readonly useScreenSafeArea: UnwrapRef<typeof import('@vueuse/core')['useScreenSafeArea']>
readonly useScriptTag: UnwrapRef<typeof import('@vueuse/core')['useScriptTag']>
readonly useScroll: UnwrapRef<typeof import('@vueuse/core')['useScroll']>
readonly useScrollLock: UnwrapRef<typeof import('@vueuse/core')['useScrollLock']>
readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']>
readonly useShare: UnwrapRef<typeof import('@vueuse/core')['useShare']>
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
readonly useSorted: UnwrapRef<typeof import('@vueuse/core')['useSorted']>
readonly useSpeechRecognition: UnwrapRef<typeof import('@vueuse/core')['useSpeechRecognition']>
readonly useSpeechSynthesis: UnwrapRef<typeof import('@vueuse/core')['useSpeechSynthesis']>
readonly useStepper: UnwrapRef<typeof import('@vueuse/core')['useStepper']>
readonly useStorage: UnwrapRef<typeof import('@vueuse/core')['useStorage']>
readonly useStorageAsync: UnwrapRef<typeof import('@vueuse/core')['useStorageAsync']>
readonly useStyleTag: UnwrapRef<typeof import('@vueuse/core')['useStyleTag']>
readonly useSupported: UnwrapRef<typeof import('@vueuse/core')['useSupported']>
readonly useSwipe: UnwrapRef<typeof import('@vueuse/core')['useSwipe']>
readonly useTemplateRefsList: UnwrapRef<typeof import('@vueuse/core')['useTemplateRefsList']>
readonly useTextDirection: UnwrapRef<typeof import('@vueuse/core')['useTextDirection']>
readonly useTextSelection: UnwrapRef<typeof import('@vueuse/core')['useTextSelection']>
readonly useTextareaAutosize: UnwrapRef<typeof import('@vueuse/core')['useTextareaAutosize']>
readonly useThrottle: UnwrapRef<typeof import('@vueuse/core')['useThrottle']>
readonly useThrottleFn: UnwrapRef<typeof import('@vueuse/core')['useThrottleFn']>
readonly useThrottledRefHistory: UnwrapRef<typeof import('@vueuse/core')['useThrottledRefHistory']>
readonly useTimeAgo: UnwrapRef<typeof import('@vueuse/core')['useTimeAgo']>
readonly useTimeout: UnwrapRef<typeof import('@vueuse/core')['useTimeout']>
readonly useTimeoutFn: UnwrapRef<typeof import('@vueuse/core')['useTimeoutFn']>
readonly useTimeoutPoll: UnwrapRef<typeof import('@vueuse/core')['useTimeoutPoll']>
readonly useTimestamp: UnwrapRef<typeof import('@vueuse/core')['useTimestamp']>
readonly useTitle: UnwrapRef<typeof import('@vueuse/core')['useTitle']>
readonly useToNumber: UnwrapRef<typeof import('@vueuse/core')['useToNumber']>
readonly useToString: UnwrapRef<typeof import('@vueuse/core')['useToString']>
readonly useToggle: UnwrapRef<typeof import('@vueuse/core')['useToggle']>
readonly useTransition: UnwrapRef<typeof import('@vueuse/core')['useTransition']>
readonly useUrlSearchParams: UnwrapRef<typeof import('@vueuse/core')['useUrlSearchParams']>
readonly useUserMedia: UnwrapRef<typeof import('@vueuse/core')['useUserMedia']>
readonly useUserStore: UnwrapRef<typeof import('./stores/user')['useUserStore']>
readonly useVModel: UnwrapRef<typeof import('@vueuse/core')['useVModel']>
readonly useVModels: UnwrapRef<typeof import('@vueuse/core')['useVModels']>
readonly useVibrate: UnwrapRef<typeof import('@vueuse/core')['useVibrate']>
readonly useVirtualList: UnwrapRef<typeof import('@vueuse/core')['useVirtualList']>
readonly useWakeLock: UnwrapRef<typeof import('@vueuse/core')['useWakeLock']>
readonly useWebNotification: UnwrapRef<typeof import('@vueuse/core')['useWebNotification']>
readonly useWebSocket: UnwrapRef<typeof import('@vueuse/core')['useWebSocket']>
readonly useWebWorker: UnwrapRef<typeof import('@vueuse/core')['useWebWorker']>
readonly useWebWorkerFn: UnwrapRef<typeof import('@vueuse/core')['useWebWorkerFn']>
readonly useWindowFocus: UnwrapRef<typeof import('@vueuse/core')['useWindowFocus']>
readonly useWindowScroll: UnwrapRef<typeof import('@vueuse/core')['useWindowScroll']>
readonly useWindowSize: UnwrapRef<typeof import('@vueuse/core')['useWindowSize']>
readonly watch: UnwrapRef<typeof import('vue')['watch']>
readonly watchArray: UnwrapRef<typeof import('@vueuse/core')['watchArray']>
readonly watchAtMost: UnwrapRef<typeof import('@vueuse/core')['watchAtMost']>
readonly watchDebounced: UnwrapRef<typeof import('@vueuse/core')['watchDebounced']>
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
readonly watchIgnorable: UnwrapRef<typeof import('@vueuse/core')['watchIgnorable']>
readonly watchOnce: UnwrapRef<typeof import('@vueuse/core')['watchOnce']>
readonly watchPausable: UnwrapRef<typeof import('@vueuse/core')['watchPausable']>
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
readonly watchThrottled: UnwrapRef<typeof import('@vueuse/core')['watchThrottled']>
readonly watchTriggerable: UnwrapRef<typeof import('@vueuse/core')['watchTriggerable']>
readonly watchWithFilter: UnwrapRef<typeof import('@vueuse/core')['watchWithFilter']>
readonly whenever: UnwrapRef<typeof import('@vueuse/core')['whenever']>
}
}

17
src/components.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
README: typeof import('./components/README.md')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
TheCounter: typeof import('./components/TheCounter.vue')['default']
TheFooter: typeof import('./components/TheFooter.vue')['default']
TheInput: typeof import('./components/TheInput.vue')['default']
}
}

10
src/components/README.md Normal file
View File

@ -0,0 +1,10 @@
## Components
Components in this dir will be auto-registered and on-demand, powered by [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components).
### Icons
You can use icons from almost any icon sets by the power of [Iconify](https://iconify.design/).
It will only bundle the icons you use. Check out [`unplugin-icons`](https://github.com/antfu/unplugin-icons) for more details.

View File

@ -0,0 +1,19 @@
<script setup lang="ts">
const props = defineProps<{
initial: number
}>()
const { count, inc, dec } = useCounter(props.initial)
</script>
<template>
<div>
{{ count }}
<button class="inc" @click="inc()">
+
</button>
<button class="dec" @click="dec()">
-
</button>
</div>
</template>

View File

@ -0,0 +1,33 @@
<script setup lang="ts">
const { t, availableLocales, locale } = useI18n()
const toggleLocales = () => {
// change to some real logic
const locales = availableLocales
locale.value = locales[(locales.indexOf(locale.value) + 1) % locales.length]
}
</script>
<template>
<nav text-xl mt-6>
<RouterLink class="icon-btn mx-2" to="/" :title="t('button.home')">
<div i-carbon-campsite />
</RouterLink>
<button class="icon-btn mx-2 !outline-none" :title="t('button.toggle_dark')" @click="toggleDark()">
<div i="carbon-sun dark:carbon-moon" />
</button>
<a class="icon-btn mx-2" :title="t('button.toggle_langs')" @click="toggleLocales()">
<div i-carbon-language />
</a>
<RouterLink class="icon-btn mx-2" to="/about" :title="t('button.about')">
<div i-carbon-dicom-overlay />
</RouterLink>
<a class="icon-btn mx-2" rel="noreferrer" href="https://github.com/antfu/vitesse" target="_blank" title="GitHub">
<div i-carbon-logo-github />
</a>
</nav>
</template>

View File

@ -0,0 +1,20 @@
<script setup lang="ts">
const { modelValue } = defineModel<{
modelValue: string
}>()
</script>
<template>
<input
id="input"
v-model="modelValue"
type="text"
v-bind="$attrs"
p="x-4 y-2"
w="250px"
text="center"
bg="transparent"
border="~ rounded gray-200 dark:gray-700"
outline="none active:none"
>
</template>

4
src/composables/dark.ts Normal file
View File

@ -0,0 +1,4 @@
// these APIs are auto-imported from @vueuse/core
export const isDark = useDark()
export const toggleDark = useToggle(isDark)
export const preferredDark = usePreferredDark()

18
src/layouts/404.vue Normal file
View File

@ -0,0 +1,18 @@
<script setup lang="ts">
const router = useRouter()
const { t } = useI18n()
</script>
<template>
<main p="x4 y10" text="center teal-700 dark:gray-200">
<div text-4xl>
<div i-carbon-warning inline-block />
</div>
<RouterView />
<div>
<button btn text-sm m="3 t8" @click="router.back()">
{{ t('button.back') }}
</button>
</div>
</main>
</template>

14
src/layouts/README.md Normal file
View File

@ -0,0 +1,14 @@
## Layouts
Vue components in this dir are used as layouts.
By default, `default.vue` will be used unless an alternative is specified in the route meta.
With [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) and [`vite-plugin-vue-layouts`](https://github.com/JohnCampionJr/vite-plugin-vue-layouts), you can specify the layout in the page's SFCs like this:
```html
<route lang="yaml">
meta:
layout: home
</route>
```

9
src/layouts/default.vue Normal file
View File

@ -0,0 +1,9 @@
<template>
<main class="px-4 py-10 text-center text-gray-700 dark:text-gray-200">
<RouterView />
<TheFooter />
<div class="mt-5 mx-auto text-center opacity-75 dark:opacity-50 text-sm">
[Default Layout]
</div>
</main>
</template>

9
src/layouts/home.vue Normal file
View File

@ -0,0 +1,9 @@
<template>
<main class="px-4 py-10 text-center text-gray-700 dark:text-gray-200">
<RouterView />
<TheFooter />
<div class="mt-5 mx-auto text-center opacity-75 dark:opacity-50 text-sm">
[Home Layout]
</div>
</main>
</template>

24
src/main.ts Normal file
View File

@ -0,0 +1,24 @@
import { ViteSSG } from 'vite-ssg'
import { setupLayouts } from 'virtual:generated-layouts'
import Previewer from 'virtual:vue-component-preview'
import App from './App.vue'
import type { UserModule } from './types'
import generatedRoutes from '~pages'
import '@unocss/reset/tailwind.css'
import './styles/main.css'
import 'uno.css'
const routes = setupLayouts(generatedRoutes)
// https://github.com/antfu/vite-ssg
export const createApp = ViteSSG(
App,
{ routes, base: import.meta.env.BASE_URL },
(ctx) => {
// install all modules under `modules/`
Object.values(import.meta.glob<{ install: UserModule }>('./modules/*.ts', { eager: true }))
.forEach(i => i.install?.(ctx))
ctx.app.use(Previewer)
},
)

11
src/modules/README.md Normal file
View File

@ -0,0 +1,11 @@
## Modules
A custom user module system. Place a `.ts` file with the following template, it will be installed automatically.
```ts
import { type UserModule } from '~/types'
export const install: UserModule = ({ app, router, isClient }) => {
// do something
}
```

25
src/modules/i18n.ts Normal file
View File

@ -0,0 +1,25 @@
import { createI18n } from 'vue-i18n'
import { type UserModule } from '~/types'
// Import i18n resources
// https://vitejs.dev/guide/features.html#glob-import
//
// Don't need this? Try vitesse-lite: https://github.com/antfu/vitesse-lite
const messages = Object.fromEntries(
Object.entries(
import.meta.glob<{ default: any }>('../../locales/*.y(a)?ml', { eager: true }))
.map(([key, value]) => {
const yaml = key.endsWith('.yaml')
return [key.slice(14, yaml ? -5 : -4), value.default]
}),
)
export const install: UserModule = ({ app }) => {
const i18n = createI18n({
legacy: false,
locale: 'en',
messages,
})
app.use(i18n)
}

14
src/modules/nprogress.ts Normal file
View File

@ -0,0 +1,14 @@
import NProgress from 'nprogress'
import { type UserModule } from '~/types'
export const install: UserModule = ({ isClient, router }) => {
if (isClient) {
router.beforeEach((to, from) => {
if (to.path !== from.path)
NProgress.start()
})
router.afterEach(() => {
NProgress.done()
})
}
}

17
src/modules/pinia.ts Normal file
View File

@ -0,0 +1,17 @@
import { createPinia } from 'pinia'
import { type UserModule } from '~/types'
// Setup Pinia
// https://pinia.vuejs.org/
export const install: UserModule = ({ isClient, initialState, app }) => {
const pinia = createPinia()
app.use(pinia)
// Refer to
// https://github.com/antfu/vite-ssg/blob/main/README.md#state-serialization
// for other serialization strategies.
if (isClient)
pinia.state.value = (initialState.pinia) || {}
else
initialState.pinia = pinia.state.value
}

14
src/modules/pwa.ts Normal file
View File

@ -0,0 +1,14 @@
import { type UserModule } from '~/types'
// https://github.com/antfu/vite-plugin-pwa#automatic-reload-when-new-content-available
export const install: UserModule = ({ isClient, router }) => {
if (!isClient)
return
router.isReady()
.then(async () => {
const { registerSW } = await import('virtual:pwa-register')
registerSW({ immediate: true })
})
.catch(() => {})
}

20
src/pages/README.md Normal file
View File

@ -0,0 +1,20 @@
## File-based Routing
Routes will be auto-generated for Vue files in this dir with the same file structure.
Check out [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) for more details.
### Path Aliasing
`~/` is aliased to `./src/` folder.
For example, instead of having
```ts
import { isDark } from '../../../../composables'
```
now, you can use
```ts
import { isDark } from '~/composables'
```

14
src/pages/[...all].vue Executable file
View File

@ -0,0 +1,14 @@
<script setup lang="ts">
const { t } = useI18n()
</script>
<template>
<div>
{{ t('not-found') }}
</div>
</template>
<route lang="yaml">
meta:
layout: 404
</route>

21
src/pages/about.md Normal file
View File

@ -0,0 +1,21 @@
---
title: About
---
<div class="text-center">
<!-- You can use Vue components inside markdown -->
<div i-carbon-dicom-overlay class="text-4xl -mb-6 m-auto" />
<h3>About</h3>
</div>
[Vitesse](https://github.com/antfu/vitesse) is an opinionated [Vite](https://github.com/vitejs/vite) starter template made by [@antfu](https://github.com/antfu) for mocking apps swiftly. With **file-based routing**, **components auto importing**, **markdown support**, I18n, PWA and uses **UnoCSS** for styling and icons.
```js
// syntax highlighting example
function vitesse() {
const foo = 'bar'
console.log(foo)
}
```
Check out the [GitHub repo](https://github.com/antfu/vitesse) for more details.

47
src/pages/hi/[name].vue Normal file
View File

@ -0,0 +1,47 @@
<script setup lang="ts">
const props = defineProps<{ name: string }>()
const router = useRouter()
const user = useUserStore()
const { t } = useI18n()
watchEffect(() => {
user.setNewName(props.name)
})
</script>
<template>
<div>
<div text-4xl>
<div i-carbon-pedestrian inline-block />
</div>
<p>
{{ t('intro.hi', { name: props.name }) }}
</p>
<p text-sm opacity-75>
<em>{{ t('intro.dynamic-route') }}</em>
</p>
<template v-if="user.otherNames.length">
<p text-sm mt-4>
<span opacity-75>{{ t('intro.aka') }}:</span>
<ul>
<li v-for="otherName in user.otherNames" :key="otherName">
<RouterLink :to="`/hi/${otherName}`" replace>
{{ otherName }}
</RouterLink>
</li>
</ul>
</p>
</template>
<div>
<button
btn m="3 t6" text-sm
@click="router.back()"
>
{{ t('button.back') }}
</button>
</div>
</div>
</template>

56
src/pages/index.vue Normal file
View File

@ -0,0 +1,56 @@
<script setup lang="ts">
defineOptions({
name: 'IndexPage',
})
const user = useUserStore()
const name = $ref(user.savedName)
const router = useRouter()
const go = () => {
if (name)
router.push(`/hi/${encodeURIComponent(name)}`)
}
const { t } = useI18n()
</script>
<template>
<div>
<div text-4xl>
<div i-carbon-campsite inline-block />
</div>
<p>
<a rel="noreferrer" href="https://github.com/antfu/vitesse" target="_blank">
Vitesse
</a>
</p>
<p>
<em text-sm opacity-75>{{ t('intro.desc') }}</em>
</p>
<div py-4 />
<TheInput
v-model="name"
placeholder="What's your name?"
autocomplete="false"
@keydown.enter="go"
/>
<label class="hidden" for="input">{{ t('intro.whats-your-name') }}</label>
<div>
<button
btn m-3 text-sm
:disabled="!name"
@click="go"
>
{{ t('button.go') }}
</button>
</div>
</div>
</template>
<route lang="yaml">
meta:
layout: home
</route>

16
src/shims.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
declare interface Window {
// extend the window
}
// with vite-plugin-vue-markdown, markdown files can be treated as Vue components
declare module '*.md' {
import { type DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare module '*.vue' {
import { type DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

34
src/stores/user.ts Normal file
View File

@ -0,0 +1,34 @@
import { acceptHMRUpdate, defineStore } from 'pinia'
export const useUserStore = defineStore('user', () => {
/**
* Current name of the user.
*/
const savedName = ref('')
const previousNames = ref(new Set<string>())
const usedNames = computed(() => Array.from(previousNames.value))
const otherNames = computed(() => usedNames.value.filter(name => name !== savedName.value))
/**
* Changes the current name of the user and saves the one that was used
* before.
*
* @param name - new name to set
*/
function setNewName(name: string) {
if (savedName.value)
previousNames.value.add(savedName.value)
savedName.value = name
}
return {
setNewName,
otherNames,
savedName,
}
})
if (import.meta.hot)
import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot))

29
src/styles/main.css Executable file
View File

@ -0,0 +1,29 @@
@import './markdown.css';
html,
body,
#app {
height: 100%;
margin: 0;
padding: 0;
}
html.dark {
background: #121212;
color-scheme: dark;
}
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: rgb(13,148,136);
opacity: 0.75;
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 2px;
}

28
src/styles/markdown.css Normal file
View File

@ -0,0 +1,28 @@
.prose pre:not(.shiki) {
padding: 0;
}
.prose .shiki {
font-family: 'DM Mono', monospace;
font-size: 1.2em;
line-height: 1.4;
}
.prose img {
width: 100%;
}
.shiki-light {
background: #f8f8f8 !important;
}
.shiki-dark {
background: #0e0e0e !important;
}
html.dark .shiki-light {
display: none;
}
html:not(.dark) .shiki-dark {
display: none;
}

3
src/types.ts Normal file
View File

@ -0,0 +1,3 @@
import { type ViteSSGContext } from 'vite-ssg'
export type UserModule = (ctx: ViteSSGContext) => void

View File

@ -0,0 +1,3 @@
// Vitest Snapshot v1
exports[`TheCounter.vue > should render 1`] = `"<div>10 <button class=\\"inc\\"> + </button><button class=\\"dec\\"> - </button></div>"`;

7
test/basic.test.ts Normal file
View File

@ -0,0 +1,7 @@
import { describe, expect, it } from 'vitest'
describe('tests', () => {
it('should works', () => {
expect(1 + 1).toEqual(2)
})
})

28
test/component.test.ts Normal file
View File

@ -0,0 +1,28 @@
import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import TheCounter from '../src/components/TheCounter.vue'
describe('TheCounter.vue', () => {
it('should render', () => {
const wrapper = mount(TheCounter, { props: { initial: 10 } })
expect(wrapper.text()).toContain('10')
expect(wrapper.html()).toMatchSnapshot()
})
it('should be interactive', async () => {
const wrapper = mount(TheCounter, { props: { initial: 0 } })
expect(wrapper.text()).toContain('0')
expect(wrapper.find('.inc').exists()).toBe(true)
expect(wrapper.find('.dec').exists()).toBe(true)
await wrapper.get('.inc').trigger('click')
expect(wrapper.text()).toContain('1')
await wrapper.get('.dec').trigger('click')
expect(wrapper.text()).toContain('0')
})
})

38
tsconfig.json Normal file
View File

@ -0,0 +1,38 @@
{
"compilerOptions": {
"baseUrl": ".",
"module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"jsx": "preserve",
"skipLibCheck": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"noUnusedLocals": true,
"strictNullChecks": true,
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"types": [
"vitest",
"vite/client",
"vue/ref-macros",
"vite-plugin-pages/client",
"vite-plugin-vue-component-preview/client",
"vite-plugin-vue-layouts/client",
"vite-plugin-pwa/client",
"unplugin-vue-macros/macros-global"
],
"paths": {
"~/*": ["src/*"]
}
},
"vueCompilerOptions": {
"plugins": [
"@vue-macros/volar/define-model",
"@vue-macros/volar/define-slots"
]
},
"exclude": ["dist", "node_modules", "cypress"]
}

38
unocss.config.ts Normal file
View File

@ -0,0 +1,38 @@
import {
defineConfig,
presetAttributify,
presetIcons,
presetTypography,
presetUno,
presetWebFonts,
transformerDirectives,
transformerVariantGroup,
} from 'unocss'
export default defineConfig({
shortcuts: [
['btn', 'px-4 py-1 rounded inline-block bg-teal-700 text-white cursor-pointer hover:bg-teal-800 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
['icon-btn', 'inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-teal-600'],
],
presets: [
presetUno(),
presetAttributify(),
presetIcons({
scale: 1.2,
warn: true,
}),
presetTypography(),
presetWebFonts({
fonts: {
sans: 'DM Sans',
serif: 'DM Serif Display',
mono: 'DM Mono',
},
}),
],
transformers: [
transformerDirectives(),
transformerVariantGroup(),
],
safelist: 'prose prose-sm m-auto text-left'.split(' '),
})

168
vite.config.ts Normal file
View File

@ -0,0 +1,168 @@
import path from 'path'
import { defineConfig } from 'vite'
import Preview from 'vite-plugin-vue-component-preview'
import Vue from '@vitejs/plugin-vue'
import Pages from 'vite-plugin-pages'
import generateSitemap from 'vite-ssg-sitemap'
import Layouts from 'vite-plugin-vue-layouts'
import Components from 'unplugin-vue-components/vite'
import AutoImport from 'unplugin-auto-import/vite'
import Markdown from 'vite-plugin-vue-markdown'
import { VitePWA } from 'vite-plugin-pwa'
import VueI18n from '@intlify/unplugin-vue-i18n/vite'
import Inspect from 'vite-plugin-inspect'
import Inspector from 'vite-plugin-vue-inspector'
import LinkAttributes from 'markdown-it-link-attributes'
import Unocss from 'unocss/vite'
import Shiki from 'markdown-it-shiki'
import VueMacros from 'unplugin-vue-macros/vite'
export default defineConfig({
resolve: {
alias: {
'~/': `${path.resolve(__dirname, 'src')}/`,
},
},
plugins: [
Preview(),
VueMacros({
plugins: {
vue: Vue({
include: [/\.vue$/, /\.md$/],
reactivityTransform: true,
}),
},
}),
// https://github.com/hannoeru/vite-plugin-pages
Pages({
extensions: ['vue', 'md'],
}),
// https://github.com/JohnCampionJr/vite-plugin-vue-layouts
Layouts(),
// https://github.com/antfu/unplugin-auto-import
AutoImport({
imports: [
'vue',
'vue-router',
'vue-i18n',
'vue/macros',
'@vueuse/head',
'@vueuse/core',
],
dts: 'src/auto-imports.d.ts',
dirs: [
'src/composables',
'src/stores',
],
vueTemplate: true,
}),
// https://github.com/antfu/unplugin-vue-components
Components({
// allow auto load markdown components under `./src/components/`
extensions: ['vue', 'md'],
// allow auto import and register components used in markdown
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
dts: 'src/components.d.ts',
}),
// https://github.com/antfu/unocss
// see unocss.config.ts for config
Unocss(),
// https://github.com/antfu/vite-plugin-vue-markdown
// Don't need this? Try vitesse-lite: https://github.com/antfu/vitesse-lite
Markdown({
wrapperClasses: 'prose prose-sm m-auto text-left',
headEnabled: true,
markdownItSetup(md) {
// https://prismjs.com/
md.use(Shiki, {
theme: {
light: 'vitesse-light',
dark: 'vitesse-dark',
},
})
md.use(LinkAttributes, {
matcher: (link: string) => /^https?:\/\//.test(link),
attrs: {
target: '_blank',
rel: 'noopener',
},
})
},
}),
// https://github.com/antfu/vite-plugin-pwa
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.svg', 'safari-pinned-tab.svg'],
manifest: {
name: 'Vitesse',
short_name: 'Vitesse',
theme_color: '#ffffff',
icons: [
{
src: '/pwa-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: '/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
},
{
src: '/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable',
},
],
},
}),
// https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n
VueI18n({
runtimeOnly: true,
compositionOnly: true,
fullInstall: true,
include: [path.resolve(__dirname, 'locales/**')],
}),
// https://github.com/antfu/vite-plugin-inspect
// Visit http://localhost:3333/__inspect/ to see the inspector
Inspect(),
// https://github.com/webfansplz/vite-plugin-vue-inspector
Inspector({
toggleButtonVisibility: 'never',
}),
],
// https://github.com/vitest-dev/vitest
test: {
include: ['test/**/*.test.ts'],
environment: 'jsdom',
deps: {
inline: ['@vue', '@vueuse', 'vue-demi'],
},
},
// https://github.com/antfu/vite-ssg
ssgOptions: {
script: 'async',
formatting: 'minify',
onFinished() { generateSitemap() },
},
ssr: {
// TODO: workaround until they support native ESM
noExternal: ['workbox-window', /vue-i18n/],
},
})