├── .eslintrc.json ├── .github └── workflows │ └── main.deploy.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .vscode └── settings.json ├── CHANGELOG.zh-CN.md ├── LICENSE ├── README.en.md ├── README.md ├── commitlint.config.cjs ├── dist ├── background.js ├── favicon-edge.png ├── favicon.ico ├── favicon.png ├── index.html ├── manifest.json ├── sw.js └── workbox │ ├── workbox-cacheable-response.prod.js │ ├── workbox-core.prod.js │ ├── workbox-expiration.prod.js │ ├── workbox-range-requests.prod.js │ ├── workbox-routing.prod.js │ ├── workbox-strategies.prod.js │ └── workbox-sw.js ├── index.html ├── package.json ├── public ├── background.js ├── favicon-edge.png ├── favicon.ico ├── favicon.png ├── img │ └── icons │ │ ├── baidu.svg │ │ ├── bilibili.svg │ │ ├── bing-en.svg │ │ ├── bing.svg │ │ ├── google.svg │ │ ├── sougou.svg │ │ ├── taobao.svg │ │ └── youdao.svg ├── manifest.json ├── sw.js └── workbox │ ├── workbox-cacheable-response.prod.js │ ├── workbox-core.prod.js │ ├── workbox-expiration.prod.js │ ├── workbox-range-requests.prod.js │ ├── workbox-routing.prod.js │ ├── workbox-strategies.prod.js │ └── workbox-sw.js ├── scripts ├── build-crx.cjs └── upload-cdn.cjs ├── src ├── App.vue ├── assets │ ├── custom-animate.scss │ ├── element-modules.scss │ ├── element-variables.scss │ ├── global.scss │ ├── imgs │ │ ├── capture-new │ │ │ ├── Bookmark.png │ │ │ ├── Clock.png │ │ │ ├── Collection.png │ │ │ ├── CountDown.png │ │ │ ├── Day.png │ │ │ ├── Editor.png │ │ │ ├── Empty.png │ │ │ ├── GithubTrending.png │ │ │ ├── Iframe.png │ │ │ ├── JuejinList.png │ │ │ ├── MovieLines.png │ │ │ ├── Search.png │ │ │ ├── TodoList.png │ │ │ ├── Verse.png │ │ │ ├── Weather.png │ │ │ ├── WeiboList.png │ │ │ └── ZhihuList.png │ │ ├── icons │ │ │ ├── baidu.svg │ │ │ ├── bilibili.svg │ │ │ ├── bing-en.svg │ │ │ ├── bing.svg │ │ │ ├── google.svg │ │ │ ├── sougou.svg │ │ │ ├── taobao.svg │ │ │ └── youdao.svg │ │ ├── theme │ │ │ ├── base.png │ │ │ ├── mobile-pro.png │ │ │ ├── mobile.png │ │ │ ├── module.png │ │ │ ├── movie-lines.png │ │ │ ├── multi.png │ │ │ ├── simple.png │ │ │ └── tabs.gif │ │ ├── video-thumb.png │ │ ├── weather-animation-icon │ │ │ ├── clear-day.svg │ │ │ ├── cloudy.svg │ │ │ ├── drizzle.svg │ │ │ ├── fog.svg │ │ │ ├── hurricane.svg │ │ │ ├── mist.svg │ │ │ ├── not-available.svg │ │ │ ├── overcast-day.svg │ │ │ ├── overcast.svg │ │ │ ├── rain.svg │ │ │ ├── sleet.svg │ │ │ ├── snow.svg │ │ │ ├── thermometer-colder.svg │ │ │ ├── thermometer-warmer.svg │ │ │ ├── thunderstorms-rain.svg │ │ │ └── wind.svg │ │ └── weather-static-icon │ │ │ ├── clear-day.svg │ │ │ ├── cloudy.svg │ │ │ ├── drizzle.svg │ │ │ ├── fog.svg │ │ │ ├── hurricane.svg │ │ │ ├── mist.svg │ │ │ ├── not-available.svg │ │ │ ├── overcast-day.svg │ │ │ ├── overcast.svg │ │ │ ├── rain.svg │ │ │ ├── sleet.svg │ │ │ ├── snow.svg │ │ │ ├── thermometer-colder.svg │ │ │ ├── thermometer-warmer.svg │ │ │ ├── thunderstorms-rain.svg │ │ │ └── wind.svg │ └── variables.scss ├── components │ ├── Action │ │ └── ActionPopover.vue │ ├── ActionConfig.vue │ ├── AuxiliaryConfig.vue │ ├── Axuiliary │ │ ├── About.vue │ │ ├── ChangeLog.vue │ │ ├── CleanCache.vue │ │ ├── EffectSelector.vue │ │ ├── FAQ.md │ │ ├── FAQ.vue │ │ ├── ImportExport.vue │ │ └── TabControl.vue │ ├── BaseConfig.vue │ ├── FormControl │ │ ├── BackgroundFilterSelector.vue │ │ ├── BackgroundSelector.vue │ │ ├── BackgroundTool │ │ │ ├── LocalImg.vue │ │ │ ├── PersonalWallpaper.vue │ │ │ ├── RecommendPicture.vue │ │ │ └── RecommendVideo.vue │ │ ├── FontSelector.vue │ │ ├── MaterialSelector.vue │ │ ├── StandardColorPicker.vue │ │ └── WarnLock.vue │ ├── Global │ │ ├── BackgroundEffect.vue │ │ ├── BackgroundEffect │ │ │ ├── FireFlies.vue │ │ │ ├── Focus.vue │ │ │ ├── RainDrop.vue │ │ │ ├── Snow.vue │ │ │ ├── Star.vue │ │ │ └── source │ │ │ │ ├── ShaderProgram.js │ │ │ │ ├── Star.ts │ │ │ │ └── raindrop-fx.js │ │ ├── BackgroundImage.vue │ │ ├── DefaultTheme.vue │ │ ├── DefaultThemeData │ │ │ ├── Base.json │ │ │ ├── Mobile.json │ │ │ ├── MobilePro.json │ │ │ ├── Module.json │ │ │ ├── MovieLines.json │ │ │ ├── Multiple.json │ │ │ ├── Simple.json │ │ │ └── Tabs.json │ │ ├── EasyDialog.vue │ │ ├── IframeOpener.vue │ │ └── TabCarousel.vue │ ├── GlobalConfig.vue │ ├── GooeyMenu.vue │ ├── Layout.vue │ └── Tools │ │ ├── Confirm.vue │ │ ├── Icon.vue │ │ ├── IconifyPicker.vue │ │ ├── Loading.vue │ │ ├── TextLoading.vue │ │ ├── Tips.vue │ │ └── Unset.vue ├── constanst │ ├── icon.ts │ └── index.ts ├── global.ts ├── hooks │ └── useScreenMode.ts ├── lang │ ├── index.ts │ └── locales │ │ ├── en.json │ │ └── zh-cn.json ├── main.ts ├── materials │ ├── Bookmark │ │ ├── ConfigDialog.vue │ │ ├── MoveDialog.vue │ │ ├── index.vue │ │ └── setting.tsx │ ├── Clock │ │ ├── index.vue │ │ └── setting.tsx │ ├── Collection │ │ ├── index.vue │ │ ├── setting.tsx │ │ └── utils.ts │ ├── CountDown │ │ ├── index.vue │ │ └── setting.tsx │ ├── DailyHot │ │ ├── index.vue │ │ └── setting.tsx │ ├── Day │ │ ├── index.vue │ │ └── setting.tsx │ ├── Editor │ │ ├── index.vue │ │ ├── milkdown │ │ │ ├── Milk.vue │ │ │ ├── custom-nord.ts │ │ │ ├── fonts │ │ │ │ ├── howdz-editor.svg │ │ │ │ ├── howdz-editor.ttf │ │ │ │ └── howdz-editor.woff │ │ │ └── icon-font.css │ │ └── setting.tsx │ ├── Empty │ │ ├── index.vue │ │ └── setting.tsx │ ├── GithubTrending │ │ ├── index.vue │ │ └── setting.tsx │ ├── Iframe │ │ ├── index.vue │ │ └── setting.tsx │ ├── JuejinList │ │ ├── index.vue │ │ └── setting.tsx │ ├── MovieLines │ │ ├── index.vue │ │ └── setting.tsx │ ├── Search │ │ ├── EngineConfig.vue │ │ ├── index.vue │ │ └── setting.tsx │ ├── TodoList │ │ ├── DatePicker.vue │ │ ├── index.vue │ │ └── setting.tsx │ ├── Verse │ │ ├── index.vue │ │ └── setting.tsx │ ├── Weather │ │ ├── icon-map.ts │ │ ├── index.vue │ │ └── setting.tsx │ ├── WeiboList │ │ ├── index.vue │ │ └── setting.tsx │ ├── ZhihuList │ │ ├── index.vue │ │ └── setting.tsx │ ├── base.tsx │ └── setting.ts ├── plugins │ ├── local-img.ts │ ├── mouse-menu.ts │ ├── position-selector │ │ ├── index.ts │ │ ├── mapPosition.ts │ │ └── position-selector.vue │ └── standard-form │ │ ├── index.ts │ │ └── src │ │ ├── jsx-render.vue │ │ └── standard-form.vue ├── shims-vue.d.ts ├── store │ └── index.ts ├── types │ ├── global.d.ts │ └── index.d.ts ├── utils │ ├── color.ts │ ├── direction.ts │ ├── font.ts │ ├── gzip.ts │ ├── images.ts │ ├── index.ts │ ├── preview-mode.ts │ ├── request.ts │ └── types.ts └── vite-env.d.ts ├── tsconfig.json └── vite.config.ts /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "eslint:recommended", 5 | "plugin:oxlint/recommended", 6 | "plugin:vue/vue3-recommended", 7 | "plugin:@typescript-eslint/recommended" 8 | ], 9 | "parser": "vue-eslint-parser", 10 | "parserOptions": { 11 | "parser": "@typescript-eslint/parser", 12 | "sourceType": "module" 13 | }, 14 | "overrides": [ 15 | { 16 | "extends": ["plugin:@typescript-eslint/disable-type-checked"], 17 | "files": ["./**/*.js"] 18 | } 19 | ], 20 | "rules": { 21 | "@typescript-eslint/no-explicit-any": "off", 22 | "vue/multi-word-component-names": "off", 23 | "vue/max-attributes-per-line": ["warn", { "singleline": 4 }], 24 | "vue/require-default-prop": "off", 25 | "vue/no-reserved-component-names": "off", 26 | "vue/html-self-closing": [ 27 | "warn", 28 | { 29 | "svg": "nerver" 30 | } 31 | ] 32 | } 33 | } -------------------------------------------------------------------------------- /.github/workflows/main.deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Doc Website 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | main-deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | with: 14 | persist-credentials: false 15 | 16 | - name: Setup node 17 | uses: actions/setup-node@v2 18 | 19 | - name: Install dependencies 20 | run: yarn 21 | 22 | - name: Build Demo 23 | run: yarn build 24 | 25 | - name: Build CRX file 26 | run: yarn build:crx 27 | 28 | - name: Move CRX file 29 | run: yarn move:crx 30 | 31 | #- name: Build Electorn file 32 | # run: yarn build:electron 33 | 34 | - name: Deploy 35 | uses: JamesIves/github-pages-deploy-action@3.7.1 36 | with: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | BRANCH: gh-pages 39 | FOLDER: dist 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist-ssr 4 | *.local 5 | crx 6 | install 7 | electron 8 | yarn-error.log 9 | package-lock.json 10 | yarn.lock 11 | pnpm-lock.yaml 12 | stats.html 13 | dist/assets/* 14 | dist/img/* 15 | dist/howdz-dashboard.crx 16 | dist/howdz-dashboard.zip 17 | 18 | # Local ENV files 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | .env 24 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run lint -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // "editor.formatOnSave": true, 3 | "editor.codeActionsOnSave": { 4 | "source.fixAll.eslint": "explicit" 5 | }, 6 | "editor.tabSize": 2 7 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 Leon.D 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 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 |

