├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── 01_bug_report.md │ ├── 02_feature_request.md │ └── 03_development_problem.md └── workflows │ └── release.yml ├── .gitignore ├── .prettierrc ├── LICENSE.md ├── README.md ├── assets ├── preview1.png ├── preview2.jpg ├── preview3.jpg ├── record1.gif ├── record2.gif ├── ubuntu.jpg └── win10.jpg ├── configs ├── vite.main.ts ├── vite.preload.ts └── vite.renderer.ts ├── electron-builder.json ├── package.json ├── paths.json ├── pnpm-lock.yaml ├── scripts ├── build.mjs └── watch.mjs ├── src ├── common │ ├── config.ts │ ├── index.ts │ └── paths.ts ├── interfaces │ └── index.ts ├── main │ ├── index.ts │ ├── initConfig.ts │ ├── tray.ts │ └── window.ts ├── preload │ └── index.ts └── renderer │ ├── index.html │ ├── public │ └── scripts │ │ ├── howler.min.js │ │ ├── live2d.min.js │ │ ├── live2dcubismcore.min.js │ │ ├── live2dv3.min.js │ │ └── pixi.min.js │ └── src │ ├── App.tsx │ ├── global.d.ts │ ├── main.tsx │ ├── models │ ├── config.ts │ ├── index.ts │ ├── models.json │ └── win.ts │ ├── pages │ ├── Model │ │ ├── Current.tsx │ │ ├── Legacy.tsx │ │ ├── Tips.tsx │ │ ├── Toolbar.tsx │ │ ├── index.tsx │ │ └── tips │ │ │ ├── en.json │ │ │ └── zh.json │ └── Setting │ │ └── index.tsx │ ├── store.ts │ ├── styles │ ├── GlobalStyles.ts │ └── Variables.ts │ ├── utils │ ├── ga.ts │ └── index.ts │ └── vite-env.d.ts ├── static ├── icons │ ├── mac │ │ └── icon.icns │ ├── png │ │ ├── 1024x1024.png │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 24x24.png │ │ ├── 256x256.png │ │ ├── 32x32.png │ │ ├── 48x48.png │ │ ├── 512x512.png │ │ └── 64x64.png │ ├── strip-tray.png │ ├── strip-tray@2x.png │ ├── strip-tray@3x.png │ ├── tray.png │ ├── tray@2x.png │ ├── tray@3x.png │ └── win │ │ └── icon.ico └── logo.png └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/01_bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: 报告一个问题 4 | --- 5 | 6 | **请先确认是否为[最新版本](https://github.com/zenghongtu/PPet/releases/latest)** 7 | 8 | #### 问题描述 9 | 10 | **_平台信息(win/mac/linux):_** 11 | 12 | #### 截图(可选) 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/02_feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: 提交一个新特性/功能 4 | --- 5 | 6 | ### 描述 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/03_development_problem.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Development Problem 3 | about: 开发方面的问题 4 | --- 5 | 6 | ### 描述 7 | 8 | ### 截图(可选) 9 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build/release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | release: 10 | runs-on: ${{ matrix.os }} 11 | 12 | strategy: 13 | matrix: 14 | os: [macos-latest, ubuntu-latest, windows-2019] 15 | 16 | steps: 17 | - name: Check out Git repository 18 | uses: actions/checkout@v2 19 | 20 | - name: Install Node.js, NPM and Yarn 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: 14 24 | 25 | - name: Build/release Electron app 26 | uses: samuelmeuli/action-electron-builder@v1 27 | with: 28 | # GitHub token, automatically provided to the action 29 | # (No need to define this secret in the repo settings) 30 | github_token: ${{ secrets.github_token }} 31 | 32 | # macOS code signing certificate 33 | mac_certs: ${{ secrets.mac_certs }} 34 | mac_certs_password: ${{ secrets.mac_certs_password }} 35 | 36 | # If the commit is tagged with a version (e.g. "v1.0.0"), 37 | # release the app after building 38 | release: ${{ startsWith(github.ref, 'refs/tags/v') }} 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | .env.test 60 | 61 | # parcel-bundler cache (https://parceljs.org/) 62 | .cache 63 | 64 | # next.js build output 65 | .next 66 | 67 | # nuxt.js build output 68 | .nuxt 69 | 70 | # vuepress build output 71 | .vuepress/dist 72 | 73 | # Serverless directories 74 | .serverless/ 75 | 76 | # FuseBox cache 77 | .fusebox/ 78 | 79 | # DynamoDB Local files 80 | .dynamodb/ 81 | 82 | # ---- 83 | dist 84 | **/.tmp 85 | release 86 | .DS_Store 87 | dist-ssr 88 | *.local 89 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "printWidth": 80, 6 | "tabWidth": 2, 7 | "endOfLine": "auto" 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-present zenghongtu (zenghongtu@gmail.com) 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # PPet 4 | 5 | > 给你的桌面多一点趣味~😁 6 | 7 | **支持 Live2d v3 啦~** 8 | 9 | [![Current Release](https://img.shields.io/github/release/zenghongtu/PPet.svg?style=flat-square)](https://github.com/zenghongtu/PPet/releases) 10 | ![License](https://img.shields.io/github/license/zenghongtu/PPet.svg?style=flat-square) 11 | 12 | ## 预览 13 | 14 | 15 | 16 | 小工具: 17 | 18 | 19 | 20 | 21 | 设置(可以使用在线/本地 Model): 22 | 23 | 24 | 25 | 托盘: 26 | 27 | 28 | 29 | 30 | ## 功能 31 | 32 | - [x] 支持 Live2d v2/v3 33 | - [x] 导入本地/在线 Model 34 | - [x] 调整渲染大小 35 | - [x] @置顶 36 | - [x] 忽略点击 37 | - [x] 开机启动 38 | - [x] 拖动位置 39 | - [ ] ... 40 | 41 | ## 安装 42 | 43 | [这里](https://github.com/zenghongtu/PPet/releases/latest)去下载最新版本,Mac使用.dmg, Window使用.exe,Linux使用.deb。 44 | 45 | (可以使用 [GitHub Proxy 代理加速](https://ghproxy.com/) 下载) 46 | 47 | ## 使用 48 | 49 | ### 导入模型 50 | 51 | 两种方式: 52 | 53 | - 在设置 -> 模型列表中添加 `json` 文件(本地模型以`file://`开头)。 54 | 55 | - 本地模型将文件夹拖入窗口中,导入成功会更新到模型列表中 56 | 57 | 58 | 59 | 60 | 61 | 62 | ## 开发 63 | 64 | ``` 65 | $ pnpm i 66 | $ pnpm start 67 | ``` 68 | 69 | ## 欢迎参与贡献 70 | 71 | 发现了错误? 向我发起一个 PR 吧! 参考 [Commit message 和 Change log 编写指南 - 阮一峰的网络日志](http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html) 提交 commit 即可。 72 | ## 其他 73 | 74 | Model资源: [zenghongtu/live2d-model-assets](https://github.com/zenghongtu/live2d-model-assets) 75 | 76 | [前往旧版](https://github.com/zenghongtu/PPet/blob/master/README.md) 77 | 78 | -------------------------------------------------------------------------------- /assets/preview1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenghongtu/PPet/2185c3f90600e3f809d55dbeedc7b82a341d06ca/assets/preview1.png -------------------------------------------------------------------------------- /assets/preview2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenghongtu/PPet/2185c3f90600e3f809d55dbeedc7b82a341d06ca/assets/preview2.jpg -------------------------------------------------------------------------------- /assets/preview3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenghongtu/PPet/2185c3f90600e3f809d55dbeedc7b82a341d06ca/assets/preview3.jpg -------------------------------------------------------------------------------- /assets/record1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenghongtu/PPet/2185c3f90600e3f809d55dbeedc7b82a341d06ca/assets/record1.gif -------------------------------------------------------------------------------- /assets/record2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenghongtu/PPet/2185c3f90600e3f809d55dbeedc7b82a341d06ca/assets/record2.gif -------------------------------------------------------------------------------- /assets/ubuntu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenghongtu/PPet/2185c3f90600e3f809d55dbeedc7b82a341d06ca/assets/ubuntu.jpg -------------------------------------------------------------------------------- /assets/win10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenghongtu/PPet/2185c3f90600e3f809d55dbeedc7b82a341d06ca/assets/win10.jpg -------------------------------------------------------------------------------- /configs/vite.main.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | import { builtinModules } from 'module' 3 | import { defineConfig } from 'vite' 4 | 5 | export default defineConfig({ 6 | mode: process.env.NODE_ENV, 7 | root: join(__dirname, '../src/main'), 8 | build: { 9 | outDir: '../../dist/main', 10 | lib: { 11 | entry: 'index.ts', 12 | formats: ['cjs'], 13 | }, 14 | minify: process.env.NODE_ENV === 'production', 15 | emptyOutDir: true, 16 | rollupOptions: { 17 | external: [...builtinModules, 'electron'], 18 | output: { 19 | entryFileNames: '[name].cjs', 20 | }, 21 | }, 22 | }, 23 | resolve: { 24 | alias: { 25 | '@src': join(__dirname, '../src'), 26 | }, 27 | }, 28 | }) 29 | -------------------------------------------------------------------------------- /configs/vite.preload.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | import { builtinModules } from 'module' 3 | import { defineConfig } from 'vite' 4 | 5 | export default defineConfig({ 6 | mode: process.env.NODE_ENV, 7 | root: join(__dirname, '../src/preload'), 8 | build: { 9 | outDir: '../../dist/preload', 10 | lib: { 11 | entry: 'index.ts', 12 | formats: ['cjs'], 13 | }, 14 | minify: process.env.NODE_ENV === 'production', 15 | emptyOutDir: true, 16 | rollupOptions: { 17 | external: [...builtinModules, 'electron'], 18 | output: { 19 | entryFileNames: '[name].cjs', 20 | }, 21 | }, 22 | }, 23 | resolve: { 24 | alias: { 25 | '@src': join(__dirname, '../src'), 26 | }, 27 | }, 28 | }) 29 | -------------------------------------------------------------------------------- /configs/vite.renderer.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | import { defineConfig } from 'vite' 3 | import react from '@vitejs/plugin-react' 4 | import vitePluginImp from 'vite-plugin-imp' 5 | import pkg from '../package.json' 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | mode: process.env.NODE_ENV, 10 | root: join(__dirname, '../src/renderer'), 11 | plugins: [ 12 | react(), 13 | vitePluginImp({ 14 | libList: [ 15 | { 16 | libName: 'antd', 17 | style(name) { 18 | // use less 19 | return `antd/es/${name}/style/index.js` 20 | }, 21 | }, 22 | ], 23 | }), 24 | ], 25 | css: { 26 | preprocessorOptions: { 27 | less: { 28 | javascriptEnabled: true, 29 | }, 30 | }, 31 | }, 32 | base: './', 33 | build: { 34 | emptyOutDir: true, 35 | outDir: '../../dist/renderer', 36 | }, 37 | resolve: { 38 | alias: { 39 | '@src': join(__dirname, '../src'), 40 | }, 41 | }, 42 | server: { 43 | host: pkg.env.HOST, 44 | port: pkg.env.PORT, 45 | }, 46 | }) 47 | -------------------------------------------------------------------------------- /electron-builder.json: -------------------------------------------------------------------------------- 1 | { 2 | "productName": "PPet3", 3 | "artifactName": "PPet3-${version}.${ext}", 4 | "appId": "com.zenghongtu.ppet-v3", 5 | "asar": true, 6 | "files": ["dist", "package.json", "static/**/*"], 7 | "mac": { 8 | "artifactName": "${productName}_${version}.${ext}", 9 | "target": ["dmg", "zip"], 10 | "category": "public.app-category.development", 11 | "icon": "static/icons/mac/icon.icns", 12 | "darkModeSupport": true 13 | }, 14 | "win": { 15 | "icon": "static/icons/win/icon.ico", 16 | "artifactName": "${productName}-v${version}-${os}-${arch}.${ext}", 17 | "target": [ 18 | { 19 | "target": "nsis", 20 | "arch": ["x64", "ia32"] 21 | }, 22 | { 23 | "target": "zip", 24 | "arch": ["x64", "ia32"] 25 | } 26 | ] 27 | }, 28 | "nsis": { 29 | "createDesktopShortcut": "always", 30 | "createStartMenuShortcut": true, 31 | "shortcutName": "PPet", 32 | "oneClick": false, 33 | "perMachine": true, 34 | "allowToChangeInstallationDirectory": true, 35 | "artifactName": "${productName}_${version}.${ext}", 36 | "unicode": true 37 | }, 38 | "extraFiles": [], 39 | "dmg": { 40 | "contents": [ 41 | { 42 | "x": 130, 43 | "y": 220 44 | }, 45 | { 46 | "x": 410, 47 | "y": 220, 48 | "type": "link", 49 | "path": "/Applications" 50 | } 51 | ] 52 | }, 53 | "linux": { 54 | "icon": "static/icons/png", 55 | "target": [ 56 | { 57 | "target": "AppImage", 58 | "arch": ["x64"] 59 | }, 60 | { 61 | "target": "deb", 62 | "arch": ["x64"] 63 | } 64 | ], 65 | "category": "Development" 66 | }, 67 | "publish": [ 68 | { 69 | "provider": "github", 70 | "owner": "zenghongtu", 71 | "repo": "PPet" 72 | } 73 | ], 74 | "directories": { 75 | "output": "./release" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PPet3", 3 | "version": "3.3.0", 4 | "description": "给你的桌面添加一点趣味~", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "dist/main/index.cjs", 8 | "repository": "https://github.com/zenghongtu/PPet.git", 9 | "homepage": "https://github.com/zenghongtu/PPet", 10 | "author": "zenghongtu ", 11 | "scripts": { 12 | "start": "node scripts/watch.mjs", 13 | "build": "node scripts/build.mjs", 14 | "dist": "yarn build && electron-builder", 15 | "dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null", 16 | "dist:all": "yarn run dist -wml", 17 | "dist:win": "yarn run dist --win --ia32", 18 | "dist:mac": "yarn run dist -m", 19 | "dist:linux": "yarn run dist -l", 20 | "release": "yarn dist -wml -p always", 21 | "patch": "yarn version --patch", 22 | "minor": "yarn version --minor", 23 | "major": "yarn version --major" 24 | }, 25 | "engines": { 26 | "node": ">=14.17.0" 27 | }, 28 | "devDependencies": { 29 | "@ant-design/icons": "^4.7.0", 30 | "@electron/remote": "^2.0.1", 31 | "@rematch/core": "^2.2.0", 32 | "@rematch/persist": "^2.1.2", 33 | "@types/react": "^17.0.33", 34 | "@types/react-dom": "^17.0.10", 35 | "@types/redux-state-sync": "^3.1.2", 36 | "@types/styled-components": "^5.1.19", 37 | "@vitejs/plugin-react": "^1.1.4", 38 | "antd": "^4.16.13", 39 | "chalk": "^5.0.0", 40 | "electron": "16.0.6", 41 | "electron-builder": "^22.14.5", 42 | "electron-store": "^8.0.1", 43 | "electron-window-state": "^5.0.3", 44 | "font-awesome": "^4.7.0", 45 | "globby": "^11.0.4", 46 | "less": "^4.1.2", 47 | "prettier": "^2.5.1", 48 | "react": "^17.0.2", 49 | "react-dom": "^17.0.2", 50 | "react-ga": "^3.3.0", 51 | "react-redux": "^7.2.6", 52 | "react-router-dom": "^6.2.1", 53 | "redux": "^4.1.2", 54 | "redux-persist": "^6.0.0", 55 | "redux-state-sync": "^3.1.2", 56 | "styled-components": "^5.3.3", 57 | "typescript": "^4.4.4", 58 | "vite": "^2.7.10", 59 | "vite-plugin-imp": "^2.1.3" 60 | }, 61 | "env": { 62 | "//": "Used in build scripts", 63 | "HOST": "127.0.0.1", 64 | "PORT": 3344 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /paths.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "paths": { 6 | "@src/*": ["src/*"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scripts/build.mjs: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'production' 2 | 3 | import { build as viteBuild } from 'vite' 4 | import chalk from 'chalk' 5 | 6 | const TAG = chalk.bgBlue('[build.mjs]') 7 | 8 | const viteConfigs = { 9 | main: 'configs/vite.main.ts', 10 | preload: 'configs/vite.preload.ts', 11 | renderer: 'configs/vite.renderer.ts', 12 | } 13 | 14 | async function buildElectron() { 15 | for (const [name, configPath] of Object.entries(viteConfigs)) { 16 | console.group(TAG, name) 17 | await viteBuild({ 18 | configFile: configPath, 19 | mode: process.env.NODE_ENV, 20 | }) 21 | console.groupEnd() 22 | console.log() // for beautiful log. 23 | } 24 | } 25 | 26 | // bootstrap 27 | await buildElectron() 28 | -------------------------------------------------------------------------------- /scripts/watch.mjs: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'development' 2 | 3 | import { readFileSync } from 'fs' 4 | import { resolve, join } from 'path' 5 | import electron from 'electron' 6 | import { spawn } from 'child_process' 7 | import { createServer, build as viteBuild } from 'vite' 8 | 9 | const pkg = JSON.parse( 10 | readFileSync(join(process.cwd(), 'package.json'), 'utf8') 11 | ) 12 | 13 | /** 14 | * @param {{ name: string; configFile: string; writeBundle: import('rollup').OutputPlugin['writeBundle'] }} param0 15 | * @returns {import('rollup').RollupWatcher} 16 | */ 17 | function getWatcher({ name, configFile, writeBundle }) { 18 | return viteBuild({ 19 | // Options here precedence over configFile 20 | mode: process.env.NODE_ENV, 21 | build: { 22 | watch: {}, 23 | }, 24 | configFile, 25 | plugins: [{ name, writeBundle }], 26 | }) 27 | } 28 | 29 | /** 30 | * @returns {Promise} 31 | */ 32 | async function watchMain() { 33 | /** 34 | * @type {import('child_process').ChildProcessWithoutNullStreams | null} 35 | */ 36 | let electronProcess = null 37 | 38 | /** 39 | * @type {import('rollup').RollupWatcher} 40 | */ 41 | const watcher = await getWatcher({ 42 | name: 'electron-main-watcher', 43 | configFile: 'configs/vite.main.ts', 44 | writeBundle() { 45 | electronProcess && electronProcess.kill() 46 | electronProcess = spawn(electron, ['.'], { 47 | stdio: 'inherit', 48 | env: Object.assign(process.env, pkg.env), 49 | }) 50 | }, 51 | }) 52 | 53 | return watcher 54 | } 55 | 56 | /** 57 | * @param {import('vite').ViteDevServer} viteDevServer 58 | * @returns {Promise} 59 | */ 60 | async function watchPreload(viteDevServer) { 61 | return getWatcher({ 62 | name: 'electron-preload-watcher', 63 | configFile: 'configs/vite.preload.ts', 64 | writeBundle() { 65 | viteDevServer.ws.send({ 66 | type: 'full-reload', 67 | }) 68 | }, 69 | }) 70 | } 71 | 72 | // bootstrap 73 | const viteDevServer = await createServer({ 74 | configFile: 'configs/vite.renderer.ts', 75 | }) 76 | 77 | await viteDevServer.listen() 78 | await watchPreload(viteDevServer) 79 | await watchMain() 80 | -------------------------------------------------------------------------------- /src/common/config.ts: -------------------------------------------------------------------------------- 1 | import { app } from 'electron' 2 | import Store from 'electron-store' 3 | 4 | const store = new Store<{ 5 | alwaysOnTop: boolean 6 | ignoreMouseEvents: boolean 7 | showTool: boolean 8 | language: 'zh' | 'en' 9 | }>({ 10 | name: 'ppet-config', 11 | defaults: { 12 | alwaysOnTop: true, 13 | ignoreMouseEvents: false, 14 | showTool: true, 15 | language: app.getLocale().includes('zh') ? 'zh' : 'en', 16 | }, 17 | }) 18 | 19 | export default store 20 | -------------------------------------------------------------------------------- /src/common/index.ts: -------------------------------------------------------------------------------- 1 | import config from './config' 2 | export * from './paths' 3 | 4 | export { config } 5 | -------------------------------------------------------------------------------- /src/common/paths.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { app } from 'electron' 3 | 4 | /** 当前应用程序所在目录 */ 5 | export const APP_PATH: string = app.getAppPath() 6 | 7 | /** 当前用户的应用数据文件夹 */ 8 | export const APP_DATA_PATH: string = app.getPath('appData') 9 | 10 | /** 储存你应用程序设置文件的文件夹 */ 11 | export const USER_DATA_PATH: string = app.getPath('userData') 12 | 13 | /** 应用程序的日志文件夹 */ 14 | export const LOGS_PATH: string = 15 | process.platform === 'darwin' 16 | ? path.resolve(app.getPath('logs'), `../${app.name}`) 17 | : path.resolve(USER_DATA_PATH, 'logs') 18 | 19 | /** 资源文件夹 */ 20 | export const ASSETS_PATH: string = 21 | process.env.NODE_ENV === 'development' 22 | ? 'assets' 23 | : path.join(APP_PATH, 'assets') 24 | -------------------------------------------------------------------------------- /src/interfaces/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenghongtu/PPet/2185c3f90600e3f809d55dbeedc7b82a341d06ca/src/interfaces/index.ts -------------------------------------------------------------------------------- /src/main/index.ts: -------------------------------------------------------------------------------- 1 | import os from 'os' 2 | import { join } from 'path' 3 | import { app, BrowserWindow, protocol, session } from 'electron' 4 | import windowStateKeeper from 'electron-window-state' 5 | import remoteMain from '@electron/remote/main' 6 | 7 | import './initConfig' 8 | import initTray from './tray' 9 | import { createWindow, winPagePathMap } from './window' 10 | 11 | remoteMain.initialize() 12 | 13 | const isWin7 = os.release().startsWith('6.1') 14 | if (isWin7) app.disableHardwareAcceleration() 15 | 16 | if (app.isPackaged) { 17 | if (!app.requestSingleInstanceLock()) { 18 | app.quit() 19 | process.exit(0) 20 | } 21 | if (process.platform === 'darwin') { 22 | app.dock.hide() 23 | } 24 | } 25 | 26 | app.commandLine.appendSwitch('disable-renderer-backgrounding') 27 | app.commandLine.appendSwitch('disable-background-timer-throttling') 28 | app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required') 29 | 30 | let mainWindowState: windowStateKeeper.State 31 | 32 | app 33 | .whenReady() 34 | .then(() => { 35 | protocol.registerFileProtocol('file', (request, callback) => { 36 | const url = request.url.replace('file://', '') 37 | const decodedUrl = decodeURI(url) 38 | try { 39 | return callback(decodedUrl) 40 | } catch (error) { 41 | console.error('Could not get file path:', error) 42 | return callback('404') 43 | } 44 | }) 45 | }) 46 | .then(() => { 47 | mainWindowState = windowStateKeeper({ 48 | defaultHeight: 600, 49 | defaultWidth: 350, 50 | }) 51 | }) 52 | .then(async () => { 53 | const options = { 54 | title: 'PPet', 55 | alwaysOnTop: true, 56 | autoHideMenuBar: true, 57 | hasShadow: false, 58 | transparent: true, 59 | frame: false, 60 | x: mainWindowState.x, 61 | y: mainWindowState.y, 62 | width: mainWindowState.width, 63 | height: mainWindowState.height, 64 | skipTaskbar: true, 65 | minimizable: false, 66 | maximizable: false, 67 | resizable: false, 68 | // titleBarStyle: 'hidden', 69 | webPreferences: { 70 | preload: join(__dirname, '../preload/index.cjs'), 71 | webSecurity: false, 72 | backgroundThrottling: false, 73 | }, 74 | } 75 | 76 | const win = await createWindow(options) 77 | if (win) { 78 | mainWindowState.manage(win) 79 | 80 | initTray(win) 81 | } 82 | }) 83 | 84 | app.on('window-all-closed', () => { 85 | winPagePathMap.clear() 86 | app.quit() 87 | }) 88 | 89 | app.on('browser-window-created', (ev, win) => { 90 | remoteMain.enable(win.webContents) 91 | }) 92 | 93 | app.on('second-instance', () => { 94 | const win = BrowserWindow.getAllWindows()[0] 95 | if (win) { 96 | // Someone tried to run a second instance, we should focus our window. 97 | if (win.isMinimized()) win.restore() 98 | win.focus() 99 | } 100 | }) 101 | 102 | app.on('activate', () => { 103 | const allWindows = BrowserWindow.getAllWindows() 104 | if (allWindows.length) { 105 | allWindows[0].focus() 106 | } 107 | }) 108 | -------------------------------------------------------------------------------- /src/main/initConfig.ts: -------------------------------------------------------------------------------- 1 | import { config } from '@src/common' 2 | ;(global as any).config = config.store 3 | 4 | config.onDidAnyChange((newValue) => { 5 | ;(global as any).config = newValue || {} 6 | }) 7 | -------------------------------------------------------------------------------- /src/main/tray.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | import electron, { 3 | Tray, 4 | nativeImage, 5 | app, 6 | MenuItemConstructorOptions, 7 | MenuItem, 8 | shell, 9 | BrowserWindow, 10 | Menu, 11 | } from 'electron' 12 | import { config } from '@src/common' 13 | 14 | import { createWindow } from './window' 15 | import stripTrayIcon from '../../static/icons/strip-tray.png' 16 | import trayIcon from '../../static/icons/tray.png' 17 | 18 | const currentIcon = process.platform === 'darwin' ? stripTrayIcon : trayIcon 19 | 20 | const langs = { 21 | zh: { 22 | alwaysOnTop: '@置顶', 23 | ignoreMouseEvents: '忽略点击', 24 | openAtLogin: '开机启动', 25 | plugins: '插件中心', 26 | tools: '小工具', 27 | language: '语言', 28 | zoomIn: '放大', 29 | zoomOut: '缩小', 30 | zoomReset: '原始大小', 31 | canvasSettings: '画布设置', 32 | clearSettings: '清除设置', 33 | importModel: '导入模型', 34 | importOnlineModel: '导入在线模型', 35 | removeModel: '移除模型', 36 | reRender: '重新渲染', 37 | debug: '调试', 38 | feedback: '反馈', 39 | about: '关于', 40 | quit: '退出', 41 | next: '下一个模型', 42 | prev: '上一个模型', 43 | model: { 44 | title: '请选择模型配置文件', 45 | buttonLabel: '导入模型', 46 | filtersName: '模型配置文件', 47 | }, 48 | errorBox: { 49 | title: '导入模型失败', 50 | title1: '移除模型失败', 51 | getContent: (text: string) => 52 | `无效的model配置文件,该文件为'.json'结尾,会包含${text}等字段`, 53 | }, 54 | settings: '配置', 55 | }, 56 | en: { 57 | alwaysOnTop: 'Always On Top', 58 | ignoreMouseEvents: 'Ignore Mouse Events', 59 | openAtLogin: 'Open At Login', 60 | plugins: 'Plugins', 61 | tools: 'Tools', 62 | language: 'Language', 63 | zoomIn: 'Zoom In', 64 | zoomOut: 'Zoom Out', 65 | zoomReset: 'Zoom Reset', 66 | canvasSettings: 'Canvas Settings', 67 | clearSettings: 'Clear Canvas Settings', 68 | importModel: 'Import Model', 69 | importOnlineModel: 'Import Online Model', 70 | removeModel: 'Remove Model', 71 | reRender: 'ReRender', 72 | debug: 'Debug', 73 | feedback: 'Feedback', 74 | about: 'About', 75 | quit: 'Quit', 76 | next: 'Next Model', 77 | prev: 'Prev Model', 78 | model: { 79 | title: 'Please select model configuration file', 80 | buttonLabel: 'Import model', 81 | filtersName: 'model configuration file', 82 | }, 83 | errorBox: { 84 | title: 'Import model failed', 85 | title1: 'Remove model failed', 86 | getContent: (text: string) => 87 | `Invalid model configuration file. The file ends with '.json' and should contain fields such as ${text}`, 88 | }, 89 | settings: 'Settings', 90 | }, 91 | } 92 | 93 | type langType = 'zh' | 'en' 94 | 95 | let tray: electron.Tray 96 | 97 | const initTray = (mainWindow: BrowserWindow) => { 98 | if (!tray) { 99 | tray = new Tray(nativeImage.createFromDataURL(currentIcon)) 100 | } 101 | 102 | const handleClickLangRadio = (lang: langType) => { 103 | config.set('language', lang) 104 | initTray(mainWindow) 105 | mainWindow.webContents.executeJavaScript(`window.setLanguage('${lang}')`) 106 | } 107 | 108 | const alwaysOnTop = config.get('alwaysOnTop') 109 | const ignoreMouseEvents = config.get('ignoreMouseEvents') 110 | const showTool = config.get('showTool') 111 | const lang = config.get('language') 112 | 113 | const cl = langs[lang] 114 | mainWindow.setAlwaysOnTop(alwaysOnTop) 115 | mainWindow.setIgnoreMouseEvents(ignoreMouseEvents, { forward: true }) 116 | 117 | const template: Array = [ 118 | { 119 | label: cl.alwaysOnTop, 120 | type: 'checkbox', 121 | checked: alwaysOnTop, 122 | click: (item) => { 123 | const { checked } = item 124 | mainWindow.setAlwaysOnTop(checked) 125 | config.set('alwaysOnTop', checked) 126 | }, 127 | }, 128 | { 129 | label: cl.tools, 130 | type: 'checkbox', 131 | accelerator: 'CmdOrCtrl+t', 132 | checked: showTool, 133 | click: (item) => { 134 | const { checked } = item 135 | mainWindow.webContents.executeJavaScript( 136 | `window.setSwitchTool(${checked})`, 137 | ) 138 | config.set('showTool', checked) 139 | }, 140 | }, 141 | { 142 | label: cl.ignoreMouseEvents, 143 | accelerator: 'CmdOrCtrl+i', 144 | type: 'checkbox', 145 | checked: ignoreMouseEvents, 146 | click: (item) => { 147 | const { checked } = item 148 | mainWindow.setIgnoreMouseEvents(checked, { forward: true }) 149 | config.set('ignoreMouseEvents', checked) 150 | }, 151 | }, 152 | { 153 | label: cl.settings, 154 | accelerator: 'CmdOrCtrl+,', 155 | click: async () => { 156 | await createWindow( 157 | { 158 | title: cl.settings, 159 | width: 800, 160 | height: 600, 161 | webPreferences: { 162 | preload: join(__dirname, '../preload/index.cjs'), 163 | webSecurity: false, 164 | backgroundThrottling: false, 165 | }, 166 | }, 167 | '/setting', 168 | ) 169 | }, 170 | }, 171 | { 172 | type: 'separator', 173 | }, 174 | { 175 | label: cl.prev, 176 | accelerator: 'CmdOrCtrl+p', 177 | click: () => { 178 | mainWindow.webContents.executeJavaScript('window.prevModel()') 179 | }, 180 | }, 181 | { 182 | label: cl.next, 183 | accelerator: 'CmdOrCtrl+n', 184 | click: () => { 185 | mainWindow.webContents.executeJavaScript('window.nextModel()') 186 | }, 187 | }, 188 | { 189 | type: 'separator', 190 | }, 191 | { 192 | label: cl.language, 193 | type: 'submenu', 194 | submenu: [ 195 | { 196 | label: '简体中文', 197 | type: 'radio', 198 | checked: lang === 'zh', 199 | click: handleClickLangRadio.bind(null, 'zh'), 200 | }, 201 | { 202 | label: 'English', 203 | type: 'radio', 204 | checked: lang === 'en', 205 | click: handleClickLangRadio.bind(null, 'en'), 206 | }, 207 | ], 208 | }, 209 | { 210 | type: 'separator', 211 | }, 212 | { 213 | label: cl.reRender, 214 | accelerator: 'CmdOrCtrl+r', 215 | click: () => { 216 | mainWindow.reload() 217 | }, 218 | }, 219 | { 220 | label: cl.debug, 221 | accelerator: 'CmdOrCtrl+d', 222 | click: () => { 223 | mainWindow.webContents.openDevTools({ mode: 'undocked' }) 224 | }, 225 | }, 226 | { 227 | type: 'separator', 228 | }, 229 | { 230 | label: cl.openAtLogin, 231 | type: 'checkbox', 232 | checked: app.getLoginItemSettings().openAtLogin, 233 | click: (item) => { 234 | const { checked } = item 235 | app.setLoginItemSettings({ openAtLogin: checked }) 236 | }, 237 | }, 238 | { 239 | type: 'separator', 240 | }, 241 | { 242 | label: cl.feedback, 243 | click: () => { 244 | shell.openExternal('https://github.com/zenghongtu/PPet/issues') 245 | }, 246 | }, 247 | { 248 | label: cl.about, 249 | role: 'about', 250 | }, 251 | { 252 | type: 'separator', 253 | }, 254 | { 255 | label: cl.quit, 256 | click: (item) => { 257 | app.quit() 258 | }, 259 | }, 260 | ] 261 | 262 | const menu = Menu.buildFromTemplate(template) 263 | 264 | tray.setContextMenu(menu) 265 | } 266 | 267 | export default initTray 268 | -------------------------------------------------------------------------------- /src/main/window.ts: -------------------------------------------------------------------------------- 1 | import { app, BrowserWindow } from 'electron' 2 | import { join } from 'path' 3 | 4 | export const winPagePathMap: Map = new Map() 5 | 6 | export async function createWindow( 7 | options: Electron.BrowserWindowConstructorOptions, 8 | pagePath: string = '', 9 | ) { 10 | const lastWin = winPagePathMap.get(pagePath) 11 | if (lastWin && !lastWin.isDestroyed()) { 12 | lastWin.focus() 13 | return 14 | } 15 | 16 | const win = new BrowserWindow({ ...options, show: false }) 17 | 18 | if (app.isPackaged) { 19 | const file = join(__dirname, '../renderer/index.html') 20 | 21 | win.loadFile(file, { hash: pagePath }) 22 | } else { 23 | const pkg = await import('../../package.json') 24 | const url = `http://${pkg.env.HOST || '127.0.0.1'}:${ 25 | pkg.env.PORT 26 | }#${pagePath}` 27 | 28 | win.loadURL(url, { 29 | // userAgent: 30 | // 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/603.1.23 (KHTML, like Gecko) Version/10.0 Mobile/14E5239e Safari/602.1', 31 | }) 32 | win.webContents.openDevTools() 33 | } 34 | 35 | win.once('ready-to-show', () => { 36 | win.show() 37 | win.focus() 38 | }) 39 | 40 | winPagePathMap.set(pagePath, win) 41 | 42 | return win 43 | } 44 | -------------------------------------------------------------------------------- /src/preload/index.ts: -------------------------------------------------------------------------------- 1 | import { contextBridge, ipcRenderer } from 'electron' 2 | import electron from '@electron/remote' 3 | import fs from 'fs/promises' 4 | import path from 'path' 5 | import globby from 'globby' 6 | 7 | const getModels = async (file: File) => { 8 | const filePath = file.path 9 | if (filePath.endsWith('model.json') || filePath.endsWith('.model3.json')) { 10 | return [filePath] 11 | } 12 | 13 | console.log('getModels: ', filePath) 14 | return fs.stat(filePath).then(async (f) => { 15 | if (f.isDirectory()) { 16 | const result = await globby(['**/*model.json', '**.model3.json'], { 17 | cwd: filePath, 18 | }) 19 | return result.map((f) => path.join(filePath, f)) 20 | } 21 | return [] 22 | }) 23 | } 24 | 25 | const setWinResizable = (resizable: boolean) => { 26 | electron.getCurrentWindow().setResizable(resizable) 27 | } 28 | 29 | const isWinResizable = () => { 30 | return electron.getCurrentWindow().isResizable() 31 | } 32 | 33 | const getConfig = () => electron.getGlobal('config') 34 | 35 | contextBridge.exposeInMainWorld('bridge', { 36 | __dirname, 37 | __filename, 38 | getModels, 39 | setWinResizable, 40 | isWinResizable, 41 | getConfig, 42 | }) 43 | -------------------------------------------------------------------------------- /src/renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/renderer/public/scripts/howler.min.js: -------------------------------------------------------------------------------- 1 | /*! howler.js v2.1.3 | (c) 2013-2019, James Simpson of GoldFire Studios | MIT License | howlerjs.com */ 2 | !function(){"use strict";var e=function(){this.init()};e.prototype={init:function(){var e=this||n;return e._counter=1e3,e._html5AudioPool=[],e.html5PoolSize=10,e._codecs={},e._howls=[],e._muted=!1,e._volume=1,e._canPlayEvent="canplaythrough",e._navigator="undefined"!=typeof window&&window.navigator?window.navigator:null,e.masterGain=null,e.noAudio=!1,e.usingWebAudio=!0,e.autoSuspend=!0,e.ctx=null,e.autoUnlock=!0,e._setup(),e},volume:function(e){var o=this||n;if(e=parseFloat(e),o.ctx||_(),void 0!==e&&e>=0&&e<=1){if(o._volume=e,o._muted)return o;o.usingWebAudio&&o.masterGain.gain.setValueAtTime(e,n.ctx.currentTime);for(var t=0;t=0;o--)e._howls[o].unload();return e.usingWebAudio&&e.ctx&&void 0!==e.ctx.close&&(e.ctx.close(),e.ctx=null,_()),e},codecs:function(e){return(this||n)._codecs[e.replace(/^x-/,"")]},_setup:function(){var e=this||n;if(e.state=e.ctx?e.ctx.state||"suspended":"suspended",e._autoSuspend(),!e.usingWebAudio)if("undefined"!=typeof Audio)try{var o=new Audio;void 0===o.oncanplaythrough&&(e._canPlayEvent="canplay")}catch(n){e.noAudio=!0}else e.noAudio=!0;try{var o=new Audio;o.muted&&(e.noAudio=!0)}catch(e){}return e.noAudio||e._setupCodecs(),e},_setupCodecs:function(){var e=this||n,o=null;try{o="undefined"!=typeof Audio?new Audio:null}catch(n){return e}if(!o||"function"!=typeof o.canPlayType)return e;var t=o.canPlayType("audio/mpeg;").replace(/^no$/,""),r=e._navigator&&e._navigator.userAgent.match(/OPR\/([0-6].)/g),a=r&&parseInt(r[0].split("/")[1],10)<33;return e._codecs={mp3:!(a||!t&&!o.canPlayType("audio/mp3;").replace(/^no$/,"")),mpeg:!!t,opus:!!o.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/,""),ogg:!!o.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),oga:!!o.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),wav:!!o.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),aac:!!o.canPlayType("audio/aac;").replace(/^no$/,""),caf:!!o.canPlayType("audio/x-caf;").replace(/^no$/,""),m4a:!!(o.canPlayType("audio/x-m4a;")||o.canPlayType("audio/m4a;")||o.canPlayType("audio/aac;")).replace(/^no$/,""),mp4:!!(o.canPlayType("audio/x-mp4;")||o.canPlayType("audio/mp4;")||o.canPlayType("audio/aac;")).replace(/^no$/,""),weba:!!o.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),webm:!!o.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),dolby:!!o.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/,""),flac:!!(o.canPlayType("audio/x-flac;")||o.canPlayType("audio/flac;")).replace(/^no$/,"")},e},_unlockAudio:function(){var e=this||n;if(!e._audioUnlocked&&e.ctx){e._audioUnlocked=!1,e.autoUnlock=!1,e._mobileUnloaded||44100===e.ctx.sampleRate||(e._mobileUnloaded=!0,e.unload()),e._scratchBuffer=e.ctx.createBuffer(1,1,22050);var o=function(n){for(var t=0;t0?i._seek:t._sprite[e][0]/1e3),s=Math.max(0,(t._sprite[e][0]+t._sprite[e][1])/1e3-_),l=1e3*s/Math.abs(i._rate),c=t._sprite[e][0]/1e3,f=(t._sprite[e][0]+t._sprite[e][1])/1e3;i._sprite=e,i._ended=!1;var p=function(){i._paused=!1,i._seek=_,i._start=c,i._stop=f,i._loop=!(!i._loop&&!t._sprite[e][2])};if(_>=f)return void t._ended(i);var m=i._node;if(t._webAudio){var v=function(){t._playLock=!1,p(),t._refreshBuffer(i);var e=i._muted||t._muted?0:i._volume;m.gain.setValueAtTime(e,n.ctx.currentTime),i._playStart=n.ctx.currentTime,void 0===m.bufferSource.start?i._loop?m.bufferSource.noteGrainOn(0,_,86400):m.bufferSource.noteGrainOn(0,_,s):i._loop?m.bufferSource.start(0,_,86400):m.bufferSource.start(0,_,s),l!==1/0&&(t._endTimers[i._id]=setTimeout(t._ended.bind(t,i),l)),o||setTimeout(function(){t._emit("play",i._id),t._loadQueue()},0)};"running"===n.state?v():(t._playLock=!0,t.once("resume",v),t._clearTimer(i._id))}else{var h=function(){m.currentTime=_,m.muted=i._muted||t._muted||n._muted||m.muted,m.volume=i._volume*n.volume(),m.playbackRate=i._rate;try{var r=m.play();if(r&&"undefined"!=typeof Promise&&(r instanceof Promise||"function"==typeof r.then)?(t._playLock=!0,p(),r.then(function(){t._playLock=!1,m._unlocked=!0,o||(t._emit("play",i._id),t._loadQueue())}).catch(function(){t._playLock=!1,t._emit("playerror",i._id,"Playback was unable to start. This is most commonly an issue on mobile devices and Chrome where playback was not within a user interaction."),i._ended=!0,i._paused=!0})):o||(t._playLock=!1,p(),t._emit("play",i._id),t._loadQueue()),m.playbackRate=i._rate,m.paused)return void t._emit("playerror",i._id,"Playback was unable to start. This is most commonly an issue on mobile devices and Chrome where playback was not within a user interaction.");"__default"!==e||i._loop?t._endTimers[i._id]=setTimeout(t._ended.bind(t,i),l):(t._endTimers[i._id]=function(){t._ended(i),m.removeEventListener("ended",t._endTimers[i._id],!1)},m.addEventListener("ended",t._endTimers[i._id],!1))}catch(e){t._emit("playerror",i._id,e)}};"data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"===m.src&&(m.src=t._src,m.load());var y=window&&window.ejecta||!m.readyState&&n._navigator.isCocoonJS;if(m.readyState>=3||y)h();else{t._playLock=!0;var g=function(){h(),m.removeEventListener(n._canPlayEvent,g,!1)};m.addEventListener(n._canPlayEvent,g,!1),t._clearTimer(i._id)}}return i._id},pause:function(e){var n=this;if("loaded"!==n._state||n._playLock)return n._queue.push({event:"pause",action:function(){n.pause(e)}}),n;for(var o=n._getSoundIds(e),t=0;t=0?o=parseInt(r[0],10):e=parseFloat(r[0])}else r.length>=2&&(e=parseFloat(r[0]),o=parseInt(r[1],10));var a;if(!(void 0!==e&&e>=0&&e<=1))return a=o?t._soundById(o):t._sounds[0],a?a._volume:0;if("loaded"!==t._state||t._playLock)return t._queue.push({event:"volume",action:function(){t.volume.apply(t,r)}}),t;void 0===o&&(t._volume=e),o=t._getSoundIds(o);for(var u=0;u0?t/_:t),l=Date.now();e._fadeTo=o,e._interval=setInterval(function(){var r=(Date.now()-l)/t;l=Date.now(),i+=d*r,i=Math.max(0,i),i=Math.min(1,i),i=Math.round(100*i)/100,u._webAudio?e._volume=i:u.volume(i,e._id,!0),a&&(u._volume=i),(on&&i>=o)&&(clearInterval(e._interval),e._interval=null,e._fadeTo=null,u.volume(o,e._id),u._emit("fade",e._id))},s)},_stopFade:function(e){var o=this,t=o._soundById(e);return t&&t._interval&&(o._webAudio&&t._node.gain.cancelScheduledValues(n.ctx.currentTime),clearInterval(t._interval),t._interval=null,o.volume(t._fadeTo,e),t._fadeTo=null,o._emit("fade",e)),o},loop:function(){var e,n,o,t=this,r=arguments;if(0===r.length)return t._loop;if(1===r.length){if("boolean"!=typeof r[0])return!!(o=t._soundById(parseInt(r[0],10)))&&o._loop;e=r[0],t._loop=e}else 2===r.length&&(e=r[0],n=parseInt(r[1],10));for(var a=t._getSoundIds(n),u=0;u=0?o=parseInt(r[0],10):e=parseFloat(r[0])}else 2===r.length&&(e=parseFloat(r[0]),o=parseInt(r[1],10));var i;if("number"!=typeof e)return i=t._soundById(o),i?i._rate:t._rate;if("loaded"!==t._state||t._playLock)return t._queue.push({event:"rate",action:function(){t.rate.apply(t,r)}}),t;void 0===o&&(t._rate=e),o=t._getSoundIds(o);for(var d=0;d=0?o=parseInt(r[0],10):t._sounds.length&&(o=t._sounds[0]._id,e=parseFloat(r[0]))}else 2===r.length&&(e=parseFloat(r[0]),o=parseInt(r[1],10));if(void 0===o)return t;if("loaded"!==t._state||t._playLock)return t._queue.push({event:"seek",action:function(){t.seek.apply(t,r)}}),t;var i=t._soundById(o);if(i){if(!("number"==typeof e&&e>=0)){if(t._webAudio){var d=t.playing(o)?n.ctx.currentTime-i._playStart:0,_=i._rateSeek?i._rateSeek-i._seek:0;return i._seek+(_+d*Math.abs(i._rate))}return i._node.currentTime}var s=t.playing(o);s&&t.pause(o,!0),i._seek=e,i._ended=!1,t._clearTimer(o),t._webAudio||!i._node||isNaN(i._node.duration)||(i._node.currentTime=e);var l=function(){t._emit("seek",o),s&&t.play(o,!0)};if(s&&!t._webAudio){var c=function(){t._playLock?setTimeout(c,0):l()};setTimeout(c,0)}else l()}return t},playing:function(e){var n=this;if("number"==typeof e){var o=n._soundById(e);return!!o&&!o._paused}for(var t=0;t=0&&n._howls.splice(a,1);var u=!0;for(t=0;t=0){u=!1;break}return r&&u&&delete r[e._src],n.noAudio=!1,e._state="unloaded",e._sounds=[],e=null,null},on:function(e,n,o,t){var r=this,a=r["_on"+e];return"function"==typeof n&&a.push(t?{id:o,fn:n,once:t}:{id:o,fn:n}),r},off:function(e,n,o){var t=this,r=t["_on"+e],a=0;if("number"==typeof n&&(o=n,n=null),n||o)for(a=0;a=0;a--)r[a].id&&r[a].id!==n&&"load"!==e||(setTimeout(function(e){e.call(this,n,o)}.bind(t,r[a].fn),0),r[a].once&&t.off(e,r[a].fn,r[a].id));return t._loadQueue(e),t},_loadQueue:function(e){var n=this;if(n._queue.length>0){var o=n._queue[0];o.event===e&&(n._queue.shift(),n._loadQueue()),e||o.action()}return n},_ended:function(e){var o=this,t=e._sprite;if(!o._webAudio&&e._node&&!e._node.paused&&!e._node.ended&&e._node.currentTime=0;t--){if(o<=n)return;e._sounds[t]._ended&&(e._webAudio&&e._sounds[t]._node&&e._sounds[t]._node.disconnect(0),e._sounds.splice(t,1),o--)}}},_getSoundIds:function(e){var n=this;if(void 0===e){for(var o=[],t=0;t=0;if(n._scratchBuffer&&e.bufferSource&&(e.bufferSource.onended=null,e.bufferSource.disconnect(0),t))try{e.bufferSource.buffer=n._scratchBuffer}catch(e){}return e.bufferSource=null,o},_clearSound:function(e){/MSIE |Trident\//.test(n._navigator&&n._navigator.userAgent)||(e.src="data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA")}};var t=function(e){this._parent=e,this.init()};t.prototype={init:function(){var e=this,o=e._parent;return e._muted=o._muted,e._loop=o._loop,e._volume=o._volume,e._rate=o._rate,e._seek=0,e._paused=!0,e._ended=!0,e._sprite="__default",e._id=++n._counter,o._sounds.push(e),e.create(),e},create:function(){var e=this,o=e._parent,t=n._muted||e._muted||e._parent._muted?0:e._volume;return o._webAudio?(e._node=void 0===n.ctx.createGain?n.ctx.createGainNode():n.ctx.createGain(),e._node.gain.setValueAtTime(t,n.ctx.currentTime),e._node.paused=!0,e._node.connect(n.masterGain)):n.noAudio||(e._node=n._obtainHtml5Audio(),e._errorFn=e._errorListener.bind(e),e._node.addEventListener("error",e._errorFn,!1),e._loadFn=e._loadListener.bind(e),e._node.addEventListener(n._canPlayEvent,e._loadFn,!1),e._node.src=o._src,e._node.preload="auto",e._node.volume=t*n.volume(),e._node.load()),e},reset:function(){var e=this,o=e._parent;return e._muted=o._muted,e._loop=o._loop,e._volume=o._volume,e._rate=o._rate,e._seek=0,e._rateSeek=0,e._paused=!0,e._ended=!0,e._sprite="__default",e._id=++n._counter,e},_errorListener:function(){var e=this;e._parent._emit("loaderror",e._id,e._node.error?e._node.error.code:0),e._node.removeEventListener("error",e._errorFn,!1)},_loadListener:function(){var e=this,o=e._parent;o._duration=Math.ceil(10*e._node.duration)/10,0===Object.keys(o._sprite).length&&(o._sprite={__default:[0,1e3*o._duration]}),"loaded"!==o._state&&(o._state="loaded",o._emit("load"),o._loadQueue()),e._node.removeEventListener(n._canPlayEvent,e._loadFn,!1)}};var r={},a=function(e){var n=e._src;if(r[n])return e._duration=r[n].duration,void d(e);if(/^data:[^;]+;base64,/.test(n)){for(var o=atob(n.split(",")[1]),t=new Uint8Array(o.length),a=0;a0?(r[o._src]=e,d(o,e)):t()};"undefined"!=typeof Promise&&1===n.ctx.decodeAudioData.length?n.ctx.decodeAudioData(e).then(a).catch(t):n.ctx.decodeAudioData(e,a,t)},d=function(e,n){n&&!e._duration&&(e._duration=n.duration),0===Object.keys(e._sprite).length&&(e._sprite={__default:[0,1e3*e._duration]}),"loaded"!==e._state&&(e._state="loaded",e._emit("load"),e._loadQueue())},_=function(){if(n.usingWebAudio){try{"undefined"!=typeof AudioContext?n.ctx=new AudioContext:"undefined"!=typeof webkitAudioContext?n.ctx=new webkitAudioContext:n.usingWebAudio=!1}catch(e){n.usingWebAudio=!1}n.ctx||(n.usingWebAudio=!1);var e=/iP(hone|od|ad)/.test(n._navigator&&n._navigator.platform),o=n._navigator&&n._navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/),t=o?parseInt(o[1],10):null;if(e&&t&&t<9){var r=/safari/.test(n._navigator&&n._navigator.userAgent.toLowerCase());(n._navigator&&n._navigator.standalone&&!r||n._navigator&&!n._navigator.standalone&&!r)&&(n.usingWebAudio=!1)}n.usingWebAudio&&(n.masterGain=void 0===n.ctx.createGain?n.ctx.createGainNode():n.ctx.createGain(),n.masterGain.gain.setValueAtTime(n._muted?0:n._volume,n.ctx.currentTime),n.masterGain.connect(n.ctx.destination)),n._setup()}};"function"==typeof define&&define.amd&&define([],function(){return{Howler:n,Howl:o}}),"undefined"!=typeof exports&&(exports.Howler=n,exports.Howl=o),"undefined"!=typeof window?(window.HowlerGlobal=e,window.Howler=n,window.Howl=o,window.Sound=t):"undefined"!=typeof global&&(global.HowlerGlobal=e,global.Howler=n,global.Howl=o,global.Sound=t)}(); 3 | /*! Spatial Plugin */ 4 | !function(){"use strict";HowlerGlobal.prototype._pos=[0,0,0],HowlerGlobal.prototype._orientation=[0,0,-1,0,1,0],HowlerGlobal.prototype.stereo=function(e){var n=this;if(!n.ctx||!n.ctx.listener)return n;for(var t=n._howls.length-1;t>=0;t--)n._howls[t].stereo(e);return n},HowlerGlobal.prototype.pos=function(e,n,t){var r=this;return r.ctx&&r.ctx.listener?(n="number"!=typeof n?r._pos[1]:n,t="number"!=typeof t?r._pos[2]:t,"number"!=typeof e?r._pos:(r._pos=[e,n,t],void 0!==r.ctx.listener.positionX?(r.ctx.listener.positionX.setTargetAtTime(r._pos[0],Howler.ctx.currentTime,.1),r.ctx.listener.positionY.setTargetAtTime(r._pos[1],Howler.ctx.currentTime,.1),r.ctx.listener.positionZ.setTargetAtTime(r._pos[2],Howler.ctx.currentTime,.1)):r.ctx.listener.setPosition(r._pos[0],r._pos[1],r._pos[2]),r)):r},HowlerGlobal.prototype.orientation=function(e,n,t,r,o,i){var a=this;if(!a.ctx||!a.ctx.listener)return a;var s=a._orientation;return n="number"!=typeof n?s[1]:n,t="number"!=typeof t?s[2]:t,r="number"!=typeof r?s[3]:r,o="number"!=typeof o?s[4]:o,i="number"!=typeof i?s[5]:i,"number"!=typeof e?s:(a._orientation=[e,n,t,r,o,i],void 0!==a.ctx.listener.forwardX?(a.ctx.listener.forwardX.setTargetAtTime(e,Howler.ctx.currentTime,.1),a.ctx.listener.forwardY.setTargetAtTime(n,Howler.ctx.currentTime,.1),a.ctx.listener.forwardZ.setTargetAtTime(t,Howler.ctx.currentTime,.1),a.ctx.listener.upX.setTargetAtTime(r,Howler.ctx.currentTime,.1),a.ctx.listener.upY.setTargetAtTime(o,Howler.ctx.currentTime,.1),a.ctx.listener.upZ.setTargetAtTime(i,Howler.ctx.currentTime,.1)):a.ctx.listener.setOrientation(e,n,t,r,o,i),a)},Howl.prototype.init=function(e){return function(n){var t=this;return t._orientation=n.orientation||[1,0,0],t._stereo=n.stereo||null,t._pos=n.pos||null,t._pannerAttr={coneInnerAngle:void 0!==n.coneInnerAngle?n.coneInnerAngle:360,coneOuterAngle:void 0!==n.coneOuterAngle?n.coneOuterAngle:360,coneOuterGain:void 0!==n.coneOuterGain?n.coneOuterGain:0,distanceModel:void 0!==n.distanceModel?n.distanceModel:"inverse",maxDistance:void 0!==n.maxDistance?n.maxDistance:1e4,panningModel:void 0!==n.panningModel?n.panningModel:"HRTF",refDistance:void 0!==n.refDistance?n.refDistance:1,rolloffFactor:void 0!==n.rolloffFactor?n.rolloffFactor:1},t._onstereo=n.onstereo?[{fn:n.onstereo}]:[],t._onpos=n.onpos?[{fn:n.onpos}]:[],t._onorientation=n.onorientation?[{fn:n.onorientation}]:[],e.call(this,n)}}(Howl.prototype.init),Howl.prototype.stereo=function(n,t){var r=this;if(!r._webAudio)return r;if("loaded"!==r._state)return r._queue.push({event:"stereo",action:function(){r.stereo(n,t)}}),r;var o=void 0===Howler.ctx.createStereoPanner?"spatial":"stereo";if(void 0===t){if("number"!=typeof n)return r._stereo;r._stereo=n,r._pos=[n,0,0]}for(var i=r._getSoundIds(t),a=0;a=0&&this._callbackFunctions.splice(t,1)}},e.prototype.clearAnimationCallback=function(){this._callbackFunctions=[]},e.prototype.callAnimationCallback=function(e){this._callbackFunctions.length>0&&this._callbackFunctions.forEach(function(t){t(e)})},e.prototype.evaluate=function(e,t,i,r,s,a){if(void 0===a&&(a=null),!(t<=.01)){if(this.loop)for(;e>this.duration;)e-=this.duration;if(this.parameterTracks.forEach(function(a){var n=r.parameters.ids.indexOf(a.targetId);if(n>=0){var o=a.evaluate(e);!0!==s[0][n]&&(r.parameters.values[n]=r.parameters.defaultValues[n],s[0][n]=!0),r.parameters.values[n]=i(r.parameters.values[n],o,a.evaluate(0),t)}}),this.partOpacityTracks.forEach(function(a){var n=r.parts.ids.indexOf(a.targetId);if(n>=0){var o=a.evaluate(e);!0!==s[1][n]&&(r.parts.opacities[n]=1,s[1][n]=!0),r.parts.opacities[n]=i(r.parts.opacities[n],o,a.evaluate(0),t)}}),this.modelTracks.forEach(function(n){if(null!=a){var o=a.getGroupById(n.targetId);if(null!=o&&"Parameter"===o.target)for(var l=0,u=o.ids;l=0){var d=n.evaluate(e);!0!==s[0][c]&&(r.parameters.values[c]=r.parameters.defaultValues[c],s[0][c]=!0),r.parameters.values[c]=i(r.parameters.values[c],d,n.evaluate(0),t)}}}}),null!=this._callbackFunctions)for(var n=0,o=this.userDataBodys;ni){if(e>i&&e0&&ei&&e0?(this._goalAnimation=e,this._goalTime=0,this._fadeTime=0,this._fadeDuration=t):(this._animation=e,this.currentTime=0,this._play=!0)},e.prototype.resume=function(){this._play=!0},e.prototype.pause=function(){this._play=!1},e.prototype.stop=function(){this._play=!1,this.currentTime=0},e.prototype._update=function(e){this._play&&(this._time+=e,this._goalTime+=e,this._fadeTime+=e,(null==this._animation||!this._animation.loop&&this._time>this._animation.duration)&&(this.stop(),this._animation=null))},e.prototype._evaluate=function(e,t){if(null!=this._animation){var i=this.weight<1?this.weight:1,r=null!=this._goalAnimation?i*this.weightCrossfade(this._fadeTime,this._fadeDuration):i;this._animation.evaluate(this._time,r,this.blend,e,t,this.groups),null!=this._goalAnimation&&(r=1-i*this.weightCrossfade(this._fadeTime,this._fadeDuration),this._goalAnimation.evaluate(this._goalTime,r,this.blend,e,t,this.groups),this._fadeTime>this._fadeDuration&&(this._animation=this._goalAnimation,this._time=this._goalTime,this._goalAnimation=null))}},e}();e.AnimationLayer=h;var c=function(){function e(e,t,i){this._target=e,this.timeScale=t,this._layers=i}return Object.defineProperty(e.prototype,"target",{get:function(){return this._target},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"isPlaying",{get:function(){var e=!1;return this._layers.forEach(function(t){t.isPlaying&&(e=!0)}),e},enumerable:!0,configurable:!0}),e.prototype.addLayer=function(e,t,i){void 0===t&&(t=u.OVERRIDE),void 0===i&&(i=1);var r=new h;r.blend=t,r.weightCrossfade=o.LINEAR,r.weight=i,r.groups=this.groups,this._layers.set(e,r)},e.prototype.getLayer=function(e){return this._layers.has(e)?this._layers.get(e):null},e.prototype.removeLayer=function(e){return this._layers.has(e)?this._layers.delete(e):null},e.prototype.clearLayers=function(){this._layers.clear()},e.prototype.updateAndEvaluate=function(e){var t=this;(e*=this.timeScale>0?this.timeScale:0)>.001&&this._layers.forEach(function(t){t._update(e)});var i=new Array(this._target.parameters.count).fill(!1),r=new Array(this._target.parts.count).fill(!1),s=new Array(i,r);this._layers.forEach(function(e){e._evaluate(t._target,s)})},e._create=function(t,i,r){var s=new e(t,i,r);return s.isValid?s:null},Object.defineProperty(e.prototype,"isValid",{get:function(){return null!=this._target},enumerable:!0,configurable:!0}),e}();e.Animator=c;var d=function(){function e(){this._timeScale=1,this._layerNames=[],this._layerBlenders=[],this._layerCrossfadeWeighters=[],this._layerWeights=[]}return e.prototype.setTarget=function(e){return this._target=e,this},e.prototype.setTimeScale=function(e){return this._timeScale=e,this},e.prototype.addLayer=function(e,t,i){return void 0===t&&(t=u.OVERRIDE),void 0===i&&(i=1),this._layerNames.push(e),this._layerBlenders.push(t),this._layerCrossfadeWeighters.push(o.LINEAR),this._layerWeights.push(i),this},e.prototype.build=function(){for(var e=new Map,t=0;ti?i:e},e.directionToDegrees=function(t,i){var r=e.directionToRadians(t,i),s=e.radiansToDegrees(r);return i.x-t.x>0?-s:s},e.radiansToDegrees=function(e){return 180*e/Math.PI},e.radiansToDirection=function(e){return new p(Math.sin(e),Math.cos(e))},e.degreesToRadians=function(e){return e/180*Math.PI},e.directionToRadians=function(e,t){var i=p.dot(e,t),r=e.length*t.length;if(0===r)return 0;var s=i/r;return Math.abs(s)<=1?Math.acos(s):0},e.gravity=new p(0,-1),e.wind=new p(0,0),e.maximumWeight=100,e.airResistance=5,e.movementThreshold=.001,e.correctAngles=!0,e}();e.Physics=f;var m=function(){return function(e,t,i,r,s){this.initialPosition=e,this.mobility=t,this.delay=i,this.acceleration=r,this.radius=s,this.position=e,this.lastPosition=this.position,this.lastGravity=new p(0,-1),this.force=new p(0,0),this.velocity=new p(0,0)}}();e.PhysicsParticle=m;var y=function(){function e(e,t,i){this.x=e,this.y=t,this.angle=i}return e.prototype.add=function(t){return new e(this.x+t.x,this.y+t.y,this.angle+t.angle)},e}();e.PhysicsFactorTuple=y;var g=function(){return function(e,t,i){this.minimum=e,this.maximum=t,this.def=i}}();e.PhysicsNormalizationTuple=g;var _=function(){return function(e,t){this.position=e,this.angle=t}}();e.PhysicsNormalizationOptions=_;var v=function(){function e(e,t,i,r){this.targetId=e,this.weight=t,this.factor=i,this.invert=r}return Object.defineProperty(e.prototype,"normalizedWeight",{get:function(){return f.clampScalar(this.weight/f.maximumWeight,0,1)},enumerable:!0,configurable:!0}),e.prototype.evaluateFactor=function(e,t,i,r,s){console.assert(i>t);var a=this.getMiddleValue(t,i),n=e-a;switch(Math.sign(n)){case 1:{const e=i-a;if(0===e)n=s.angle.def;else{const t=s.angle.maximum-s.angle.def;0===t?n=s.angle.maximum:(n*=Math.abs(t/e),n+=s.angle.def)}}break;case-1:{const e=a-t;if(0===e)n=s.angle.def;else{const t=s.angle.def-s.angle.minimum;0===t?n=s.angle.minimum:(n*=Math.abs(t/e),n+=s.angle.def)}}break;case 0:n=s.angle.def}var o=this.weight/f.maximumWeight;return n*=this.invert?1:-1,new y(n*this.factor.x*o,n*this.factor.y*o,n*this.factor.angle*o)},e.prototype.getRangeValue=function(e,t){var i=Math.max(e,t),r=Math.min(e,t);return Math.abs(i-r)},e.prototype.getMiddleValue=function(e,t){return Math.min(e,t)+this.getRangeValue(e,t)/2},e}();e.PhysicsInput=v;var b=function(){function e(e,t,i,r,s,a){this.targetId=e,this.particleIndex=t,this.weight=i,this.factor=s,this.invert=a,this.factor.angle*=r}return Object.defineProperty(e.prototype,"normalizedWeight",{get:function(){return f.clampScalar(this.weight/f.maximumWeight,0,1)},enumerable:!0,configurable:!0}),e.prototype.evaluateValue=function(e,t){var i=e.x*this.factor.x+e.y*this.factor.y;if(this.factor.angle>0){var r=f.gravity;f.correctAngles&&this.particleIndex>1&&(r=t[this.particleIndex-2].position.substract(t[this.particleIndex-1].position));var s=f.directionToRadians(r,e);i+=(e.x-r.x>0?-s:s)*this.factor.angle}return i*=this.invert?-1:1},e}();e.PhysicsOutput=b;var w=function(){function e(e,t,i,r){this.input=e,this.output=t,this.particles=i,this.normalization=r}return e.prototype._update=function(e,t){var i=this,r=t.parameters,s=new y(0,0,0);this.input.forEach(function(e){var t=r.ids.indexOf(e.targetId);-1!==t&&(s=s.add(e.evaluateFactor(r.values[t],r.minimumValues[t],r.maximumValues[t],r.defaultValues[t],i.normalization)))});var a=f.degreesToRadians(-s.angle),n=new p(s.x,s.y).rotateByRadians(a);s.x=n.x,s.y=n.y;var o=a,l=f.radiansToDirection(o).normalize();this.particles.forEach(function(t,r){if(0!==r){t.force=l.multiplyByScalar(t.acceleration).add(f.wind),t.lastPosition=t.position;var a=t.delay*e*30,n=t.position.substract(i.particles[r-1].position),o=p.distance(p.zero,n),u=f.directionToDegrees(t.lastGravity,l),h=f.degreesToRadians(u)/f.airResistance;n=n.rotateByRadians(h).normalize(),t.position=i.particles[r-1].position.add(n.multiplyByScalar(o));var c=t.velocity.multiplyByScalar(a),d=t.force.multiplyByScalar(a).multiplyByScalar(a);t.position=t.position.add(c).add(d);var m=t.position.substract(i.particles[r-1].position).normalize();t.position=i.particles[r-1].position.add(m.multiplyByScalar(t.radius)),Math.abs(t.position.x)=t.particles.length)){var r=i.ids.indexOf(e.targetId);if(-1!==r){var s=t.particles[e.particleIndex-1].position.substract(t.particles[e.particleIndex].position),a=f.clampScalar(e.evaluateValue(s,t.particles),i.minimumValues[r],i.maximumValues[r]),n=i.values[r]*(1-e.normalizedWeight)+a*e.normalizedWeight;i.values[r]=f.clampScalar(n,i.minimumValues[r],i.maximumValues[r])}}})},e}();e.PhysicsSubRig=w;var P=function(){function e(e,t,i){var r=this;this.timeScale=1,this.timeScale=t,this._target=e,e&&(this._subRigs=[],i.PhysicsSettings.forEach(function(e){var t=[];e.Input.forEach(function(e){var i=new y(1,0,0);"Y"===e.Type?(i.x=0,i.y=1):"Angle"===e.Type&&(i.x=0,i.angle=1),t.push(new v(e.Source.Id,e.Weight,i,e.Reflect))});var i=[];e.Output.forEach(function(e){var t=new y(1,0,0);"Y"===e.Type?(t.x=0,t.y=1):"Angle"===e.Type&&(t.x=0,t.angle=1),i.push(new b(e.Destination.Id,e.VertexIndex,e.Weight,e.Scale,t,e.Reflect))});var s=[];e.Vertices.forEach(function(e){var t=new p(e.Position.X,e.Position.Y);s.push(new m(t,e.Mobility,e.Delay,e.Acceleration,e.Radius))});var a=e.Normalization,n=new g(a.Position.Minimum,a.Position.Maximum,a.Position.Default),o=new g(a.Angle.Minimum,a.Angle.Maximum,a.Angle.Default),l=new _(n,o);r._subRigs.push(new w(t,i,s,l))}))}return e.prototype.updateAndEvaluate=function(e){var t=this;(e*=this.timeScale>0?this.timeScale:0)>.01&&this._subRigs.forEach(function(i){i._update(e,t._target)}),this._subRigs.forEach(function(e){e._evaluate(t._target)})},e._fromPhysics3Json=function(t,i,r){var s=new e(t,i,r);return s._isValid?s:null},Object.defineProperty(e.prototype,"_isValid",{get:function(){return null!=this._target},enumerable:!0,configurable:!0}),e}();e.PhysicsRig=P;var M=function(){function e(){this._timeScale=1}return e.prototype.setTarget=function(e){return this._target=e,this},e.prototype.setTimeScale=function(e){return this._timeScale=e,this},e.prototype.setPhysics3Json=function(e){return this._physics3Json=e,this},e.prototype.build=function(){return P._fromPhysics3Json(this._target,this._timeScale,this._physics3Json)},e}();e.PhysicsRigBuilder=M;var x=function(){function e(e,t){var i=this;this._target=e,e&&(this._version=t.Version,this._userDataCount=t.Meta.UserDataCount,this._totalUserDataSize=t.Meta.TotalUserDataSize,null!=t.UserData&&(this._userDataBodys=[],t.UserData.forEach(function(e){i._userDataBodys.push(new B(e.Target,e.Id,e.Value))}),console.assert(this._userDataBodys.length===this._userDataCount)))}return e._fromUserData3Json=function(t,i){var r=new e(t,i);return r._isValid?r:null},Object.defineProperty(e.prototype,"_isValid",{get:function(){return null!=this._target},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"userDataCount",{get:function(){return null==this._userDataBodys?0:this._userDataCount},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"totalUserDataSize",{get:function(){return null==this._userDataBodys?0:this._totalUserDataSize},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"userDataBodys",{get:function(){return null==this._userDataBodys?null:this._userDataBodys},enumerable:!0,configurable:!0}),e.prototype.isExistUserDataById=function(e){if(null==this._userDataBodys)return!1;for(var t=0,i=this._userDataBodys;t0){var p=new PIXI.Filter;p.blendMode=PIXI.BLEND_MODES.ADD,u._meshes[h].filters=[p]}else u._meshes[h].blendMode=PIXI.BLEND_MODES.ADD;else if(Live2DCubismCore.Utils.hasBlendMultiplicativeBit(u._coreModel.drawables.constantFlags[h]))if(u._coreModel.drawables.maskCounts[h]>0){var f=new PIXI.Filter;f.blendMode=PIXI.BLEND_MODES.MULTIPLY,u._meshes[h].filters=[f]}else u._meshes[h].blendMode=PIXI.BLEND_MODES.MULTIPLY;u.addChild(u._meshes[h])}return u._maskSpriteContainer=new s(t,u),u}return r(t,e),Object.defineProperty(t.prototype,"parameters",{get:function(){return this._coreModel.parameters},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"parts",{get:function(){return this._coreModel.parts},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"drawables",{get:function(){return this._coreModel.drawables},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"canvasinfo",{get:function(){return this._coreModel.canvasinfo},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"textures",{get:function(){return this._textures},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"animator",{get:function(){return this._animator},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"userData",{get:function(){return this._userData},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"meshes",{get:function(){return this._meshes},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"masks",{get:function(){return this._maskSpriteContainer},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"groups",{get:function(){return this._groups},enumerable:!0,configurable:!0}),t.prototype.update=function(e){var t=this,i=.016*e;this._animator.updateAndEvaluate(i),this._physicsRig&&this._physicsRig.updateAndEvaluate(i),this._coreModel.update();for(var r=!1,s=0;s0){for(var l=new PIXI.Container,u=0;u{const h=u[t+"_model"].data;if(void 0!==h.FileReferences.Moc&&a.add(t+"_moc",s+h.FileReferences.Moc,{xhrType:PIXI.loaders.Resource.XHR_RESPONSE_TYPE.BUFFER}),void 0!==h.FileReferences.Textures&&h.FileReferences.Textures.forEach(e=>{a.add(t+"_texture"+o,s+e),o++}),void 0!==h.FileReferences.Physics&&a.add(t+"_physics",s+h.FileReferences.Physics,{xhrType:PIXI.loaders.Resource.XHR_RESPONSE_TYPE.JSON}),void 0!==h.FileReferences.Motions)for(const e in h.FileReferences.Motions)h.FileReferences.Motions[e].forEach(e=>{const i=e.File.split("/").pop().split(".").shift();if(l.includes(t+"_"+i)){var r=t+"_"+i+String(Date.now());a.add(r,s+e.File,{xhrType:PIXI.loaders.Resource.XHR_RESPONSE_TYPE.JSON}),l.push(t+"_"+i)}else a.add(t+"_"+i,s+e.File,{xhrType:PIXI.loaders.Resource.XHR_RESPONSE_TYPE.JSON}),l.push(t+"_"+i)});let c=null;void 0!==h.Groups&&(c=e.Groups.fromModel3Json(h)),a.load((s,a)=>{let u=null;if(void 0!==a[t+"_moc"]&&(u=Live2DCubismCore.Moc.fromArrayBuffer(a[t+"_moc"].data)),void 0!==a[t+"_texture0"])for(let e=0;e{const r=i.split(t+"_").pop();h.set(r,e.Animation.fromMotion3Json(a[i].data))});let d=null;const p=Live2DCubismCore.Model.fromMoc(u);if(null==p)return;const f=this.animatorBuilder.setTarget(p).setTimeScale(this.timeScale).build(),m=this.physicsRigBuilder.setTarget(p).setTimeScale(this.timeScale).build();(d=i.Model._create(p,n,f,m,null,c)).motions=h,this.models[t]=d,r.changeCanvas(d)})})}}}class a{constructor({basePath:e,modelName:t,width:i=500,height:r=300,el:a,sizeLimit:n,mobileLimit:o,sounds:l}){if("undefined"!=typeof Live2DCubismCore)if("undefined"!=typeof PIXI)if(a){if(!this.isDom(a)){if(!(a.length>0))return void console.error("live2dv3 failed to load:\n",a,"is not a HTMLElement object");if(!this.isDom(a[0]))return void console.error("live2dv3 failed to load:\n",a[0],"is not a HTMLElement object");a=a[0]}n&&(document.documentElement.clientWidth{this.model&&(this.model.update(e),this.model.masks.update(this.app.renderer))}),window.onresize=(e=>{void 0===e&&(e=null),this.app.view.style.width=i+"px",this.app.view.style.height=r+"px",this.app.renderer.resize(i,r),this.model&&(this.model.position=new PIXI.Point(.5*i,.5*r),this.model.scale=new PIXI.Point(.06*this.model.position.x,.06*this.model.position.x),this.model.masks.resize(this.app.view.width,this.app.view.height))}),this.isClick=!1,this.app.view.addEventListener("mousedown",e=>{this.isClick=!0}),this.app.view.addEventListener("mousemove",e=>{if(this.isClick&&(this.isClick=!1,this.model&&(this.model.inDrag=!0)),this.model){const t=this.model.position.x-e.offsetX,i=this.model.position.y-e.offsetY;this.model.pointerX=-t/this.app.view.height,this.model.pointerY=-i/this.app.view.width}}),this.app.view.addEventListener("mouseup",i=>{if(this.model){if(this.isClick){if(this.isHit("TouchHead",i.offsetX,i.offsetY))this.startAnimation("touch_head","base");else if(this.isHit("TouchSpecial",i.offsetX,i.offsetY))this.startAnimation("touch_special","base");else{const e=["touch_body","main_1","main_2","main_3"],t=e[Math.floor(Math.random()*e.length)];this.startAnimation(t,"base")}if(l&&l.length>0){const i=l[Math.floor(Math.random()*l.length)],r=/^https?:\/\//.test(i)?i:[e,t,i].join("/").replace(/(?" tag.\nLook at https://github.com/HCLonely/Live2dV3');else console.error('live2dv3 failed to load:\nMissing live2dcubismcore.js\nPlease add "https://cdn.jsdelivr.net/gh/HCLonely/Live2dV3/js/live2dcubismcore.min.js" to the "