├── .eslintignore ├── public ├── CNAME ├── robots.txt ├── logo.png ├── img │ ├── birthday.png │ └── icons │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── apple-touch-icon.png │ │ ├── mstile-150x150.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── msapplication-icon-144x144.png │ │ └── safari-pinned-tab.svg ├── audio │ └── happy-birthday.mp3 ├── favicon.svg └── manifest.json ├── .eslintrc ├── src ├── composables │ ├── index.ts │ └── dark.ts ├── styles │ ├── vars.scss │ └── index.scss ├── types.ts ├── layouts │ ├── default.vue │ └── 404.vue ├── pages │ ├── about.md │ └── index.vue ├── modules │ ├── nprogress.ts │ ├── README.md │ ├── pwa.ts │ ├── pinia.ts │ └── i18n.ts ├── shims.d.ts ├── App.vue ├── utils │ ├── egg.ts │ └── bubble.ts ├── components.d.ts ├── components │ ├── BestWish.vue │ └── BirthdayCake.vue ├── main.ts └── auto-imports.d.ts ├── .browserslistrc ├── .npmrc ├── .vscode ├── extensions.json └── settings.json ├── .gitignore ├── unocss.config.ts ├── .github └── workflows │ ├── gh-pages.yml │ └── ci.yml ├── tsconfig.json ├── index.html ├── README.md ├── LICENSE ├── package.json └── vite.config.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | public 3 | -------------------------------------------------------------------------------- /public/CNAME: -------------------------------------------------------------------------------- 1 | birthday.yunyoujun.cn 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@antfu"] 3 | } 4 | -------------------------------------------------------------------------------- /src/composables/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dark' 2 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /src/styles/vars.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-primary: #866b62; 3 | } 4 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/logo.png -------------------------------------------------------------------------------- /public/img/birthday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/birthday.png -------------------------------------------------------------------------------- /public/audio/happy-birthday.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/audio/happy-birthday.mp3 -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { ViteSSGContext } from 'vite-ssg' 2 | 3 | export type UserModule = (ctx: ViteSSGContext) => void 4 | -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YunYouJun/birthday/HEAD/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /src/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/pages/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About 3 | --- 4 | 5 |
6 | 7 | # Happy Birthday! 8 | 9 | 新的一岁,更加开心! 10 | 11 |
12 | -------------------------------------------------------------------------------- /src/composables/dark.ts: -------------------------------------------------------------------------------- 1 | // these APIs are auto-imported from @vueuse/core 2 | export const isDark = useDark() 3 | export const toggleDark = useToggle(isDark) 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "johnsoncodehk.volar", 4 | "lokalise.i18n-ally", 5 | "antfu.iconify", 6 | "dbaeumer.vscode-eslint", 7 | "voorjaar.windicss-intellisense", 8 | "csstools.postcss" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/modules/nprogress.ts: -------------------------------------------------------------------------------- 1 | import NProgress from 'nprogress' 2 | import type { UserModule } from '~/types' 3 | 4 | export const install: UserModule = ({ isClient, router }) => { 5 | if (isClient) { 6 | router.beforeEach(() => { NProgress.start() }) 7 | router.afterEach(() => { NProgress.done() }) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/modules/README.md: -------------------------------------------------------------------------------- 1 | ## Modules 2 | 3 | A custom user module system. Place a `.ts` file with the following template, it will be installed automatically. 4 | 5 | ```ts 6 | import type { UserModule } from '~/types' 7 | 8 | export const install: UserModule = ({ app, router, isClient }) => { 9 | // do something 10 | } 11 | ``` 12 | -------------------------------------------------------------------------------- /src/shims.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-duplicates */ 2 | 3 | declare interface Window { 4 | // extend the window 5 | } 6 | 7 | // with vite-plugin-vue-markdown, markdowns can be treat as Vue components 8 | declare module '*.md' { 9 | import type { ComponentOptions } from 'vue' 10 | const component: ComponentOptions 11 | export default component 12 | } 13 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /src/modules/pwa.ts: -------------------------------------------------------------------------------- 1 | import type { UserModule } from '~/types' 2 | 3 | // https://github.com/antfu/vite-plugin-pwa#automatic-reload-when-new-content-available 4 | export const install: UserModule = ({ isClient, router }) => { 5 | if (!isClient) 6 | return 7 | 8 | router.isReady().then(async () => { 9 | const { registerSW } = await import('virtual:pwa-register') 10 | registerSW({ immediate: true }) 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["Vitesse"], 3 | "i18n-ally.keystyle": "nested", 4 | "i18n-ally.localesPaths": "locales", 5 | "i18n-ally.sortKeys": true, 6 | "prettier.enable": false, 7 | "typescript.tsdk": "node_modules/typescript/lib", 8 | "editor.codeActionsOnSave": { 9 | "source.fixAll.eslint": true 10 | }, 11 | "files.associations": { 12 | "*.css": "postcss" 13 | }, 14 | "editor.formatOnSave": false 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # lock 2 | yarn.lock 3 | package-lock.json 4 | 5 | .DS_Store 6 | node_modules 7 | /dist 8 | 9 | # local env files 10 | *.local 11 | .env.local 12 | .env.*.local 13 | 14 | # Log files 15 | *.log 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | pnpm-debug.log* 20 | 21 | # Editor directories and files 22 | .idea 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | *.sw? 28 | 29 | .vite-ssg-dist 30 | .vite-ssg-temp 31 | dist 32 | dist-ssr 33 | -------------------------------------------------------------------------------- /src/utils/egg.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | console.log( 3 | '%c Happy Birthday! ', 4 | 'background-color:#93B7F3;color:white;padding:2px;', 5 | ) 6 | console.log( 7 | '%c 虽然离死亡更进一步,但祝你新的一岁更加开心!', 8 | 'background-color:dodgerblue;color:white;padding:2px;', 9 | ) 10 | console.log( 11 | '%c From your friend. @YunYouJun %c https://github.com/YunYouJun/birthday ', 12 | 'background-color:#000000;color:white;padding:3px;', 13 | 'border: 1px solid black;padding:2px;', 14 | ) 15 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Happy Birthday!", 3 | "short_name": "Birth", 4 | "icons": [ 5 | { 6 | "src": "./img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "./img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "./index.html", 17 | "display": "standalone", 18 | "background_color": "#000000", 19 | "theme_color": "#66CCFF" 20 | } 21 | -------------------------------------------------------------------------------- /src/layouts/404.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 21 | -------------------------------------------------------------------------------- /unocss.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig, 3 | presetAttributify, 4 | presetIcons, 5 | presetTypography, 6 | presetUno, 7 | transformerDirectives, 8 | transformerVariantGroup, 9 | } from 'unocss' 10 | 11 | export default defineConfig({ 12 | presets: [ 13 | presetUno(), 14 | presetAttributify(), 15 | presetIcons({ 16 | scale: 1.2, 17 | warn: true, 18 | }), 19 | presetTypography(), 20 | ], 21 | transformers: [ 22 | transformerDirectives(), 23 | transformerVariantGroup(), 24 | ], 25 | }) 26 | -------------------------------------------------------------------------------- /src/modules/pinia.ts: -------------------------------------------------------------------------------- 1 | import { createPinia } from 'pinia' 2 | import type { UserModule } from '~/types' 3 | 4 | // Setup Pinia 5 | // https://pinia.esm.dev/ 6 | export const install: UserModule = ({ isClient, initialState, app }) => { 7 | const pinia = createPinia() 8 | app.use(pinia) 9 | // Refer to 10 | // https://github.com/antfu/vite-ssg/blob/main/README.md#state-serialization 11 | // for other serialization strategies. 12 | if (isClient) 13 | pinia.state.value = (initialState.pinia) || {} 14 | 15 | else 16 | initialState.pinia = pinia.state.value 17 | } 18 | -------------------------------------------------------------------------------- /src/components.d.ts: -------------------------------------------------------------------------------- 1 | // generated by unplugin-vue-components 2 | // We suggest you to commit this file into source control 3 | // Read more: https://github.com/vuejs/core/pull/3399 4 | import '@vue/runtime-core' 5 | 6 | declare module '@vue/runtime-core' { 7 | export interface GlobalComponents { 8 | BestWish: typeof import('./components/BestWish.vue')['default'] 9 | BirthdayCake: typeof import('./components/BirthdayCake.vue')['default'] 10 | RouterLink: typeof import('vue-router')['RouterLink'] 11 | RouterView: typeof import('vue-router')['RouterView'] 12 | } 13 | } 14 | 15 | export {} 16 | -------------------------------------------------------------------------------- /src/components/BestWish.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 21 | 22 | 28 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: gh-pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: pnpm/action-setup@v2 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 16.x 17 | cache: pnpm 18 | 19 | - name: Install 20 | run: pnpm install 21 | 22 | - run: pnpm run build 23 | 24 | - name: Deploy 25 | uses: peaceiris/actions-gh-pages@v3 26 | with: 27 | github_token: ${{ secrets.GITHUB_TOKEN }} 28 | publish_dir: ./dist 29 | force_orphan: true 30 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | // register vue composition api globally 2 | import { ViteSSG } from 'vite-ssg' 3 | import generatedRoutes from 'virtual:generated-pages' 4 | import { setupLayouts } from 'virtual:generated-layouts' 5 | import App from './App.vue' 6 | 7 | // your custom styles here 8 | import './styles/vars.scss' 9 | import './styles/index.scss' 10 | 11 | import 'uno.css' 12 | 13 | // utils 14 | import '~/utils/egg' 15 | 16 | const routes = setupLayouts(generatedRoutes) 17 | 18 | // https://github.com/antfu/vite-ssg 19 | export const createApp = ViteSSG(App, { routes }, (ctx) => { 20 | // install all modules under `modules/` 21 | Object.values(import.meta.globEager('./modules/*.ts')).map(i => 22 | i.install?.(ctx), 23 | ) 24 | }) 25 | -------------------------------------------------------------------------------- /src/modules/i18n.ts: -------------------------------------------------------------------------------- 1 | import { createI18n } from 'vue-i18n' 2 | import type { UserModule } from '~/types' 3 | 4 | // Import i18n resources 5 | // https://vitejs.dev/guide/features.html#glob-import 6 | // 7 | // Don't need this? Try vitesse-lite: https://github.com/antfu/vitesse-lite 8 | const messages = Object.fromEntries( 9 | Object.entries( 10 | import.meta.globEager('../../locales/*.y(a)?ml')) 11 | .map(([key, value]) => { 12 | const yaml = key.endsWith('.yaml') 13 | return [key.slice(14, yaml ? -5 : -4), value.default] 14 | }), 15 | ) 16 | 17 | export const install: UserModule = ({ app }) => { 18 | const i18n = createI18n({ 19 | legacy: false, 20 | locale: 'en', 21 | messages, 22 | }) 23 | 24 | app.use(i18n) 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "ESNext", 5 | "target": "ESNext", 6 | "lib": ["DOM", "ESNext"], 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "jsx": "preserve", 10 | "skipLibCheck": true, 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "noUnusedLocals": true, 14 | "strictNullChecks": true, 15 | "allowJs": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "types": [ 18 | "vite/client", 19 | "vue/ref-macros", 20 | "vite-plugin-pages/client", 21 | "vite-plugin-vue-layouts/client" 22 | ], 23 | "paths": { 24 | "~/*": ["src/*"] 25 | } 26 | }, 27 | "exclude": ["dist", "node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | pull_request: 9 | branches: 10 | - master 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: pnpm/action-setup@v2 18 | - uses: actions/setup-node@v3 19 | with: 20 | node-version: 16.x 21 | cache: pnpm 22 | 23 | - name: Install 24 | run: pnpm install 25 | 26 | - name: Lint 27 | run: pnpm run lint 28 | 29 | typecheck: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v3 33 | - uses: pnpm/action-setup@v2 34 | - uses: actions/setup-node@v3 35 | with: 36 | node-version: 16.x 37 | cache: pnpm 38 | 39 | - name: Install 40 | run: pnpm install 41 | 42 | - name: Typecheck 43 | run: pnpm run typecheck 44 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # birthday 2 | 3 | [![gh-pages](https://github.com/YunYouJun/birthday/workflows/gh-pages/badge.svg)](https://github.com/YunYouJun/birthday/actions) 4 | 5 | [Happy Birthday To You!](https://birthday.yunyoujun.cn) 6 | 7 | ## Use 8 | 9 | If you in , then you can see in this page. 10 | 11 | ```txt 12 | World 13 | Hello 14 | ``` 15 | 16 | | Prop | Type | Description | Default | 17 | | ---- | ------ | --------------------- | --------------- | 18 | | name | String | who you want to greet | null | 19 | | msg | String | what you want to say | Happy Birthday! | 20 | 21 | ### Setup 22 | 23 | ```sh 24 | yarn 25 | # npm run install 26 | ``` 27 | 28 | ### Build 29 | 30 | ```sh 31 | yarn build 32 | # npm run build 33 | ``` 34 | 35 | ### View 36 | 37 | ```sh 38 | # http://localhost:8080 39 | yarn serve 40 | # npm run serve 41 | ``` 42 | 43 | ## Reference 44 | 45 | [Codepen-Cake](https://codepen.io/fixcl/pen/nKFDr) 46 | 47 | ## Change Log 48 | 49 | - 2019.01.08 | Use vue rewrite 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 YunYouJun 云游君 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 36 | 37 | 56 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | font-family: Avenir, Helvetica, Arial, sans-serif; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | text-align: center; 6 | color: #2c3e50; 7 | } 8 | 9 | html { 10 | padding: 0; 11 | margin: 0; 12 | width: 100%; 13 | height: 100%; 14 | } 15 | 16 | body { 17 | margin: 0; 18 | line-height: 1; 19 | } 20 | 21 | body { 22 | width: 100%; 23 | height: 100%; 24 | background: rgb(105, 155, 200); 25 | background: -moz-radial-gradient( 26 | top left, 27 | ellipse cover, 28 | rgba(105, 155, 200, 1) 0%, 29 | rgba(181, 197, 216, 1) 57% 30 | ); 31 | background: -webkit-gradient( 32 | radial, 33 | top left, 34 | 0px, 35 | top left, 36 | 100%, 37 | color-stop(0%, rgba(105, 155, 200, 1)), 38 | color-stop(57%, rgba(181, 197, 216, 1)) 39 | ); 40 | background: -webkit-radial-gradient( 41 | top left, 42 | ellipse cover, 43 | rgba(105, 155, 200, 1) 0%, 44 | rgba(181, 197, 216, 1) 57% 45 | ); 46 | background: -o-radial-gradient( 47 | top left, 48 | ellipse cover, 49 | rgba(105, 155, 200, 1) 0%, 50 | rgba(181, 197, 216, 1) 57% 51 | ); 52 | background: -ms-radial-gradient( 53 | top left, 54 | ellipse cover, 55 | rgba(105, 155, 200, 1) 0%, 56 | rgba(181, 197, 216, 1) 57% 57 | ); 58 | background: radial-gradient( 59 | ellipse at top left, 60 | rgba(105, 155, 200, 1) 0%, 61 | rgba(181, 197, 216, 1) 57% 62 | ); 63 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#699bc8', endColorstr='#b5c5d8',GradientType=1 ); 64 | } 65 | 66 | // elements 67 | .music-btn { 68 | color: var(--color-primary); 69 | border: 2px solid var(--color-primary); 70 | } 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "birthday", 3 | "version": "1.1.0", 4 | "private": true, 5 | "packageManager": "pnpm@7.3.0", 6 | "description": "Happy Birthday To You!", 7 | "author": { 8 | "email": "me@yunyoujun.cn", 9 | "name": "YunYouJun", 10 | "url": "https://www.yunyoujun.cn" 11 | }, 12 | "license": "MIT", 13 | "repository": "https://github.com/YunYouJun/birthday", 14 | "scripts": { 15 | "build": "cross-env NODE_ENV=production vite-ssg build", 16 | "dev": "vite --port 3333 --open --host", 17 | "lint": "eslint .", 18 | "preview": "vite preview", 19 | "preview-https": "serve dist", 20 | "typecheck": "vue-tsc --noEmit" 21 | }, 22 | "dependencies": { 23 | "@vueuse/core": "^8.7.5", 24 | "@vueuse/head": "^0.7.6", 25 | "@vueuse/sound": "^1.1.7", 26 | "nprogress": "^0.2.0", 27 | "pinia": "^2.0.14", 28 | "prism-theme-vars": "^0.2.3", 29 | "vue-demi": "^0.13.1", 30 | "vue-i18n": "^9.1.10", 31 | "vue-router": "^4.0.16" 32 | }, 33 | "devDependencies": { 34 | "@antfu/eslint-config": "^0.25.2", 35 | "@iconify-json/carbon": "^1.1.6", 36 | "@iconify-json/mdi": "^1.1.24", 37 | "@intlify/vite-plugin-vue-i18n": "^3.4.0", 38 | "@types/markdown-it-link-attributes": "^3.0.1", 39 | "@types/nprogress": "^0.2.0", 40 | "@vitejs/plugin-vue": "^2.3.3", 41 | "critters": "^0.0.16", 42 | "cross-env": "^7.0.3", 43 | "eslint": "^8.18.0", 44 | "https-localhost": "^4.7.0", 45 | "markdown-it-link-attributes": "^4.0.0", 46 | "markdown-it-prism": "^2.2.4", 47 | "pnpm": "^7.3.0", 48 | "sass": "^1.53.0", 49 | "typescript": "^4.7.4", 50 | "unocss": "^0.41.1", 51 | "unplugin-auto-import": "^0.9.1", 52 | "unplugin-vue-components": "^0.20.1", 53 | "vite": "^2.9.13", 54 | "vite-plugin-inspect": "^0.5.0", 55 | "vite-plugin-pages": "^0.24.2", 56 | "vite-plugin-pwa": "^0.12.0", 57 | "vite-plugin-vue-layouts": "^0.6.0", 58 | "vite-plugin-vue-markdown": "^0.1.1", 59 | "vite-ssg": "^0.20.1", 60 | "vue": "^3.2.37", 61 | "vue-tsc": "^0.38.2" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { defineConfig } from 'vite' 3 | import Vue from '@vitejs/plugin-vue' 4 | import Pages from 'vite-plugin-pages' 5 | import Layouts from 'vite-plugin-vue-layouts' 6 | import Components from 'unplugin-vue-components/vite' 7 | import AutoImport from 'unplugin-auto-import/vite' 8 | import Markdown from 'vite-plugin-vue-markdown' 9 | import { VitePWA } from 'vite-plugin-pwa' 10 | import VueI18n from '@intlify/vite-plugin-vue-i18n' 11 | import Inspect from 'vite-plugin-inspect' 12 | import Prism from 'markdown-it-prism' 13 | import LinkAttributes from 'markdown-it-link-attributes' 14 | 15 | import Unocss from 'unocss/vite' 16 | 17 | const markdownWrapperClasses = 'prose prose-sm m-auto text-left' 18 | 19 | export default defineConfig({ 20 | resolve: { 21 | alias: { 22 | '~/': `${path.resolve(__dirname, 'src')}/`, 23 | }, 24 | }, 25 | plugins: [ 26 | Vue({ 27 | include: [/\.vue$/, /\.md$/], 28 | }), 29 | 30 | // https://github.com/hannoeru/vite-plugin-pages 31 | Pages({ 32 | extensions: ['vue', 'md'], 33 | }), 34 | 35 | // https://github.com/JohnCampionJr/vite-plugin-vue-layouts 36 | Layouts(), 37 | 38 | // https://github.com/antfu/unplugin-auto-import 39 | AutoImport({ 40 | imports: [ 41 | 'vue', 42 | 'vue-router', 43 | 'vue-i18n', 44 | '@vueuse/head', 45 | '@vueuse/core', 46 | ], 47 | dts: 'src/auto-imports.d.ts', 48 | }), 49 | 50 | // https://github.com/antfu/unplugin-vue-components 51 | Components({ 52 | // allow auto load markdown components under `./src/components/` 53 | extensions: ['vue', 'md'], 54 | 55 | // allow auto import and register components used in markdown 56 | include: [/\.vue$/, /\.vue\?vue/, /\.md$/], 57 | 58 | dts: 'src/components.d.ts', 59 | }), 60 | 61 | // https://github.com/unocss/unocss 62 | Unocss(), 63 | 64 | // https://github.com/antfu/vite-plugin-vue-markdown 65 | // Don't need this? Try vitesse-lite: https://github.com/antfu/vitesse-lite 66 | Markdown({ 67 | wrapperClasses: markdownWrapperClasses, 68 | headEnabled: true, 69 | markdownItSetup(md) { 70 | // https://prismjs.com/ 71 | md.use(Prism) 72 | md.use(LinkAttributes, { 73 | pattern: /^https?:\/\//, 74 | attrs: { 75 | target: '_blank', 76 | rel: 'noopener', 77 | }, 78 | }) 79 | }, 80 | }), 81 | 82 | // https://github.com/antfu/vite-plugin-pwa 83 | VitePWA({ 84 | registerType: 'autoUpdate', 85 | includeAssets: ['favicon.svg', 'robots.txt', 'safari-pinned-tab.svg'], 86 | manifest: { 87 | name: 'Vitesse', 88 | short_name: 'Vitesse', 89 | theme_color: '#ffffff', 90 | icons: [ 91 | { 92 | src: '/pwa-192x192.png', 93 | sizes: '192x192', 94 | type: 'image/png', 95 | }, 96 | { 97 | src: '/pwa-512x512.png', 98 | sizes: '512x512', 99 | type: 'image/png', 100 | }, 101 | { 102 | src: '/pwa-512x512.png', 103 | sizes: '512x512', 104 | type: 'image/png', 105 | purpose: 'any maskable', 106 | }, 107 | ], 108 | }, 109 | }), 110 | 111 | // https://github.com/intlify/bundle-tools/tree/main/packages/vite-plugin-vue-i18n 112 | VueI18n({ 113 | runtimeOnly: true, 114 | compositionOnly: true, 115 | include: [path.resolve(__dirname, 'locales/**')], 116 | }), 117 | 118 | // https://github.com/antfu/vite-plugin-inspect 119 | Inspect({ 120 | // change this to enable inspect for debugging 121 | enabled: false, 122 | }), 123 | ], 124 | 125 | server: { 126 | fs: { 127 | strict: true, 128 | }, 129 | }, 130 | 131 | // https://github.com/antfu/vite-ssg 132 | ssgOptions: { 133 | script: 'async', 134 | formatting: 'minify', 135 | }, 136 | 137 | optimizeDeps: { 138 | include: ['vue', 'vue-router', '@vueuse/core', '@vueuse/head', '@vueuse/sound'], 139 | exclude: ['vue-demi'], 140 | }, 141 | }) 142 | -------------------------------------------------------------------------------- /src/utils/bubble.ts: -------------------------------------------------------------------------------- 1 | interface Circle { 2 | x: number 3 | y: number 4 | radius: number 5 | speed: number 6 | color: number[] 7 | alpha: number 8 | } 9 | 10 | interface Line { 11 | x: number 12 | y: number 13 | width: number 14 | speed: number 15 | color: number[] 16 | alpha: number 17 | } 18 | 19 | // ------- 20 | // Bubble Function Code 21 | 22 | function bubble(): void { 23 | const canvas = document.querySelectorAll( 24 | '#bg', 25 | ) as NodeListOf 26 | const background = canvas[0] 27 | const foreground1 = canvas[1] 28 | const foreground2 = canvas[2] 29 | const config = { 30 | circle: { 31 | amount: 15, 32 | layer: 3, 33 | color: [255, 255, 255], 34 | alpha: 0.3, 35 | }, 36 | line: { 37 | amount: 12, 38 | layer: 3, 39 | color: [255, 255, 255], 40 | alpha: 0.3, 41 | }, 42 | speed: 0.5, 43 | angle: 20, 44 | } 45 | 46 | if (background.getContext) { 47 | const bctx = background.getContext('2d') 48 | const fctx1 = foreground1.getContext('2d') 49 | const fctx2 = foreground2.getContext('2d') 50 | const M = window.Math // Cached Math 51 | const degree = (config.angle / 360) * M.PI * 2 52 | let circles: Circle[] = [] 53 | let lines: Line[] = [] 54 | let wWidth: number 55 | let wHeight: number 56 | let timer: number 57 | 58 | if (!bctx || !fctx1 || !fctx2) 59 | return 60 | 61 | // requestAnimationFrame = window.requestAnimationFrame || 62 | // window.mozRequestAnimationFrame || 63 | // window.webkitRequestAnimationFrame || 64 | // window.msRequestAnimationFrame || 65 | // window.oRequestAnimationFrame || 66 | // function(callback, element) { setTimeout(callback, 1000 / 60); }; 67 | 68 | // cancelAnimationFrame = window.cancelAnimationFrame || 69 | // window.mozCancelAnimationFrame || 70 | // window.webkitCancelAnimationFrame || 71 | // window.msCancelAnimationFrame || 72 | // window.oCancelAnimationFrame || 73 | // clearTimeout; 74 | 75 | const setCanvasHeight = function () { 76 | wWidth = window.innerWidth 77 | wHeight = window.innerHeight 78 | canvas.forEach((item: HTMLCanvasElement) => { 79 | item.width = wWidth 80 | item.height = wHeight 81 | }) 82 | } 83 | 84 | const drawCircle = function ( 85 | x: number, 86 | y: number, 87 | radius: number, 88 | color: number[], 89 | alpha: number, 90 | ) { 91 | const gradient = fctx1.createRadialGradient(x, y, radius, x, y, 0) 92 | gradient.addColorStop( 93 | 0, 94 | `rgba(${color[0]},${color[1]},${color[2]},${alpha})`, 95 | ) 96 | gradient.addColorStop( 97 | 1, 98 | `rgba(${ 99 | color[0] 100 | },${ 101 | color[1] 102 | },${ 103 | color[2] 104 | },${ 105 | alpha - 0.1 106 | })`, 107 | ) 108 | 109 | fctx1.beginPath() 110 | fctx1.arc(x, y, radius, 0, M.PI * 2, true) 111 | fctx1.fillStyle = gradient 112 | fctx1.fill() 113 | } 114 | 115 | const drawLine = function ( 116 | x: number, 117 | y: number, 118 | width: number, 119 | color: number[], 120 | alpha: number, 121 | ) { 122 | const endX = x + M.sin(degree) * width 123 | const endY = y - M.cos(degree) * width 124 | const gradient = fctx2.createLinearGradient(x, y, endX, endY) 125 | gradient.addColorStop( 126 | 0, 127 | `rgba(${color[0]},${color[1]},${color[2]},${alpha})`, 128 | ) 129 | gradient.addColorStop( 130 | 1, 131 | `rgba(${ 132 | color[0] 133 | },${ 134 | color[1] 135 | },${ 136 | color[2] 137 | },${ 138 | alpha - 0.1 139 | })`, 140 | ) 141 | 142 | fctx2.beginPath() 143 | fctx2.moveTo(x, y) 144 | fctx2.lineTo(endX, endY) 145 | fctx2.lineWidth = 3 146 | fctx2.lineCap = 'round' 147 | fctx2.strokeStyle = gradient 148 | fctx2.stroke() 149 | } 150 | 151 | const drawBack = function () { 152 | bctx.clearRect(0, 0, wWidth, wHeight) 153 | 154 | const gradient = [] 155 | 156 | gradient[0] = bctx.createRadialGradient( 157 | wWidth * 0.3, 158 | wHeight * 0.1, 159 | 0, 160 | wWidth * 0.3, 161 | wHeight * 0.1, 162 | wWidth * 0.9, 163 | ) 164 | // gradient[0].addColorStop(0, 'rgb(0, 126, 177)'); 165 | gradient[0].addColorStop(1, 'transparent') 166 | 167 | bctx.translate(wWidth, 0) 168 | bctx.scale(-1, 1) 169 | bctx.beginPath() 170 | bctx.fillStyle = gradient[0] 171 | bctx.fillRect(0, 0, wWidth, wHeight) 172 | 173 | gradient[1] = bctx.createRadialGradient( 174 | wWidth * 0.1, 175 | wHeight * 0.1, 176 | 0, 177 | wWidth * 0.3, 178 | wHeight * 0.1, 179 | wWidth, 180 | ) 181 | // gradient[1].addColorStop(0, 'rgb(0, 150, 240)'); 182 | gradient[1].addColorStop(0.8, 'transparent') 183 | 184 | bctx.translate(wWidth, 0) 185 | bctx.scale(-1, 1) 186 | bctx.beginPath() 187 | bctx.fillStyle = gradient[1] 188 | bctx.fillRect(0, 0, wWidth, wHeight) 189 | 190 | gradient[2] = bctx.createRadialGradient( 191 | wWidth * 0.1, 192 | wHeight * 0.5, 193 | 0, 194 | wWidth * 0.1, 195 | wHeight * 0.5, 196 | wWidth * 0.5, 197 | ) 198 | // gradient[2].addColorStop(0, 'rgb(40, 20, 105)'); 199 | gradient[2].addColorStop(1, 'transparent') 200 | 201 | bctx.beginPath() 202 | bctx.fillStyle = gradient[2] 203 | bctx.fillRect(0, 0, wWidth, wHeight) 204 | } 205 | 206 | const animate = function () { 207 | const sin = M.sin(degree) 208 | const cos = M.cos(degree) 209 | 210 | if (config.circle.amount > 0 && config.circle.layer > 0) { 211 | if (!fctx1) 212 | return 213 | fctx1.clearRect(0, 0, wWidth, wHeight) 214 | for (let i = 0, len = circles.length; i < len; i++) { 215 | const item = circles[i] 216 | let x = item.x 217 | let y = item.y 218 | const radius = item.radius 219 | const speed = item.speed 220 | 221 | if (x > wWidth + radius) 222 | x = -radius 223 | 224 | else if (x < -radius) 225 | x = wWidth + radius 226 | 227 | else 228 | x += sin * speed 229 | 230 | if (y > wHeight + radius) 231 | y = -radius 232 | 233 | else if (y < -radius) 234 | y = wHeight + radius 235 | 236 | else 237 | y -= cos * speed 238 | 239 | item.x = x 240 | item.y = y 241 | drawCircle(x, y, radius, item.color, item.alpha) 242 | } 243 | } 244 | 245 | if (config.line.amount > 0 && config.line.layer > 0) { 246 | if (!fctx2) 247 | return 248 | fctx2.clearRect(0, 0, wWidth, wHeight) 249 | for (let j = 0, len = lines.length; j < len; j++) { 250 | const item = lines[j] 251 | let x = item.x 252 | let y = item.y 253 | const width = item.width 254 | const speed = item.speed 255 | 256 | if (x > wWidth + width * sin) 257 | x = -width * sin 258 | 259 | else if (x < -width * sin) 260 | x = wWidth + width * sin 261 | 262 | else 263 | x += sin * speed 264 | 265 | if (y > wHeight + width * cos) 266 | y = -width * cos 267 | 268 | else if (y < -width * cos) 269 | y = wHeight + width * cos 270 | 271 | else 272 | y -= cos * speed 273 | 274 | item.x = x 275 | item.y = y 276 | drawLine(x, y, width, item.color, item.alpha) 277 | } 278 | } 279 | 280 | timer = requestAnimationFrame(animate) 281 | } 282 | 283 | const createItem = function () { 284 | circles = [] 285 | lines = [] 286 | 287 | if (config.circle.amount > 0 && config.circle.layer > 0) { 288 | for (let i = 0; i < config.circle.amount / config.circle.layer; i++) { 289 | for (let j = 0; j < config.circle.layer; j++) { 290 | circles.push({ 291 | x: M.random() * wWidth, 292 | y: M.random() * wHeight, 293 | radius: M.random() * (20 + j * 5) + (20 + j * 5), 294 | color: config.circle.color, 295 | alpha: M.random() * 0.2 + (config.circle.alpha - j * 0.1), 296 | speed: config.speed * (1 + j * 0.5), 297 | }) 298 | } 299 | } 300 | } 301 | 302 | if (config.line.amount > 0 && config.line.layer > 0) { 303 | for (let m = 0; m < config.line.amount / config.line.layer; m++) { 304 | for (let n = 0; n < config.line.layer; n++) { 305 | lines.push({ 306 | x: M.random() * wWidth, 307 | y: M.random() * wHeight, 308 | width: M.random() * (20 + n * 5) + (20 + n * 5), 309 | color: config.line.color, 310 | alpha: M.random() * 0.2 + (config.line.alpha - n * 0.1), 311 | speed: config.speed * (1 + n * 0.5), 312 | }) 313 | } 314 | } 315 | } 316 | 317 | cancelAnimationFrame(timer) 318 | timer = requestAnimationFrame(animate) 319 | drawBack() 320 | } 321 | 322 | window.onload = function () { 323 | setCanvasHeight() 324 | createItem() 325 | } 326 | window.onresize = function () { 327 | setCanvasHeight() 328 | createItem() 329 | } 330 | } 331 | } 332 | 333 | export default bubble 334 | -------------------------------------------------------------------------------- /public/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /src/auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by 'unplugin-auto-import' 2 | export {} 3 | declare global { 4 | const EffectScope: typeof import('vue')['EffectScope'] 5 | const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] 6 | const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] 7 | const computed: typeof import('vue')['computed'] 8 | const computedAsync: typeof import('@vueuse/core')['computedAsync'] 9 | const computedEager: typeof import('@vueuse/core')['computedEager'] 10 | const computedInject: typeof import('@vueuse/core')['computedInject'] 11 | const computedWithControl: typeof import('@vueuse/core')['computedWithControl'] 12 | const controlledComputed: typeof import('@vueuse/core')['controlledComputed'] 13 | const controlledRef: typeof import('@vueuse/core')['controlledRef'] 14 | const createApp: typeof import('vue')['createApp'] 15 | const createEventHook: typeof import('@vueuse/core')['createEventHook'] 16 | const createGlobalState: typeof import('@vueuse/core')['createGlobalState'] 17 | const createInjectionState: typeof import('@vueuse/core')['createInjectionState'] 18 | const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] 19 | const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] 20 | const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn'] 21 | const customRef: typeof import('vue')['customRef'] 22 | const debouncedRef: typeof import('@vueuse/core')['debouncedRef'] 23 | const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] 24 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 25 | const defineComponent: typeof import('vue')['defineComponent'] 26 | const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] 27 | const effectScope: typeof import('vue')['effectScope'] 28 | const extendRef: typeof import('@vueuse/core')['extendRef'] 29 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 30 | const getCurrentScope: typeof import('vue')['getCurrentScope'] 31 | const h: typeof import('vue')['h'] 32 | const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] 33 | const inject: typeof import('vue')['inject'] 34 | const isDefined: typeof import('@vueuse/core')['isDefined'] 35 | const isProxy: typeof import('vue')['isProxy'] 36 | const isReactive: typeof import('vue')['isReactive'] 37 | const isReadonly: typeof import('vue')['isReadonly'] 38 | const isRef: typeof import('vue')['isRef'] 39 | const logicAnd: typeof import('@vueuse/core')['logicAnd'] 40 | const logicNot: typeof import('@vueuse/core')['logicNot'] 41 | const logicOr: typeof import('@vueuse/core')['logicOr'] 42 | const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] 43 | const markRaw: typeof import('vue')['markRaw'] 44 | const nextTick: typeof import('vue')['nextTick'] 45 | const onActivated: typeof import('vue')['onActivated'] 46 | const onBeforeMount: typeof import('vue')['onBeforeMount'] 47 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 48 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 49 | const onClickOutside: typeof import('@vueuse/core')['onClickOutside'] 50 | const onDeactivated: typeof import('vue')['onDeactivated'] 51 | const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 52 | const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke'] 53 | const onLongPress: typeof import('@vueuse/core')['onLongPress'] 54 | const onMounted: typeof import('vue')['onMounted'] 55 | const onRenderTracked: typeof import('vue')['onRenderTracked'] 56 | const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 57 | const onScopeDispose: typeof import('vue')['onScopeDispose'] 58 | const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 59 | const onStartTyping: typeof import('@vueuse/core')['onStartTyping'] 60 | const onUnmounted: typeof import('vue')['onUnmounted'] 61 | const onUpdated: typeof import('vue')['onUpdated'] 62 | const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] 63 | const provide: typeof import('vue')['provide'] 64 | const reactify: typeof import('@vueuse/core')['reactify'] 65 | const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] 66 | const reactive: typeof import('vue')['reactive'] 67 | const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed'] 68 | const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit'] 69 | const reactivePick: typeof import('@vueuse/core')['reactivePick'] 70 | const readonly: typeof import('vue')['readonly'] 71 | const ref: typeof import('vue')['ref'] 72 | const refAutoReset: typeof import('@vueuse/core')['refAutoReset'] 73 | const refDebounced: typeof import('@vueuse/core')['refDebounced'] 74 | const refDefault: typeof import('@vueuse/core')['refDefault'] 75 | const refThrottled: typeof import('@vueuse/core')['refThrottled'] 76 | const refWithControl: typeof import('@vueuse/core')['refWithControl'] 77 | const resolveComponent: typeof import('vue')['resolveComponent'] 78 | const shallowReactive: typeof import('vue')['shallowReactive'] 79 | const shallowReadonly: typeof import('vue')['shallowReadonly'] 80 | const shallowRef: typeof import('vue')['shallowRef'] 81 | const syncRef: typeof import('@vueuse/core')['syncRef'] 82 | const syncRefs: typeof import('@vueuse/core')['syncRefs'] 83 | const templateRef: typeof import('@vueuse/core')['templateRef'] 84 | const throttledRef: typeof import('@vueuse/core')['throttledRef'] 85 | const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] 86 | const toRaw: typeof import('vue')['toRaw'] 87 | const toReactive: typeof import('@vueuse/core')['toReactive'] 88 | const toRef: typeof import('vue')['toRef'] 89 | const toRefs: typeof import('vue')['toRefs'] 90 | const triggerRef: typeof import('vue')['triggerRef'] 91 | const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount'] 92 | const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount'] 93 | const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted'] 94 | const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose'] 95 | const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted'] 96 | const unref: typeof import('vue')['unref'] 97 | const unrefElement: typeof import('@vueuse/core')['unrefElement'] 98 | const until: typeof import('@vueuse/core')['until'] 99 | const useActiveElement: typeof import('@vueuse/core')['useActiveElement'] 100 | const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] 101 | const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] 102 | const useAttrs: typeof import('vue')['useAttrs'] 103 | const useBase64: typeof import('@vueuse/core')['useBase64'] 104 | const useBattery: typeof import('@vueuse/core')['useBattery'] 105 | const useBluetooth: typeof import('@vueuse/core')['useBluetooth'] 106 | const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints'] 107 | const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel'] 108 | const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] 109 | const useCached: typeof import('@vueuse/core')['useCached'] 110 | const useClamp: typeof import('@vueuse/core')['useClamp'] 111 | const useClipboard: typeof import('@vueuse/core')['useClipboard'] 112 | const useColorMode: typeof import('@vueuse/core')['useColorMode'] 113 | const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] 114 | const useCounter: typeof import('@vueuse/core')['useCounter'] 115 | const useCssModule: typeof import('vue')['useCssModule'] 116 | const useCssVar: typeof import('@vueuse/core')['useCssVar'] 117 | const useCssVars: typeof import('vue')['useCssVars'] 118 | const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement'] 119 | const useCycleList: typeof import('@vueuse/core')['useCycleList'] 120 | const useDark: typeof import('@vueuse/core')['useDark'] 121 | const useDateFormat: typeof import('@vueuse/core')['useDateFormat'] 122 | const useDebounce: typeof import('@vueuse/core')['useDebounce'] 123 | const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn'] 124 | const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory'] 125 | const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion'] 126 | const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation'] 127 | const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio'] 128 | const useDevicesList: typeof import('@vueuse/core')['useDevicesList'] 129 | const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia'] 130 | const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility'] 131 | const useDraggable: typeof import('@vueuse/core')['useDraggable'] 132 | const useDropZone: typeof import('@vueuse/core')['useDropZone'] 133 | const useElementBounding: typeof import('@vueuse/core')['useElementBounding'] 134 | const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint'] 135 | const useElementHover: typeof import('@vueuse/core')['useElementHover'] 136 | const useElementSize: typeof import('@vueuse/core')['useElementSize'] 137 | const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility'] 138 | const useEventBus: typeof import('@vueuse/core')['useEventBus'] 139 | const useEventListener: typeof import('@vueuse/core')['useEventListener'] 140 | const useEventSource: typeof import('@vueuse/core')['useEventSource'] 141 | const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper'] 142 | const useFavicon: typeof import('@vueuse/core')['useFavicon'] 143 | const useFetch: typeof import('@vueuse/core')['useFetch'] 144 | const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess'] 145 | const useFocus: typeof import('@vueuse/core')['useFocus'] 146 | const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] 147 | const useFps: typeof import('@vueuse/core')['useFps'] 148 | const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] 149 | const useGamepad: typeof import('@vueuse/core')['useGamepad'] 150 | const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] 151 | const useHead: typeof import('@vueuse/head')['useHead'] 152 | const useI18n: typeof import('vue-i18n')['useI18n'] 153 | const useIdle: typeof import('@vueuse/core')['useIdle'] 154 | const useImage: typeof import('@vueuse/core')['useImage'] 155 | const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] 156 | const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver'] 157 | const useInterval: typeof import('@vueuse/core')['useInterval'] 158 | const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn'] 159 | const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier'] 160 | const useLastChanged: typeof import('@vueuse/core')['useLastChanged'] 161 | const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage'] 162 | const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] 163 | const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory'] 164 | const useMediaControls: typeof import('@vueuse/core')['useMediaControls'] 165 | const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery'] 166 | const useMemoize: typeof import('@vueuse/core')['useMemoize'] 167 | const useMemory: typeof import('@vueuse/core')['useMemory'] 168 | const useMounted: typeof import('@vueuse/core')['useMounted'] 169 | const useMouse: typeof import('@vueuse/core')['useMouse'] 170 | const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement'] 171 | const useMousePressed: typeof import('@vueuse/core')['useMousePressed'] 172 | const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver'] 173 | const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage'] 174 | const useNetwork: typeof import('@vueuse/core')['useNetwork'] 175 | const useNow: typeof import('@vueuse/core')['useNow'] 176 | const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination'] 177 | const useOnline: typeof import('@vueuse/core')['useOnline'] 178 | const usePageLeave: typeof import('@vueuse/core')['usePageLeave'] 179 | const useParallax: typeof import('@vueuse/core')['useParallax'] 180 | const usePermission: typeof import('@vueuse/core')['usePermission'] 181 | const usePointer: typeof import('@vueuse/core')['usePointer'] 182 | const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] 183 | const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] 184 | const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] 185 | const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] 186 | const useRafFn: typeof import('@vueuse/core')['useRafFn'] 187 | const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] 188 | const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] 189 | const useRoute: typeof import('vue-router')['useRoute'] 190 | const useRouter: typeof import('vue-router')['useRouter'] 191 | const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] 192 | const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] 193 | const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] 194 | const useScroll: typeof import('@vueuse/core')['useScroll'] 195 | const useScrollLock: typeof import('@vueuse/core')['useScrollLock'] 196 | const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] 197 | const useShare: typeof import('@vueuse/core')['useShare'] 198 | const useSlots: typeof import('vue')['useSlots'] 199 | const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] 200 | const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] 201 | const useStorage: typeof import('@vueuse/core')['useStorage'] 202 | const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync'] 203 | const useStyleTag: typeof import('@vueuse/core')['useStyleTag'] 204 | const useSwipe: typeof import('@vueuse/core')['useSwipe'] 205 | const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList'] 206 | const useTextSelection: typeof import('@vueuse/core')['useTextSelection'] 207 | const useThrottle: typeof import('@vueuse/core')['useThrottle'] 208 | const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn'] 209 | const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory'] 210 | const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo'] 211 | const useTimeout: typeof import('@vueuse/core')['useTimeout'] 212 | const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn'] 213 | const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll'] 214 | const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] 215 | const useTitle: typeof import('@vueuse/core')['useTitle'] 216 | const useToggle: typeof import('@vueuse/core')['useToggle'] 217 | const useTransition: typeof import('@vueuse/core')['useTransition'] 218 | const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams'] 219 | const useUserMedia: typeof import('@vueuse/core')['useUserMedia'] 220 | const useVModel: typeof import('@vueuse/core')['useVModel'] 221 | const useVModels: typeof import('@vueuse/core')['useVModels'] 222 | const useVibrate: typeof import('@vueuse/core')['useVibrate'] 223 | const useVirtualList: typeof import('@vueuse/core')['useVirtualList'] 224 | const useWakeLock: typeof import('@vueuse/core')['useWakeLock'] 225 | const useWebNotification: typeof import('@vueuse/core')['useWebNotification'] 226 | const useWebSocket: typeof import('@vueuse/core')['useWebSocket'] 227 | const useWebWorker: typeof import('@vueuse/core')['useWebWorker'] 228 | const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn'] 229 | const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus'] 230 | const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll'] 231 | const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] 232 | const watch: typeof import('vue')['watch'] 233 | const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] 234 | const watchDebounced: typeof import('@vueuse/core')['watchDebounced'] 235 | const watchEffect: typeof import('vue')['watchEffect'] 236 | const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable'] 237 | const watchOnce: typeof import('@vueuse/core')['watchOnce'] 238 | const watchPausable: typeof import('@vueuse/core')['watchPausable'] 239 | const watchPostEffect: typeof import('vue')['watchPostEffect'] 240 | const watchSyncEffect: typeof import('vue')['watchSyncEffect'] 241 | const watchThrottled: typeof import('@vueuse/core')['watchThrottled'] 242 | const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter'] 243 | const whenever: typeof import('@vueuse/core')['whenever'] 244 | } 245 | -------------------------------------------------------------------------------- /src/components/BirthdayCake.vue: -------------------------------------------------------------------------------- 1 | 243 | 244 | 245 | 335 | --------------------------------------------------------------------------------