2 |

Howdz

3 | 4 | # Howdz Dashboard 5 | 6 | Custom your personal **browser start page** from some **configurable components**. 7 | 8 | Written in `Vue3`, `Vite`, `Typescript`. 9 | 10 | - 中文文档 11 | 12 | ## Website 13 | 14 | - [Online Website #1](https://howdz.xyz/) 15 | - [Online Website #2](https://s.kongfandong.cn/) 16 | - [Chrome Extension](https://chromewebstore.google.com/detail/howdz%E8%B5%B7%E5%A7%8B%E9%A1%B5/ggglfehkglgpenacfalffmiojghklamm) 17 | - [Edge Extension](https://microsoftedge.microsoft.com/addons/detail/howdz%E8%B5%B7%E5%A7%8B%E9%A1%B5/cgcggcdgjfmeoemmdpleinicgepijegd) 18 | 19 | ## Feature 20 | 21 | - ✨Build in responsive, Custom drag to layout. 22 | - 💫Two mode to layout, based on document flow or fixed position mode. 23 | - 🍭The component will be configurable, includes it's function or style. 24 | - 🍌Data export for random key or json file. 25 | - 🎉Pick up a default theme when first enter. 26 | - 🌟Dynamic wallpaper is ready, config a video url or pick a recommand picture in background setting. 27 | - 📋`Tab Pages Mode`, allow to config **multiple pages**. 28 | - 🍦`Component action`, config that click the component to toggle an another component. 29 | - 🚀`Service worker` is supported to cache the static source. 30 | 31 | ## Screenshot 32 | 33 |

34 |

35 |

36 |

37 | 38 | ## Materials 39 | 40 | - Empty 41 | - Clock 42 | - Verse 43 | - Search 44 | - Collection 45 | - Iframe 46 | - TodoList 47 | - Weather 48 | - CountDown 49 | - Juejin 50 | - Weibo 51 | - GithubTrending 52 | - Day 53 | - ZhihuList 54 | - Editor 55 | - MovieLines 56 | - Bookmark 57 | - DailyHot 58 | 59 | ## Chrome extension mode 60 | 61 | 62 | 63 | - **Chrome** - [Install from store](https://chrome.google.com/webstore/detail/howdz%E8%B5%B7%E5%A7%8B%E9%A1%B5/ggglfehkglgpenacfalffmiojghklamm/related) 64 | - **Microsoft Edge** - [Install from store](https://microsoftedge.microsoft.com/addons/detail/howdz%E8%B5%B7%E5%A7%8B%E9%A1%B5/cgcggcdgjfmeoemmdpleinicgepijegd) 65 | 66 | ## More 67 | 68 | - [CHANGELOG](./CHANGELOG.zh-CN.md) 69 | - [Introduce Video](https://www.bilibili.com/video/BV1Vu411Z7i1?share_source=copy_web) 70 | - [FAQ](https://github.com/leon-kfd/Dashboard/blob/main/src/components/Axuiliary/FAQ.md) 71 | 72 | ## License 73 | 74 | All for [MIT](https://github.com/leon-kfd/Dashboard/blob/main/LICENSE) 75 | 76 | Copyright (c) 2025 Leon.D 77 | -------------------------------------------------------------------------------- /commitlint.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'] 3 | }; 4 | -------------------------------------------------------------------------------- /dist/background.js: -------------------------------------------------------------------------------- 1 | 2 | // chrome.browserAction.onClicked.addListener(function () { 3 | // chrome.management.getSelf(function (res) { 4 | // chrome.tabs.create({ url: 'chrome-extension://' + res.id + '/index.html' }); 5 | // }); 6 | // }); 7 | -------------------------------------------------------------------------------- /dist/favicon-edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/dist/favicon-edge.png -------------------------------------------------------------------------------- /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/dist/favicon.ico -------------------------------------------------------------------------------- /dist/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/dist/favicon.png -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 19 | 20 | 24 | 25 | 26 | 27 | Howdz 起始页 28 | 71 | 72 | 73 | 74 | 75 | 76 |
77 | 78 |
79 |
80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /dist/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Howdz起始页", 3 | "version": "1.5.5", 4 | "description": "提供大量组件用于定制化你的起始页,可适配响应式设计。", 5 | "icons": { 6 | "128": "favicon.png" 7 | }, 8 | "permissions": [], 9 | "background": { 10 | "service_worker": "sw.js" 11 | }, 12 | "offline_enabled": true, 13 | "manifest_version": 3, 14 | "chrome_url_overrides": { 15 | "newtab": "index.html" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /dist/sw.js: -------------------------------------------------------------------------------- 1 | 2 | if (location.href.includes('howdz.xyz')) { 3 | importScripts('https://cdn.staticfile.org/workbox-sw/7.0.0/workbox-sw.js') 4 | workbox.setConfig({ 5 | debug: false, 6 | }); 7 | console.log('sw.js is load by CDN!') 8 | } else { 9 | importScripts('./workbox/workbox-sw.js') 10 | workbox.setConfig({ 11 | debug: false, 12 | modulePathPrefix: './workbox/' 13 | }); 14 | console.log('sw.js is load by local!') 15 | } 16 | 17 | // Cache css/js/font. 18 | workbox.routing.registerRoute( 19 | ({ request }) => request.destination === 'style' || request.destination === 'script' || request.destination === 'font', 20 | new workbox.strategies.CacheFirst({ 21 | cacheName: 'css-js-font', 22 | plugins: [ 23 | new workbox.cacheableResponse.CacheableResponsePlugin({ 24 | statuses: [200], 25 | }), 26 | new workbox.expiration.ExpirationPlugin({ 27 | maxEntries: 50, 28 | maxAgeSeconds: 60 * 60 * 24 * 7, // 7 Days 29 | }), 30 | ] 31 | }) 32 | ); 33 | 34 | // Cache image. 35 | workbox.routing.registerRoute( 36 | ({ request }) => request.destination === 'image', 37 | new workbox.strategies.StaleWhileRevalidate({ 38 | cacheName: 'image', 39 | plugins: [ 40 | new workbox.cacheableResponse.CacheableResponsePlugin({ 41 | statuses: [200], 42 | }), 43 | new workbox.expiration.ExpirationPlugin({ 44 | maxEntries: 50, 45 | maxAgeSeconds: 60 * 60 * 24 * 7, // 7 Days 46 | }) 47 | ] 48 | }) 49 | ) 50 | 51 | // Cache video 52 | workbox.routing.registerRoute( 53 | ({ request }) => request.destination === 'video', 54 | new workbox.strategies.CacheFirst({ 55 | cacheName: 'video', 56 | plugins: [ 57 | new workbox.cacheableResponse.CacheableResponsePlugin({ 58 | statuses: [200], 59 | }), 60 | new workbox.expiration.ExpirationPlugin({ 61 | maxEntries: 50, 62 | maxAgeSeconds: 60 * 60 * 24 * 7, // 7 Days 63 | }), 64 | new workbox.rangeRequests.RangeRequestsPlugin() 65 | ] 66 | }) 67 | ) 68 | -------------------------------------------------------------------------------- /dist/workbox/workbox-cacheable-response.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.cacheableResponse=function(s){"use strict";try{self["workbox:cacheable-response:6.1.5"]&&_()}catch(s){}class t{constructor(s={}){this.j=s.statuses,this.O=s.headers}isResponseCacheable(s){let t=!0;return this.j&&(t=this.j.includes(s.status)),this.O&&t&&(t=Object.keys(this.O).some((t=>s.headers.get(t)===this.O[t]))),t}}return s.CacheableResponse=t,s.CacheableResponsePlugin=class{constructor(s){this.cacheWillUpdate=async({response:s})=>this.B.isResponseCacheable(s)?s:null,this.B=new t(s)}},s}({}); 2 | -------------------------------------------------------------------------------- /dist/workbox/workbox-expiration.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.expiration=function(t,s,e,i,a,n,h){"use strict";try{self["workbox:expiration:6.1.5"]&&_()}catch(t){}const r="cache-entries",c=t=>{const s=new URL(t,location.href);return s.hash="",s.href};class o{constructor(t){this.T=t,this.i=new e.DBWrapper("workbox-expiration",1,{onupgradeneeded:t=>this.M(t)})}M(t){const s=t.target.result.createObjectStore(r,{keyPath:"id"});s.createIndex("cacheName","cacheName",{unique:!1}),s.createIndex("timestamp","timestamp",{unique:!1}),i.deleteDatabase(this.T)}async setTimestamp(t,s){const e={url:t=c(t),timestamp:s,cacheName:this.T,id:this.L(t)};await this.i.put(r,e)}async getTimestamp(t){return(await this.i.get(r,this.L(t))).timestamp}async expireEntries(t,s){const e=await this.i.transaction(r,"readwrite",((e,i)=>{const a=e.objectStore(r).index("timestamp").openCursor(null,"prev"),n=[];let h=0;a.onsuccess=()=>{const e=a.result;if(e){const i=e.value;i.cacheName===this.T&&(t&&i.timestamp=s?n.push(e.value):h++),e.continue()}else i(n)}})),i=[];for(const t of e)await this.i.delete(r,t.id),i.push(t.url);return i}L(t){return this.T+"|"+c(t)}}class u{constructor(t,s={}){this.F=!1,this.H=!1,this.I=s.maxEntries,this.G=s.maxAgeSeconds,this.J=s.matchOptions,this.T=t,this.V=new o(t)}async expireEntries(){if(this.F)return void(this.H=!0);this.F=!0;const t=this.G?Date.now()-1e3*this.G:0,e=await this.V.expireEntries(t,this.I),i=await self.caches.open(this.T);for(const t of e)await i.delete(t,this.J);this.F=!1,this.H&&(this.H=!1,s.dontWaitFor(this.expireEntries()))}async updateTimestamp(t){await this.V.setTimestamp(t,Date.now())}async isURLExpired(t){if(this.G){return await this.V.getTimestamp(t){if(!a)return null;const n=this.W(a),h=this.X(i);s.dontWaitFor(h.expireEntries());const r=h.updateTimestamp(e.url);if(t)try{t.waitUntil(r)}catch(t){}return n?a:null},this.cacheDidUpdate=async({cacheName:t,request:s})=>{const e=this.X(t);await e.updateTimestamp(s.url),await e.expireEntries()},this.Y=t,this.G=t.maxAgeSeconds,this.Z=new Map,t.purgeOnQuotaError&&n.registerQuotaErrorCallback((()=>this.deleteCacheAndMetadata()))}X(t){if(t===a.cacheNames.getRuntimeName())throw new h.WorkboxError("expire-custom-caches-only");let s=this.Z.get(t);return s||(s=new u(t,this.Y),this.Z.set(t,s)),s}W(t){if(!this.G)return!0;const s=this.tt(t);if(null===s)return!0;return s>=Date.now()-1e3*this.G}tt(t){if(!t.headers.has("date"))return null;const s=t.headers.get("date"),e=new Date(s).getTime();return isNaN(e)?null:e}async deleteCacheAndMetadata(){for(const[t,s]of this.Z)await self.caches.delete(t),await s.delete();this.Z=new Map}},t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core,workbox.core._private); 2 | -------------------------------------------------------------------------------- /dist/workbox/workbox-range-requests.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.rangeRequests=function(t,e,n){"use strict";try{self["workbox:range-requests:5.1.4"]&&_()}catch(t){}async function r(t,n){try{if(206===n.status)return n;const r=t.headers.get("range");if(!r)throw new e.WorkboxError("no-range-header");const s=function(t){const n=t.trim().toLowerCase();if(!n.startsWith("bytes="))throw new e.WorkboxError("unit-must-be-bytes",{normalizedRangeHeader:n});if(n.includes(","))throw new e.WorkboxError("single-range-only",{normalizedRangeHeader:n});const r=/(\d*)-(\d*)/.exec(n);if(!r||!r[1]&&!r[2])throw new e.WorkboxError("invalid-range-values",{normalizedRangeHeader:n});return{start:""===r[1]?void 0:Number(r[1]),end:""===r[2]?void 0:Number(r[2])}}(r),a=await n.blob(),o=function(t,n,r){const s=t.size;if(r&&r>s||n&&n<0)throw new e.WorkboxError("range-not-satisfiable",{size:s,end:r,start:n});let a,o;return void 0!==n&&void 0!==r?(a=n,o=r+1):void 0!==n&&void 0===r?(a=n,o=s):void 0!==r&&void 0===n&&(a=s-r,o=s),{start:a,end:o}}(a,s.start,s.end),i=a.slice(o.start,o.end),d=i.size,u=new Response(i,{status:206,statusText:"Partial Content",headers:n.headers});return u.headers.set("Content-Length",String(d)),u.headers.set("Content-Range",`bytes ${o.start}-${o.end-1}/`+a.size),u}catch(t){return new Response("",{status:416,statusText:"Range Not Satisfiable"})}}return t.RangeRequestsPlugin=class{constructor(){this.cachedResponseWillBeUsed=async({request:t,cachedResponse:e})=>e&&t.headers.has("range")?await r(t,e):e}},t.createPartialResponse=r,t}({},workbox.core._private,workbox.core._private); 2 | 3 | -------------------------------------------------------------------------------- /dist/workbox/workbox-routing.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.routing=function(t,e){"use strict";try{self["workbox:routing:6.1.5"]&&_()}catch(t){}const s=t=>t&&"object"==typeof t?t:{handle:t};class r{constructor(t,e,r="GET"){this.handler=s(e),this.match=t,this.method=r}setCatchHandler(t){this.catchHandler=s(t)}}class n extends r{constructor(t,e,s){super((({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)}),e,s)}}class i{constructor(){this.ft=new Map,this.dt=new Map}get routes(){return this.ft}addFetchListener(){self.addEventListener("fetch",(t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map((e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})})));t.waitUntil(s),t.ports&&t.ports[0]&&s.then((()=>t.ports[0].postMessage(!0)))}}))}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const r=s.origin===location.origin,{params:n,route:i}=this.findMatchingRoute({event:e,request:t,sameOrigin:r,url:s});let o=i&&i.handler;const u=t.method;if(!o&&this.dt.has(u)&&(o=this.dt.get(u)),!o)return;let c;try{c=o.handle({url:s,request:t,event:e,params:n})}catch(t){c=Promise.reject(t)}const a=i&&i.catchHandler;return c instanceof Promise&&(this.wt||a)&&(c=c.catch((async r=>{if(a)try{return await a.handle({url:s,request:t,event:e,params:n})}catch(t){r=t}if(this.wt)return this.wt.handle({url:s,request:t,event:e});throw r}))),c}findMatchingRoute({url:t,sameOrigin:e,request:s,event:r}){const n=this.ft.get(s.method)||[];for(const i of n){let n;const o=i.match({url:t,sameOrigin:e,request:s,event:r});if(o)return n=o,(Array.isArray(o)&&0===o.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(n=void 0),{route:i,params:n}}return{}}setDefaultHandler(t,e="GET"){this.dt.set(e,s(t))}setCatchHandler(t){this.wt=s(t)}registerRoute(t){this.ft.has(t.method)||this.ft.set(t.method,[]),this.ft.get(t.method).push(t)}unregisterRoute(t){if(!this.ft.has(t.method))throw new e.WorkboxError("unregister-route-but-not-found-with-method",{method:t.method});const s=this.ft.get(t.method).indexOf(t);if(!(s>-1))throw new e.WorkboxError("unregister-route-route-not-registered");this.ft.get(t.method).splice(s,1)}}let o;const u=()=>(o||(o=new i,o.addFetchListener(),o.addCacheListener()),o);return t.NavigationRoute=class extends r{constructor(t,{allowlist:e=[/./],denylist:s=[]}={}){super((t=>this.gt(t)),t),this.qt=e,this.yt=s}gt({url:t,request:e}){if(e&&"navigate"!==e.mode)return!1;const s=t.pathname+t.search;for(const t of this.yt)if(t.test(s))return!1;return!!this.qt.some((t=>t.test(s)))}},t.RegExpRoute=n,t.Route=r,t.Router=i,t.registerRoute=function(t,s,i){let o;if("string"==typeof t){const e=new URL(t,location.href);o=new r((({url:t})=>t.href===e.href),s,i)}else if(t instanceof RegExp)o=new n(t,s,i);else if("function"==typeof t)o=new r(t,s,i);else{if(!(t instanceof r))throw new e.WorkboxError("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});o=t}return u().registerRoute(o),o},t.setCatchHandler=function(t){u().setCatchHandler(t)},t.setDefaultHandler=function(t){u().setDefaultHandler(t)},t}({},workbox.core._private); 2 | -------------------------------------------------------------------------------- /dist/workbox/workbox-sw.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";try{self["workbox:sw:6.1.5"]&&_()}catch(t){}const t={backgroundSync:"background-sync",broadcastUpdate:"broadcast-update",cacheableResponse:"cacheable-response",core:"core",expiration:"expiration",googleAnalytics:"offline-ga",navigationPreload:"navigation-preload",precaching:"precaching",rangeRequests:"range-requests",routing:"routing",strategies:"strategies",streams:"streams",recipes:"recipes"};self.workbox=new class{constructor(){return this.v={},this.Pt={debug:"localhost"===self.location.hostname,modulePathPrefix:null,modulePathCb:null},this.$t=this.Pt.debug?"dev":"prod",this.jt=!1,new Proxy(this,{get(e,s){if(e[s])return e[s];const o=t[s];return o&&e.loadModule(`workbox-${o}`),e[s]}})}setConfig(t={}){if(this.jt)throw new Error("Config must be set before accessing workbox.* modules");Object.assign(this.Pt,t),this.$t=this.Pt.debug?"dev":"prod"}loadModule(t){const e=this.St(t);try{importScripts(e),this.jt=!0}catch(s){throw console.error(`Unable to import module '${t}' from '${e}'.`),s}}St(t){if(this.Pt.modulePathCb)return this.Pt.modulePathCb(t,this.Pt.debug);let e=["https://storage.googleapis.com/workbox-cdn/releases/6.1.5"];const s=`${t}.${this.$t}.js`,o=this.Pt.modulePathPrefix;return o&&(e=o.split("/"),""===e[e.length-1]&&e.splice(e.length-1,1)),e.push(s),e.join("/")}}}(); 2 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 19 | 20 | 24 | 25 | 26 | 27 | Howdz 起始页 28 | 71 | 72 | 73 | 74 |
75 | 76 |
77 |
78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "howdz-dashboard", 3 | "version": "1.5.5", 4 | "description": "Custom your personal browser start page from some configurable components", 5 | "author": { 6 | "name": "leon-kfd", 7 | "email": "kfd_personal@163.com" 8 | }, 9 | "keywords": [ 10 | "javascript", 11 | "vue", 12 | "vite", 13 | "typescript", 14 | "dashboard", 15 | "drag", 16 | "custom" 17 | ], 18 | "type": "module", 19 | "licence": "MIT", 20 | "scripts": { 21 | "prepare": "husky install", 22 | "commit": "cz", 23 | "dev": "vite", 24 | "build": "cross-env VITE_APP_BUILD_MODE=cdn vite build", 25 | "upload": "node scripts/upload-cdn.cjs", 26 | "serve": "vite preview", 27 | "build:crx": "cross-env VITE_APP_BUILD_MODE=crx vite build", 28 | "move:crx": "node scripts/build-crx.cjs", 29 | "lint": "oxlint src" 30 | }, 31 | "config": { 32 | "commitizen": { 33 | "path": "cz-conventional-changelog" 34 | } 35 | }, 36 | "husky": { 37 | "hooks": { 38 | "commit-msg": "npx --no-install commitlint --edit '$1'", 39 | "pre-commit": "npm run lint" 40 | } 41 | }, 42 | "dependencies": { 43 | "@emotion/css": "^11.7.1", 44 | "@howdyjs/mouse-menu": "^2.1.1", 45 | "@howdyjs/to-control": "^2.1.1", 46 | "@milkdown/core": "5.3.1", 47 | "@milkdown/plugin-clipboard": "5.3.1", 48 | "@milkdown/plugin-collaborative": "5.3.1", 49 | "@milkdown/plugin-history": "5.3.1", 50 | "@milkdown/plugin-listener": "5.3.1", 51 | "@milkdown/plugin-math": "5.3.1", 52 | "@milkdown/plugin-slash": "5.3.1", 53 | "@milkdown/plugin-tooltip": "5.3.1", 54 | "@milkdown/preset-commonmark": "5.3.1", 55 | "@milkdown/preset-gfm": "5.3.1", 56 | "@milkdown/theme-nord": "5.3.1", 57 | "dayjs": "^1.10.4", 58 | "element-plus": "2.4.1", 59 | "eslint-plugin-oxlint": "^0.2.7", 60 | "file-saver": "^2.0.5", 61 | "js-md5": "^0.7.3", 62 | "localforage": "^1.10.0", 63 | "pako": "1.0.11", 64 | "pinia": "^2.0.11", 65 | "pinia-plugin-persistedstate": "^1.5.0", 66 | "rough-notation": "^0.5.1", 67 | "vue": "^3.2.4", 68 | "vue-eslint-parser": "^9.4.2", 69 | "vue-grid-layout": "3.0.0-beta1", 70 | "vue-i18n": "9", 71 | "vuedraggable": "^4.0.1" 72 | }, 73 | "devDependencies": { 74 | "@commitlint/config-conventional": "^16.2.4", 75 | "@intlify/unplugin-vue-i18n": "^1.4.0", 76 | "@types/file-saver": "^2.0.2", 77 | "@types/js-md5": "^0.4.2", 78 | "@types/node": "^14.14.37", 79 | "@types/pako": "^1.0.6", 80 | "@typescript-eslint/eslint-plugin": "^6.21.0", 81 | "@typescript-eslint/parser": "^6.21.0", 82 | "@vitejs/plugin-vue": "^4.4.0", 83 | "@vitejs/plugin-vue-jsx": "^3.0.2", 84 | "adm-zip": "^0.5.5", 85 | "commitizen": "^4.2.4", 86 | "commitlint": "^16.2.4", 87 | "cross-env": "^7.0.3", 88 | "cz-conventional-changelog": "^3.3.0", 89 | "dotenv": "^16.3.1", 90 | "eslint": "^8.57.0", 91 | "eslint-plugin-vue": "^9.24.0", 92 | "husky": "^7.0.4", 93 | "oxlint": "^0.2.16", 94 | "qiniu": "^7.9.0", 95 | "rimraf": "^3.0.2", 96 | "sass": "^1.68.0", 97 | "typescript": "5.2.2", 98 | "vite": "^5.2.8", 99 | "vite-plugin-md": "^0.21.5" 100 | }, 101 | "repository": { 102 | "type": "git", 103 | "url": "https://github.com/leon-kfd/dashboard" 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /public/background.js: -------------------------------------------------------------------------------- 1 | 2 | // chrome.browserAction.onClicked.addListener(function () { 3 | // chrome.management.getSelf(function (res) { 4 | // chrome.tabs.create({ url: 'chrome-extension://' + res.id + '/index.html' }); 5 | // }); 6 | // }); 7 | -------------------------------------------------------------------------------- /public/favicon-edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/public/favicon-edge.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/public/favicon.png -------------------------------------------------------------------------------- /public/img/icons/bing-en.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/icons/bing.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/icons/google.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/icons/sougou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/icons/taobao.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Howdz起始页", 3 | "version": "1.5.5", 4 | "description": "提供大量组件用于定制化你的起始页,可适配响应式设计。", 5 | "icons": { 6 | "128": "favicon.png" 7 | }, 8 | "permissions": [], 9 | "background": { 10 | "service_worker": "sw.js" 11 | }, 12 | "offline_enabled": true, 13 | "manifest_version": 3, 14 | "chrome_url_overrides": { 15 | "newtab": "index.html" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /public/sw.js: -------------------------------------------------------------------------------- 1 | 2 | if (location.href.includes('howdz.xyz')) { 3 | importScripts('https://cdn.staticfile.org/workbox-sw/7.0.0/workbox-sw.js') 4 | workbox.setConfig({ 5 | debug: false, 6 | }); 7 | console.log('sw.js is load by CDN!') 8 | } else { 9 | importScripts('./workbox/workbox-sw.js') 10 | workbox.setConfig({ 11 | debug: false, 12 | modulePathPrefix: './workbox/' 13 | }); 14 | console.log('sw.js is load by local!') 15 | } 16 | 17 | // Cache css/js/font. 18 | workbox.routing.registerRoute( 19 | ({ request }) => request.destination === 'style' || request.destination === 'script' || request.destination === 'font', 20 | new workbox.strategies.CacheFirst({ 21 | cacheName: 'css-js-font', 22 | plugins: [ 23 | new workbox.cacheableResponse.CacheableResponsePlugin({ 24 | statuses: [200], 25 | }), 26 | new workbox.expiration.ExpirationPlugin({ 27 | maxEntries: 50, 28 | maxAgeSeconds: 60 * 60 * 24 * 7, // 7 Days 29 | }), 30 | ] 31 | }) 32 | ); 33 | 34 | // Cache image. 35 | workbox.routing.registerRoute( 36 | ({ request }) => request.destination === 'image', 37 | new workbox.strategies.StaleWhileRevalidate({ 38 | cacheName: 'image', 39 | plugins: [ 40 | new workbox.cacheableResponse.CacheableResponsePlugin({ 41 | statuses: [200], 42 | }), 43 | new workbox.expiration.ExpirationPlugin({ 44 | maxEntries: 50, 45 | maxAgeSeconds: 60 * 60 * 24 * 7, // 7 Days 46 | }) 47 | ] 48 | }) 49 | ) 50 | 51 | // Cache video 52 | workbox.routing.registerRoute( 53 | ({ request }) => request.destination === 'video', 54 | new workbox.strategies.CacheFirst({ 55 | cacheName: 'video', 56 | plugins: [ 57 | new workbox.cacheableResponse.CacheableResponsePlugin({ 58 | statuses: [200], 59 | }), 60 | new workbox.expiration.ExpirationPlugin({ 61 | maxEntries: 50, 62 | maxAgeSeconds: 60 * 60 * 24 * 7, // 7 Days 63 | }), 64 | new workbox.rangeRequests.RangeRequestsPlugin() 65 | ] 66 | }) 67 | ) 68 | -------------------------------------------------------------------------------- /public/workbox/workbox-cacheable-response.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.cacheableResponse=function(s){"use strict";try{self["workbox:cacheable-response:6.1.5"]&&_()}catch(s){}class t{constructor(s={}){this.j=s.statuses,this.O=s.headers}isResponseCacheable(s){let t=!0;return this.j&&(t=this.j.includes(s.status)),this.O&&t&&(t=Object.keys(this.O).some((t=>s.headers.get(t)===this.O[t]))),t}}return s.CacheableResponse=t,s.CacheableResponsePlugin=class{constructor(s){this.cacheWillUpdate=async({response:s})=>this.B.isResponseCacheable(s)?s:null,this.B=new t(s)}},s}({}); 2 | -------------------------------------------------------------------------------- /public/workbox/workbox-expiration.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.expiration=function(t,s,e,i,a,n,h){"use strict";try{self["workbox:expiration:6.1.5"]&&_()}catch(t){}const r="cache-entries",c=t=>{const s=new URL(t,location.href);return s.hash="",s.href};class o{constructor(t){this.T=t,this.i=new e.DBWrapper("workbox-expiration",1,{onupgradeneeded:t=>this.M(t)})}M(t){const s=t.target.result.createObjectStore(r,{keyPath:"id"});s.createIndex("cacheName","cacheName",{unique:!1}),s.createIndex("timestamp","timestamp",{unique:!1}),i.deleteDatabase(this.T)}async setTimestamp(t,s){const e={url:t=c(t),timestamp:s,cacheName:this.T,id:this.L(t)};await this.i.put(r,e)}async getTimestamp(t){return(await this.i.get(r,this.L(t))).timestamp}async expireEntries(t,s){const e=await this.i.transaction(r,"readwrite",((e,i)=>{const a=e.objectStore(r).index("timestamp").openCursor(null,"prev"),n=[];let h=0;a.onsuccess=()=>{const e=a.result;if(e){const i=e.value;i.cacheName===this.T&&(t&&i.timestamp=s?n.push(e.value):h++),e.continue()}else i(n)}})),i=[];for(const t of e)await this.i.delete(r,t.id),i.push(t.url);return i}L(t){return this.T+"|"+c(t)}}class u{constructor(t,s={}){this.F=!1,this.H=!1,this.I=s.maxEntries,this.G=s.maxAgeSeconds,this.J=s.matchOptions,this.T=t,this.V=new o(t)}async expireEntries(){if(this.F)return void(this.H=!0);this.F=!0;const t=this.G?Date.now()-1e3*this.G:0,e=await this.V.expireEntries(t,this.I),i=await self.caches.open(this.T);for(const t of e)await i.delete(t,this.J);this.F=!1,this.H&&(this.H=!1,s.dontWaitFor(this.expireEntries()))}async updateTimestamp(t){await this.V.setTimestamp(t,Date.now())}async isURLExpired(t){if(this.G){return await this.V.getTimestamp(t){if(!a)return null;const n=this.W(a),h=this.X(i);s.dontWaitFor(h.expireEntries());const r=h.updateTimestamp(e.url);if(t)try{t.waitUntil(r)}catch(t){}return n?a:null},this.cacheDidUpdate=async({cacheName:t,request:s})=>{const e=this.X(t);await e.updateTimestamp(s.url),await e.expireEntries()},this.Y=t,this.G=t.maxAgeSeconds,this.Z=new Map,t.purgeOnQuotaError&&n.registerQuotaErrorCallback((()=>this.deleteCacheAndMetadata()))}X(t){if(t===a.cacheNames.getRuntimeName())throw new h.WorkboxError("expire-custom-caches-only");let s=this.Z.get(t);return s||(s=new u(t,this.Y),this.Z.set(t,s)),s}W(t){if(!this.G)return!0;const s=this.tt(t);if(null===s)return!0;return s>=Date.now()-1e3*this.G}tt(t){if(!t.headers.has("date"))return null;const s=t.headers.get("date"),e=new Date(s).getTime();return isNaN(e)?null:e}async deleteCacheAndMetadata(){for(const[t,s]of this.Z)await self.caches.delete(t),await s.delete();this.Z=new Map}},t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core,workbox.core._private); 2 | -------------------------------------------------------------------------------- /public/workbox/workbox-range-requests.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.rangeRequests=function(t,e,n){"use strict";try{self["workbox:range-requests:5.1.4"]&&_()}catch(t){}async function r(t,n){try{if(206===n.status)return n;const r=t.headers.get("range");if(!r)throw new e.WorkboxError("no-range-header");const s=function(t){const n=t.trim().toLowerCase();if(!n.startsWith("bytes="))throw new e.WorkboxError("unit-must-be-bytes",{normalizedRangeHeader:n});if(n.includes(","))throw new e.WorkboxError("single-range-only",{normalizedRangeHeader:n});const r=/(\d*)-(\d*)/.exec(n);if(!r||!r[1]&&!r[2])throw new e.WorkboxError("invalid-range-values",{normalizedRangeHeader:n});return{start:""===r[1]?void 0:Number(r[1]),end:""===r[2]?void 0:Number(r[2])}}(r),a=await n.blob(),o=function(t,n,r){const s=t.size;if(r&&r>s||n&&n<0)throw new e.WorkboxError("range-not-satisfiable",{size:s,end:r,start:n});let a,o;return void 0!==n&&void 0!==r?(a=n,o=r+1):void 0!==n&&void 0===r?(a=n,o=s):void 0!==r&&void 0===n&&(a=s-r,o=s),{start:a,end:o}}(a,s.start,s.end),i=a.slice(o.start,o.end),d=i.size,u=new Response(i,{status:206,statusText:"Partial Content",headers:n.headers});return u.headers.set("Content-Length",String(d)),u.headers.set("Content-Range",`bytes ${o.start}-${o.end-1}/`+a.size),u}catch(t){return new Response("",{status:416,statusText:"Range Not Satisfiable"})}}return t.RangeRequestsPlugin=class{constructor(){this.cachedResponseWillBeUsed=async({request:t,cachedResponse:e})=>e&&t.headers.has("range")?await r(t,e):e}},t.createPartialResponse=r,t}({},workbox.core._private,workbox.core._private); 2 | 3 | -------------------------------------------------------------------------------- /public/workbox/workbox-routing.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.routing=function(t,e){"use strict";try{self["workbox:routing:6.1.5"]&&_()}catch(t){}const s=t=>t&&"object"==typeof t?t:{handle:t};class r{constructor(t,e,r="GET"){this.handler=s(e),this.match=t,this.method=r}setCatchHandler(t){this.catchHandler=s(t)}}class n extends r{constructor(t,e,s){super((({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)}),e,s)}}class i{constructor(){this.ft=new Map,this.dt=new Map}get routes(){return this.ft}addFetchListener(){self.addEventListener("fetch",(t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map((e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})})));t.waitUntil(s),t.ports&&t.ports[0]&&s.then((()=>t.ports[0].postMessage(!0)))}}))}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const r=s.origin===location.origin,{params:n,route:i}=this.findMatchingRoute({event:e,request:t,sameOrigin:r,url:s});let o=i&&i.handler;const u=t.method;if(!o&&this.dt.has(u)&&(o=this.dt.get(u)),!o)return;let c;try{c=o.handle({url:s,request:t,event:e,params:n})}catch(t){c=Promise.reject(t)}const a=i&&i.catchHandler;return c instanceof Promise&&(this.wt||a)&&(c=c.catch((async r=>{if(a)try{return await a.handle({url:s,request:t,event:e,params:n})}catch(t){r=t}if(this.wt)return this.wt.handle({url:s,request:t,event:e});throw r}))),c}findMatchingRoute({url:t,sameOrigin:e,request:s,event:r}){const n=this.ft.get(s.method)||[];for(const i of n){let n;const o=i.match({url:t,sameOrigin:e,request:s,event:r});if(o)return n=o,(Array.isArray(o)&&0===o.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(n=void 0),{route:i,params:n}}return{}}setDefaultHandler(t,e="GET"){this.dt.set(e,s(t))}setCatchHandler(t){this.wt=s(t)}registerRoute(t){this.ft.has(t.method)||this.ft.set(t.method,[]),this.ft.get(t.method).push(t)}unregisterRoute(t){if(!this.ft.has(t.method))throw new e.WorkboxError("unregister-route-but-not-found-with-method",{method:t.method});const s=this.ft.get(t.method).indexOf(t);if(!(s>-1))throw new e.WorkboxError("unregister-route-route-not-registered");this.ft.get(t.method).splice(s,1)}}let o;const u=()=>(o||(o=new i,o.addFetchListener(),o.addCacheListener()),o);return t.NavigationRoute=class extends r{constructor(t,{allowlist:e=[/./],denylist:s=[]}={}){super((t=>this.gt(t)),t),this.qt=e,this.yt=s}gt({url:t,request:e}){if(e&&"navigate"!==e.mode)return!1;const s=t.pathname+t.search;for(const t of this.yt)if(t.test(s))return!1;return!!this.qt.some((t=>t.test(s)))}},t.RegExpRoute=n,t.Route=r,t.Router=i,t.registerRoute=function(t,s,i){let o;if("string"==typeof t){const e=new URL(t,location.href);o=new r((({url:t})=>t.href===e.href),s,i)}else if(t instanceof RegExp)o=new n(t,s,i);else if("function"==typeof t)o=new r(t,s,i);else{if(!(t instanceof r))throw new e.WorkboxError("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});o=t}return u().registerRoute(o),o},t.setCatchHandler=function(t){u().setCatchHandler(t)},t.setDefaultHandler=function(t){u().setDefaultHandler(t)},t}({},workbox.core._private); 2 | -------------------------------------------------------------------------------- /public/workbox/workbox-sw.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";try{self["workbox:sw:6.1.5"]&&_()}catch(t){}const t={backgroundSync:"background-sync",broadcastUpdate:"broadcast-update",cacheableResponse:"cacheable-response",core:"core",expiration:"expiration",googleAnalytics:"offline-ga",navigationPreload:"navigation-preload",precaching:"precaching",rangeRequests:"range-requests",routing:"routing",strategies:"strategies",streams:"streams",recipes:"recipes"};self.workbox=new class{constructor(){return this.v={},this.Pt={debug:"localhost"===self.location.hostname,modulePathPrefix:null,modulePathCb:null},this.$t=this.Pt.debug?"dev":"prod",this.jt=!1,new Proxy(this,{get(e,s){if(e[s])return e[s];const o=t[s];return o&&e.loadModule(`workbox-${o}`),e[s]}})}setConfig(t={}){if(this.jt)throw new Error("Config must be set before accessing workbox.* modules");Object.assign(this.Pt,t),this.$t=this.Pt.debug?"dev":"prod"}loadModule(t){const e=this.St(t);try{importScripts(e),this.jt=!0}catch(s){throw console.error(`Unable to import module '${t}' from '${e}'.`),s}}St(t){if(this.Pt.modulePathCb)return this.Pt.modulePathCb(t,this.Pt.debug);let e=["https://storage.googleapis.com/workbox-cdn/releases/6.1.5"];const s=`${t}.${this.$t}.js`,o=this.Pt.modulePathPrefix;return o&&(e=o.split("/"),""===e[e.length-1]&&e.splice(e.length-1,1)),e.push(s),e.join("/")}}}(); 2 | -------------------------------------------------------------------------------- /scripts/build-crx.cjs: -------------------------------------------------------------------------------- 1 | const AdmZip = require('adm-zip'); 2 | const zip = new AdmZip(); 3 | zip.addLocalFolder('crx'); 4 | zip.writeZip('dist/howdz-dashboard.crx'); 5 | zip.writeZip('dist/howdz-dashboard.zip'); 6 | console.log('build crx file success!'); 7 | -------------------------------------------------------------------------------- /scripts/upload-cdn.cjs: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const fs = require('fs') 3 | const qiniu = require('qiniu'); 4 | const QiniuAccessKey = process.env.QINIU_ACCESS_KEY 5 | const QinuiSecretKey = process.env.QINIU_SECRET_KEY 6 | const mac = new qiniu.auth.digest.Mac(QiniuAccessKey, QinuiSecretKey); 7 | const bucket = 'howdy' 8 | const bucketManager = new qiniu.rs.BucketManager(mac) 9 | 10 | const clearAssets = async () => { 11 | try { 12 | const assetsFileList = await new Promise((resolve, reject) => { 13 | bucketManager.listPrefix(bucket, { 14 | limit: 100, 15 | prefix: 'howdz/dist/assets/', 16 | }, function(err, respBody, respInfo) { 17 | if (err) { 18 | reject(err) 19 | } 20 | if (respInfo.statusCode === 200) { 21 | const list = respBody.items.map(item => item.key) 22 | resolve(list) 23 | } else { 24 | reject(new Error('Server error')) 25 | } 26 | }); 27 | }) 28 | const deleteOperations = assetsFileList.map(item => { 29 | return qiniu.rs.deleteOp(bucket, item) 30 | }) 31 | deleteOperations.push(qiniu.rs.deleteOp(bucket, 'howdz/dist/index.html')) 32 | await new Promise((resolve, reject) => { 33 | bucketManager.batch(deleteOperations, function(err, respBody, respInfo) { 34 | if (err) { 35 | reject(err) 36 | } else { 37 | if (parseInt(respInfo.statusCode / 100) === 2) { 38 | resolve(1) 39 | } else { 40 | reject(new Error('Batch delete error')) 41 | } 42 | } 43 | }) 44 | }) 45 | } catch (e) { 46 | console.error(e) 47 | } 48 | } 49 | 50 | const uploadFile = async (fileString, fileName, isAssets = true) => { 51 | if (!fileName) { 52 | throw new Error('Lose filename') 53 | } 54 | const key = `howdz/dist/${isAssets ? 'assets/' : ''}${fileName}` 55 | const putPolicy = new qiniu.rs.PutPolicy({ 56 | scope: bucket 57 | }); 58 | const uploadToken = putPolicy.uploadToken(mac); 59 | const formUploader = new qiniu.form_up.FormUploader(); 60 | const putExtra = new qiniu.form_up.PutExtra(); 61 | return await new Promise((resolve, reject) => { 62 | formUploader.putFile(uploadToken, key, fileString, putExtra, function(err, ret) { 63 | if (!err) { 64 | resolve(ret) 65 | } else { 66 | reject(err) 67 | } 68 | }) 69 | }) 70 | } 71 | 72 | const uploadAssets = () => { 73 | const assetsFileList = fs.readdirSync('dist/assets') 74 | const task = assetsFileList.map(item => { 75 | return uploadFile(`dist/assets/${item}`, item, true) 76 | }) 77 | return Promise.all(task) 78 | } 79 | 80 | const task = async () => { 81 | try { 82 | await clearAssets() 83 | await uploadAssets() 84 | await uploadFile('dist/index.html', 'index.html', false) 85 | if (fs.existsSync('dist/howdz-dashboard.zip')) { 86 | await uploadFile('dist/howdz-dashboard.zip', 'howdz-dashboard.zip', false) 87 | } 88 | if (fs.existsSync('dist/howdz-dashboard.crx')) { 89 | await uploadFile('dist/howdz-dashboard.crx', 'howdz-dashboard.crx', false) 90 | } 91 | console.log('upload success') 92 | } catch (e) { 93 | console.error(e) 94 | } 95 | } 96 | 97 | task() 98 | -------------------------------------------------------------------------------- /src/assets/custom-animate.scss: -------------------------------------------------------------------------------- 1 | /* animate.css flipInY*/ 2 | @keyframes flipInY { 3 | from { 4 | transform: perspective(400px) scale(0.5) rotate3d(0, 1, 0, 90deg); 5 | animation-timing-function: ease-in; 6 | opacity: 0; 7 | } 8 | 9 | 40% { 10 | transform: perspective(400px) scale(1) rotate3d(0, 1, 0, -20deg); 11 | animation-timing-function: ease-in; 12 | } 13 | 14 | 60% { 15 | transform: perspective(400px) rotate3d(0, 1, 0, 10deg); 16 | opacity: 1; 17 | } 18 | 19 | 80% { 20 | transform: perspective(400px) rotate3d(0, 1, 0, -5deg); 21 | } 22 | 23 | to { 24 | transform: perspective(400px); 25 | } 26 | } 27 | .animate__flipInY { 28 | backface-visibility: visible !important; 29 | animation-name: flipInY; 30 | } 31 | 32 | /* vue transition fadeIn */ 33 | .fadeIn-enter-active { 34 | animation: fadeInCustom 0.5s; 35 | } 36 | .fadeIn-leave-active { 37 | animation: fadeInCustom 0.5s reverse; 38 | } 39 | .fadeOut-leave-active { 40 | animation: fadeInCustom 0.5s reverse; 41 | } 42 | @keyframes fadeInCustom { 43 | from { 44 | opacity: 0; 45 | } 46 | to { 47 | opacity: 1; 48 | } 49 | } 50 | 51 | /* vue transition fadeInUp */ 52 | .fadeInUp-enter-active { 53 | animation: fadeInUpCustom 0.5s; 54 | } 55 | .fadeInUp-leave-active { 56 | animation: fadeInUpCustom 0.5s reverse; 57 | } 58 | @keyframes fadeInUpCustom { 59 | from { 60 | opacity: 0; 61 | transform: translateY(10px); 62 | } 63 | to { 64 | opacity: 1; 65 | transform: translateY(0); 66 | } 67 | } 68 | 69 | @keyframes bgmove { 70 | 0% { 71 | background-position: 0 0; 72 | } 73 | 50% { 74 | background-position: 100% 0; 75 | } 76 | 100% { 77 | background-position: 0 0; 78 | } 79 | } 80 | 81 | .zoomIn-enter-active { 82 | animation: zoomInCustom 0.4s; 83 | } 84 | .zoomIn-leave-active { 85 | animation: zoomInCustom 0.4s reverse; 86 | } 87 | @keyframes zoomInCustom { 88 | from { 89 | opacity: 0; 90 | transform: scale(0); 91 | } 92 | to { 93 | opacity: 1; 94 | transform: scale(1); 95 | } 96 | } 97 | 98 | 99 | .infinty-loading { 100 | animation: infintyLoading infinite 2s; 101 | } 102 | @keyframes infintyLoading { 103 | from { 104 | transform: rotate(0); 105 | } 106 | to { 107 | transform: rotate(360deg); 108 | } 109 | } -------------------------------------------------------------------------------- /src/assets/element-modules.scss: -------------------------------------------------------------------------------- 1 | @use "element-plus/theme-chalk/src/base.scss"; 2 | @use "element-plus/theme-chalk/src/dialog.scss"; 3 | @use "element-plus/theme-chalk/src/input.scss"; 4 | @use "element-plus/theme-chalk/src/input-number.scss"; 5 | @use "element-plus/theme-chalk/src/radio.scss"; 6 | @use "element-plus/theme-chalk/src/radio-group.scss"; 7 | @use "element-plus/theme-chalk/src/radio-button.scss"; 8 | @use "element-plus/theme-chalk/src/checkbox.scss"; 9 | @use "element-plus/theme-chalk/src/checkbox-button.scss"; 10 | @use "element-plus/theme-chalk/src/checkbox-group.scss"; 11 | @use "element-plus/theme-chalk/src/switch.scss"; 12 | @use "element-plus/theme-chalk/src/select.scss"; 13 | @use "element-plus/theme-chalk/src/option.scss"; 14 | @use "element-plus/theme-chalk/src/button.scss"; 15 | @use "element-plus/theme-chalk/src/date-picker.scss"; 16 | @use "element-plus/theme-chalk/src/tooltip.scss"; 17 | @use "element-plus/theme-chalk/src/popper.scss"; 18 | @use "element-plus/theme-chalk/src/form.scss"; 19 | @use "element-plus/theme-chalk/src/form-item.scss"; 20 | @use "element-plus/theme-chalk/src/tabs.scss"; 21 | @use "element-plus/theme-chalk/src/tab-pane.scss"; 22 | @use "element-plus/theme-chalk/src/notification.scss"; 23 | @use "element-plus/theme-chalk/src/message.scss"; 24 | @use "element-plus/theme-chalk/src/color-picker.scss"; 25 | @use "element-plus/theme-chalk/src/image.scss"; 26 | @use "element-plus/theme-chalk/src/alert.scss"; 27 | @use "element-plus/theme-chalk/src/select-dropdown.scss"; 28 | @use "element-plus/theme-chalk/src/scrollbar.scss"; 29 | -------------------------------------------------------------------------------- /src/assets/element-variables.scss: -------------------------------------------------------------------------------- 1 | @forward "element-plus/theme-chalk/src/common/var.scss" with ( 2 | $colors: ( 3 | "primary": ("base": rgb(46, 90, 219)), 4 | "success": ("base": rgb(100, 201, 53)), 5 | "warning": ("base": rgb(233, 174, 49)), 6 | "danger": ("base": rgb(216, 94, 94)), 7 | ), 8 | $border-radius: ( 9 | 'base': 0, 10 | 'small': 0 11 | ), 12 | $input: ( 13 | 'text-color': '#464649', 14 | 'bg-color': '#F7F7F9', 15 | 'placeholder-color': '#8D8DA5' 16 | ), 17 | $popover: ( 18 | 'border-radius': 0 19 | ), 20 | $popper: ( 21 | 'border-radius': 0 22 | ) 23 | ); 24 | @import "@/assets/variables.scss"; -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/Bookmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/Bookmark.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/Clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/Clock.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/Collection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/Collection.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/CountDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/CountDown.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/Day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/Day.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/Editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/Editor.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/Empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/Empty.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/GithubTrending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/GithubTrending.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/Iframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/Iframe.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/JuejinList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/JuejinList.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/MovieLines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/MovieLines.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/Search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/Search.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/TodoList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/TodoList.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/Verse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/Verse.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/Weather.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/Weather.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/WeiboList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/WeiboList.png -------------------------------------------------------------------------------- /src/assets/imgs/capture-new/ZhihuList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/capture-new/ZhihuList.png -------------------------------------------------------------------------------- /src/assets/imgs/icons/bing-en.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/icons/bing.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/icons/google.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/icons/sougou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/icons/taobao.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/theme/base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/theme/base.png -------------------------------------------------------------------------------- /src/assets/imgs/theme/mobile-pro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/theme/mobile-pro.png -------------------------------------------------------------------------------- /src/assets/imgs/theme/mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/theme/mobile.png -------------------------------------------------------------------------------- /src/assets/imgs/theme/module.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/theme/module.png -------------------------------------------------------------------------------- /src/assets/imgs/theme/movie-lines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/theme/movie-lines.png -------------------------------------------------------------------------------- /src/assets/imgs/theme/multi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/theme/multi.png -------------------------------------------------------------------------------- /src/assets/imgs/theme/simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/theme/simple.png -------------------------------------------------------------------------------- /src/assets/imgs/theme/tabs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/theme/tabs.gif -------------------------------------------------------------------------------- /src/assets/imgs/video-thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leon-kfd/Dashboard/38b221553ad241b65ed59ab44498af5092509357/src/assets/imgs/video-thumb.png -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/clear-day.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/cloudy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/drizzle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/fog.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/hurricane.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/mist.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/not-available.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/overcast-day.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/overcast.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 18 | 25 | 26 | 27 | 34 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/rain.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/sleet.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/snow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/thermometer-colder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/thermometer-warmer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/thunderstorms-rain.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-animation-icon/wind.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/clear-day.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/cloudy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/drizzle.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 21 | 29 | 30 | 31 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/fog.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 20 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/hurricane.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/mist.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 21 | 22 | 23 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/not-available.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/overcast-day.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 27 | 28 | 29 | 30 | 31 | 38 | 39 | 40 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/overcast.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 18 | 19 | 20 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/rain.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 21 | 29 | 30 | 31 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/snow.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 26 | 27 | 28 | 36 | 43 | 44 | 45 | 53 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/thermometer-colder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 21 | 22 | 30 | 31 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/thermometer-warmer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 21 | 22 | 30 | 31 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/thunderstorms-rain.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 21 | 29 | 30 | 31 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/assets/imgs/weather-static-icon/wind.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/variables.scss: -------------------------------------------------------------------------------- 1 | $color-primary: rgb(46, 90, 219); 2 | $color-success: rgb(100, 201, 53); 3 | $color-warning: rgb(233, 174, 49); 4 | $color-danger: rgb(216, 94, 94); 5 | $color-dark: #363636; 6 | $color-grey1: #43434b; 7 | $color-grey2: #56565c; 8 | $color-grey3: #898992; 9 | $color-grey4: #aaa2b3; 10 | $color-grey5: #d4d4e0; 11 | $color-white: #f9f9fa; 12 | 13 | @mixin flex-center { 14 | display: flex; 15 | justify-content: center; 16 | align-items: center; 17 | } 18 | 19 | @mixin flex-center-x { 20 | display: flex; 21 | justify-content: center; 22 | } 23 | 24 | @mixin flex-center-y { 25 | display: flex; 26 | align-items: center; 27 | } -------------------------------------------------------------------------------- /src/components/Axuiliary/CleanCache.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 41 | 61 | -------------------------------------------------------------------------------- /src/components/Axuiliary/FAQ.md: -------------------------------------------------------------------------------- 1 | ### 如何切换背景图? 2 | 3 | 点击右下角菜单进入全局设置,可选择设置背景图,当前支持`本地图片`、`网络图片`、`随机图片`等方式。当选择随机图片源后可以设置**自动刷新**,并且在主页面右键菜单会加入刷新壁纸菜单。随机图片源还允许你点击喜欢然后添加到**个人收藏库**,当收藏足够多壁纸后可以以自己的个人收藏库作为**随机图片源**。 4 | 5 | ### 如何更改组件大小与位置? 6 | 7 | 点击右下角菜单锁定按钮解除锁定进入`编辑模式`,**右键相应组件打开菜单**(移动端下可使用长按操作唤起菜单)选择基础配置可更改组件大小。当前组件有 2 种定位模式,按**栅格布局**与 **Fix 固定布局**,2 种模式都支持直接拖拽更改位置和拖拽右下角更改组件大小。 8 | 9 | ### 如何在另一设备同步我的配置? 10 | 11 | 当前提供 2 种数据同步方案,使用**密钥同步**(推荐)与**JSON 文件**同步,打开辅助功能弹出,选择导入导出,在导出模块生成密钥后即可复制密钥到另一设备进行导入。 12 | 13 | ### 组件被另外的组件阻挡? 14 | 15 | 可以在组件基础配置中,找到**ZIndex**配置项,尝试加大该值可将组件层级顺序加大。 16 | 17 | ### 如何实现自定义字体? 18 | 19 | 目前物料组件的字体库是可以选择亦可手动输入的。要实现自定义字体,可在全局设置的**CSS 注入**里面使用`@import`加载线上字体,然后字体库中手动输入字体名称即可。可在[Google Fonts](https://fonts.google.com/)、[Google Fonts 中文版](https://www.googlefonts.cn/)等网站寻找你喜欢的字体。 20 | 21 | ### 如何添加自定义搜索引擎? 22 | 23 | 在`Search`物料中,在组件配置中找到添加按钮即可添加自定义搜索引擎。其中搜索地址中关键词使用`[0]`进行占位。另外,这种方式也可以实现屏蔽某些网站搜索结果,例如**百度搜索屏蔽 CSDN**,其引擎地址为`https://baidu.com/s?wd=[0] -csdn` 24 | 25 | ### 如何导入浏览器书签? 26 | 27 | 在`Bookmark`物料中,可以添加自己收藏的网站,同时允许从 Chrome 或 Edge 浏览器导出的 HTML 书签文件进行导入。可以在`chrome://bookmarks/`右上角选择导出书签文件。书签的解析是使用**纯前端解析**,你不需担心隐私问题。另外书签文件记录的 ICON 是 16 x 16,若过于模糊可以编辑重新获取。 28 | 29 | ### 什么是预览模式? 30 | 31 | 用户数据默认是记录在`LocalStorage`中进行持久性存储,预览模式允许在当前地址后拼接`?preview={previewKey}`进入预览模式。预览模式下的数据存储在`SessionStroge`中,不会对原站的数据进行改写。 32 | 33 | `previewKey`可以是从页面导出生成的exportKey,也可以是一个远程JSON地址。该功能能快速将你自定义的站点分享给好友。以下是使用示例: 34 | + [https://howdz.xyz?preview=7ZMSA](https://howdz.xyz?preview=7ZMSA) 35 | + [https://howdz.xyz?preview=https://raw.gitmirror.com/leon-kfd/Dashboard/main/src/components/Global/DefaultThemeData/Simple.json](https://howdz.xyz?preview=https://raw.gitmirror.com/leon-kfd/Dashboard/main/src/components/Global/DefaultThemeData/Simple.json) 36 | 37 | ### 为什么 IFrame 组件无法正常加载? 38 | 39 | 因为当前系统部署在**HTTPS**站点下,新版 Chrome 等浏览器已不支持 https 与 http 协议混用,所以 Iframe 组件只支持 https 的外部网站。同时目标网站需要允许跨域加载,允许Iframe嵌入。 40 | 41 | ### 如何重新选择预设主题? 42 | 43 | **预设主题**只会第一次进入弹出,若想体验其他预设主题,你可以在辅助功能找到**清除数据**,清空数据后可以重新选择预设主题。清空数据会清空所有配置,请谨慎操作。 44 | 45 | ### 浏览器插件起始页突然空白? 46 | 47 | 出现这个情况一般是因为浏览器插件有了新版本,并且浏览器后台已自动更新完毕,这时**重启浏览器**即可。 48 | -------------------------------------------------------------------------------- /src/components/Axuiliary/FAQ.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 57 | 122 | -------------------------------------------------------------------------------- /src/components/FormControl/FontSelector.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 95 | 122 | -------------------------------------------------------------------------------- /src/components/FormControl/StandardColorPicker.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 23 | 34 | -------------------------------------------------------------------------------- /src/components/FormControl/WarnLock.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 30 | 32 | -------------------------------------------------------------------------------- /src/components/Global/BackgroundEffect.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 25 | 26 | 35 | -------------------------------------------------------------------------------- /src/components/Global/BackgroundEffect/FireFlies.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/Global/BackgroundEffect/Focus.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 67 | 68 | 74 | -------------------------------------------------------------------------------- /src/components/Global/BackgroundEffect/RainDrop.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 107 | 108 | 115 | -------------------------------------------------------------------------------- /src/components/Global/BackgroundEffect/Star.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | 21 | 28 | -------------------------------------------------------------------------------- /src/components/Global/IframeOpener.vue: -------------------------------------------------------------------------------- 1 |