从webpack迁移到vite

This commit is contained in:
lyswhut
2023-08-07 18:49:14 +08:00
parent 31e16a7ed7
commit 028bf5919d
166 changed files with 3071 additions and 11869 deletions
+15
View File
@@ -0,0 +1,15 @@
/* eslint-env node */
const { base, typescript } = require('../../.eslintrc.base.cjs')
module.exports = {
root: true,
...base,
overrides: [
{
...typescript,
parserOptions: {
project: './tsconfig.json',
},
},
],
}
+3 -3
View File
@@ -1,5 +1,5 @@
import { join } from 'path'
import { homedir } from 'os'
import path from 'node:path'
import os from 'node:os'
const isMac = process.platform == 'darwin'
const isWin = process.platform == 'win32'
@@ -102,7 +102,7 @@ const defaultSetting: LX.AppSetting = {
'list.actionButtonsVisible': false,
'download.enable': false,
'download.savePath': join(homedir(), 'Desktop'),
'download.savePath': path.join(os.homedir(), 'Desktop'),
'download.fileName': '歌名 - 歌手',
'download.maxDownloadNum': 3,
'download.skipExistFile': true,
+1 -1
View File
@@ -6,7 +6,7 @@ const ignoreErrorMessage = [
]
process.on('uncaughtException', err => {
if (ignoreErrorMessage.includes(err.message)) return
if (ignoreErrorMessage.includes(err?.message)) return
console.error('An uncaught error occurred!')
console.error(err)
log.error(err)
+5 -5
View File
@@ -8,7 +8,7 @@
* @param {string} c1 rgb(a) color2
* @returns color
*/
exports.RGB_Linear_Blend=(p,c0,c1)=>{
export const RGB_Linear_Blend=(p,c0,c1)=>{
var i=parseInt,r=Math.round,P=1-p,[a,b,c,d]=c0.split(","),[e,f,g,h]=c1.split(","),x=d||h,j=x?","+(!d?h:!h?d:r((parseFloat(d)*P+parseFloat(h)*p)*1000)/1000+")"):")";
return"rgb"+(x?"a(":"(")+r(i(a[3]=="a"?a.slice(5):a.slice(4))*P+i(e[3]=="a"?e.slice(5):e.slice(4))*p)+","+r(i(b)*P+i(f)*p)+","+r(i(c)*P+i(g)*p)+j;
}
@@ -20,7 +20,7 @@ exports.RGB_Linear_Blend=(p,c0,c1)=>{
* @param {string} c1 rgb(a) color2
* @returns color
*/
exports.RGB_Log_Blend=(p,c0,c1)=>{
export const RGB_Log_Blend=(p,c0,c1)=>{
var i=parseInt,r=Math.round,P=1-p,[a,b,c,d]=c0.split(","),[e,f,g,h]=c1.split(","),x=d||h,j=x?","+(!d?h:!h?d:r((parseFloat(d)*P+parseFloat(h)*p)*1000)/1000+")"):")";
return"rgb"+(x?"a(":"(")+r((P*i(a[3]=="a"?a.slice(5):a.slice(4))**2+p*i(e[3]=="a"?e.slice(5):e.slice(4))**2)**0.5)+","+r((P*i(b)**2+p*i(f)**2)**0.5)+","+r((P*i(c)**2+p*i(g)**2)**0.5)+j;
}
@@ -32,7 +32,7 @@ exports.RGB_Log_Blend=(p,c0,c1)=>{
* @param {string} c0 rgb(a) color
* @returns color
*/
exports.RGB_Linear_Shade=(p,c0)=>{
export const RGB_Linear_Shade=(p,c0)=>{
var i=parseInt,r=Math.round,[a,b,c,d]=c0.split(","),n=p<0,t=n?0:255*p,P=n?1+p:1-p;
return"rgb"+(d?"a(":"(")+r(i(a[3]=="a"?a.slice(5):a.slice(4))*P+t)+","+r(i(b)*P+t)+","+r(i(c)*P+t)+(d?","+d:")");
}
@@ -44,7 +44,7 @@ exports.RGB_Linear_Shade=(p,c0)=>{
* @param {string} c0 rgb(a) color
* @returns color
*/
exports.RGB_Log_Shade=(p,c0)=>{
export const RGB_Log_Shade=(p,c0)=>{
var i=parseInt,r=Math.round,[a,b,c,d]=c0.split(","),n=p<0,t=n?0:p*255**2,P=n?1+p:1-p;
return"rgb"+(d?"a(":"(")+r((P*i(a[3]=="a"?a.slice(5):a.slice(4))**2+t)**0.5)+","+r((P*i(b)**2+t)**0.5)+","+r((P*i(c)**2+t)**0.5)+(d?","+d:")");
}
@@ -56,7 +56,7 @@ exports.RGB_Log_Shade=(p,c0)=>{
* @param {string} color
* @returns color
*/
exports.RGB_Alpha_Shade = (p, color) => {
export const RGB_Alpha_Shade = (p, color) => {
var i = parseInt
var n = p < 0
var [r, g, b, a] = color.split(",")
+3 -3
View File
@@ -1,8 +1,8 @@
//! 更新默认主题配置后,需要执行 npm run build:theme 重新构建index.json
const fs = require('fs')
const path = require('path')
const { createThemeColors } = require('./utils')
import fs from 'fs'
import path from 'path'
import { createThemeColors } from './utils'
const defaultThemes = [
{

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Before

Width:  |  Height:  |  Size: 353 KiB

After

Width:  |  Height:  |  Size: 353 KiB

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Before

Width:  |  Height:  |  Size: 997 KiB

After

Width:  |  Height:  |  Size: 997 KiB

+2 -2
View File
@@ -1,6 +1,6 @@
const { RGB_Linear_Shade, RGB_Alpha_Shade } = require('./colorUtils')
import { RGB_Linear_Shade, RGB_Alpha_Shade } from './colorUtils'
exports.createThemeColors = (rgbaColor, fontRgbaColor, isDark) => {
export const createThemeColors = (rgbaColor, fontRgbaColor, isDark) => {
const colors = {
'--color-primary': rgbaColor,
}
+3 -3
View File
@@ -1,11 +1,11 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"paths": {
"@common/*": ["common/*"],
},
"typeRoots": [
"./types"
],
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
"@common/*": ["common/*"],
},
},
}
+1 -1
View File
@@ -1,4 +1,4 @@
import type { I18n } from '@/lang/i18n'
import type { I18n } from '@root/lang/i18n'
declare global {
+1 -1
View File
@@ -1,4 +1,4 @@
import { type Message } from '@/lang'
import { type Message } from '@root/lang'
// interface DownloadList {
-1
View File
@@ -1,6 +1,5 @@
import log from 'electron-log'
log.transports.file.level = 'info'
export const isLinux = process.platform == 'linux'
export const isWin = process.platform == 'win32'
@@ -1,4 +1,4 @@
const { getNow, TimeoutTools } = require('./utils')
import { getNow, TimeoutTools } from './utils'
// const fontFormateRxp = /(?=<\d+,\d+>).*?/g
const fontSplitRxp = /(?=<\d+,\d+>).*?/g
@@ -25,7 +25,7 @@ const createAnimation = (dom, duration, isVertical) => new window.Animation(new
// https://jsfiddle.net/ceqpnbky/
// https://jsfiddle.net/ceqpnbky/1/
module.exports = class FontPlayer {
export default class FontPlayer {
constructor({
time = 0,
rate = 1,
+3 -3
View File
@@ -1,9 +1,9 @@
const LinePlayer = require('./line-player')
const FontPlayer = require('./font-player')
import LinePlayer from './line-player'
import FontPlayer from './font-player'
const fontTimeExp = /<(\d+),(\d+)>/g
module.exports = class Lyric {
export default class Lyric {
constructor({
lyric = '',
extendedLyrics = [],
@@ -1,4 +1,4 @@
const { getNow, TimeoutTools } = require('./utils')
import { getNow, TimeoutTools } from './utils'
const timeFieldExp = /^(?:\[[\d:.]+\])+/g
const timeExp = /\d{1,3}(:\d{1,3}){0,2}(?:\.\d{1,3})/g
@@ -43,7 +43,7 @@ const parseExtendedLyric = (lrcLinesMap, extendedLyric) => {
}
}
module.exports = class LinePlayer {
export default class LinePlayer {
constructor({ offset = 0, rate = 1, onPlay = function() { }, onSetLyric = function() { } } = {}) {
this.tags = {}
this.lines = null
+2 -2
View File
@@ -1,7 +1,7 @@
const getNow = exports.getNow = typeof performance == 'object' && window.performance.now ? window.performance.now.bind(window.performance) : Date.now.bind(Date)
export const getNow = typeof performance == 'object' && window.performance.now ? window.performance.now.bind(window.performance) : Date.now.bind(Date)
exports.TimeoutTools = class TimeoutTools {
export class TimeoutTools {
constructor(thresholdTime = 80) {
this.invokeTime = 0
this.animationFrameId = null
+4 -4
View File
@@ -1,8 +1,8 @@
import fs from 'fs'
import crypto from 'crypto'
import { gzip, gunzip } from 'zlib'
import fs from 'node:fs'
import crypto from 'node:crypto'
import { gzip, gunzip } from 'node:zlib'
import path from 'node:path'
import { log } from '@common/utils'
import path from 'path'
export const joinPath = (...paths: string[]): string => path.join(...paths)
+15
View File
@@ -0,0 +1,15 @@
/* eslint-env node */
const { base, typescript } = require('../../.eslintrc.base.cjs')
module.exports = {
root: true,
...base,
overrides: [
{
...typescript,
parserOptions: {
project: './tsconfig.json',
},
},
],
}
+18
View File
@@ -0,0 +1,18 @@
/* eslint-env node */
const { base, typescript } = require('../../.eslintrc.base.cjs')
module.exports = {
root: true,
...base,
overrides: [
{
...typescript,
parserOptions: {
project: './tsconfig.json',
},
},
],
ignorePatterns: [
'vendors',
],
}
+14 -17
View File
@@ -1,4 +1,4 @@
import { join, dirname } from 'path'
import path from 'node:path'
import { existsSync, mkdirSync, renameSync } from 'fs'
import { app, shell, screen, nativeTheme, dialog } from 'electron'
import { URL_SCHEME_RXP } from '@common/constants'
@@ -10,22 +10,19 @@ import { createAppEvent, createListEvent } from '@main/event'
import { isMac, log } from '@common/utils'
import createWorkers from './worker'
import { migrateDBData } from './utils/migrate'
import { openDirInExplorer } from '@common/utils/electron'
import { encodePath, openDirInExplorer } from '@common/utils/electron'
export const initGlobalData = () => {
global.isDev = process.env.NODE_ENV !== 'production'
const envParams = parseEnvParams()
global.envParams = {
cmdParams: envParams.cmdParams,
deeplink: envParams.deeplink,
}
if (global.isDev) {
// eslint-disable-next-line no-undef
global.staticPath = webpackStaticPath
} else {
global.staticPath = join(__dirname, '/static')
}
global.staticPath =
process.env.NODE_ENV !== 'production'
? __STATIC_PATH__
: path.join(encodePath(__dirname), '../static')
}
export const initSingleInstanceHandle = () => {
@@ -75,10 +72,10 @@ export const applyElectronEnvParams = () => {
export const setUserDataPath = () => {
// windows平台下如果应用目录下存在 portable 文件夹则将数据存在此文件下
if (process.platform == 'win32') {
const portablePath = join(dirname(app.getPath('exe')), '/portable')
const portablePath = path.join(path.dirname(app.getPath('exe')), '/portable')
if (existsSync(portablePath)) {
app.setPath('appData', portablePath)
const appDataPath = join(portablePath, '/userData')
const appDataPath = path.join(portablePath, '/userData')
if (!existsSync(appDataPath)) mkdirSync(appDataPath)
app.setPath('userData', appDataPath)
}
@@ -86,12 +83,12 @@ export const setUserDataPath = () => {
const userDataPath = app.getPath('userData')
global.lxOldDataPath = userDataPath
global.lxDataPath = join(userDataPath, 'LxDatas')
global.lxDataPath = path.join(userDataPath, 'LxDatas')
if (!existsSync(global.lxDataPath)) mkdirSync(global.lxDataPath)
}
export const registerDeeplink = (startApp: () => void) => {
if (global.isDev && process.platform === 'win32') {
if (process.env.NODE_ENV !== 'production' && process.platform === 'win32') {
// Set the path of electron.exe and your app.
// These two additional parameters are only available on windows.
// console.log(process.execPath, process.argv)
@@ -117,8 +114,8 @@ export const registerDeeplink = (startApp: () => void) => {
export const listenerAppEvent = (startApp: () => void) => {
app.on('web-contents-created', (event, contents) => {
contents.on('will-navigate', (event, navigationUrl) => {
if (global.isDev) {
console.log('navigation to url:', navigationUrl)
if (process.env.NODE_ENV !== 'production') {
console.log('navigation to url:', navigationUrl.length > 130 ? navigationUrl.substring(0, 130) + '...' : navigationUrl)
return
}
if (!navigationUrlWhiteList.some(url => url.test(navigationUrl))) {
@@ -240,13 +237,13 @@ export const initAppSetting = async() => {
if (!isInitialized) {
let dbFileExists = await global.lx.worker.dbService.init(global.lxDataPath)
if (dbFileExists === null) {
const backPath = join(global.lxDataPath, `lx.data.db.${Date.now()}.bak`)
const backPath = path.join(global.lxDataPath, `lx.data.db.${Date.now()}.bak`)
dialog.showMessageBoxSync({
type: 'warning',
message: 'Database verify failed',
detail: `数据库表结构校验失败,我们将把有问题的数据库备份到:${backPath}\n若此问题导致你的数据丢失,你可以尝试从备份文件找回它们。\n\nThe database table structure verification failed, we will back up the problematic database to: ${backPath}\nIf this problem causes your data to be lost, you can try to retrieve them from the backup file.`,
})
renameSync(join(global.lxDataPath, 'lx.data.db'), backPath)
renameSync(path.join(global.lxDataPath, 'lx.data.db'), backPath)
openDirInExplorer(backPath)
dbFileExists = await global.lx.worker.dbService.init(global.lxDataPath)
}
+1 -1
View File
@@ -26,5 +26,5 @@ app.on('ready', () => {
})
// Require `main` process to boot app
require('./index')
import('./index')
+1
View File
@@ -1,4 +1,5 @@
import { app } from 'electron'
import './utils/logInit'
import '@common/error'
import {
initGlobalData,
+2 -2
View File
@@ -1,8 +1,8 @@
import WebSocket from 'ws'
import { encryptMsg, decryptMsg } from './utils'
import * as modules from './modules'
// import { action as commonAction } from '@/store/modules/common'
// import { getStore } from '@/store'
// import { action as commonAction } from '@root/store/modules/common'
// import { getStore } from '@root/store'
import registerSyncListHandler from './syncList'
import log from '../log'
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants'
+1 -1
View File
@@ -1,6 +1,6 @@
import handleAuth from './auth'
import { connect as socketConnect, disconnect as socketDisconnect, sendSyncStatus, sendSyncMessage } from './client'
// import { getSyncHost } from '@/utils/data'
// import { getSyncHost } from '@root/utils/data'
import { SYNC_CODE } from '@common/constants'
import log from '../log'
import { parseUrl } from './utils'
+3 -3
View File
@@ -1,6 +1,6 @@
import { Tray, Menu, nativeImage } from 'electron'
import { isWin } from '@common/utils'
import { join } from 'path'
import path from 'node:path'
import {
hideWindow as hideMainWindow,
isExistWindow as isExistMainWindow,
@@ -45,7 +45,7 @@ export const createTray = () => {
themeId = global.lx.appSetting['tray.themeId']
let theme = themeList.find(item => item.id === themeId) ?? themeList[0]
const iconPath = join(global.staticPath, 'images/tray', theme.fileName + '.png')
const iconPath = path.join(global.staticPath, 'images/tray', theme.fileName + '.png')
// 托盘
tray = new Tray(nativeImage.createFromPath(iconPath))
@@ -140,7 +140,7 @@ export const createMenu = () => {
export const setTrayImage = (themeId: number) => {
if (!tray) return
let theme = themeList.find(item => item.id === themeId) ?? themeList[0]
const iconPath = join(global.staticPath, 'images/tray', theme.fileName + '.png')
const iconPath = path.join(global.staticPath, 'images/tray', theme.fileName + '.png')
tray.setImage(nativeImage.createFromPath(iconPath))
}
-2
View File
@@ -1,2 +0,0 @@
exports.userApis = []
+2
View File
@@ -0,0 +1,2 @@
export const userApis: LX.UserApi.UserApiInfo[] = []
+8 -7
View File
@@ -1,7 +1,7 @@
import { mainSend } from '@common/mainIpc'
import { BrowserWindow } from 'electron'
import fs from 'fs'
import { join } from 'path'
import path from 'node:path'
import { openDevTools as handleOpenDevTools } from '@main/utils'
import { encodePath } from '@common/utils/electron'
@@ -27,15 +27,16 @@ const winEvent = () => {
export const createWindow = async(userApi: LX.UserApi.UserApiInfo) => {
await closeWindow()
dir ??= global.isDev ? webpackUserApiPath : join(encodePath(__dirname), 'userApi')
dir ??= process.env.NODE_ENV !== 'production' ? path.join(__USER_API_PATH__, 'renderer') : encodePath(__dirname)
if (!html) {
html = await fs.promises.readFile(join(dir, 'renderer/user-api.html'), 'utf8')
html = await fs.promises.readFile(path.join(dir, 'user-api.html'), 'utf8')
}
const preloadUrl = global.isDev
? `${join(encodePath(__dirname), '../dist/user-api-preload.js')}`
: `${join(encodePath(__dirname), 'user-api-preload.js')}`
// console.log(preloadUrl)
const preloadUrl = process.env.NODE_ENV !== 'production'
? `${path.join(encodePath(__dirname), '../dist/user-api-preload.js')}`
: `${path.join(encodePath(__dirname), '../preload/user-api-preload.js')}`
// console.log(preloadUrl, html)
/**
* Initial window options
+5 -5
View File
@@ -1,8 +1,8 @@
const { contextBridge, ipcRenderer } = require('electron')
const needle = require('needle')
const zlib = require('zlib')
const { createCipheriv, publicEncrypt, constants, randomBytes, createHash } = require('crypto')
const USER_API_RENDERER_EVENT_NAME = require('../rendererEvent/name')
import { contextBridge, ipcRenderer } from 'electron'
import needle from 'needle'
import zlib from 'zlib'
import { createCipheriv, publicEncrypt, constants, randomBytes, createHash } from 'crypto'
import USER_API_RENDERER_EVENT_NAME from '../rendererEvent/name'
for (const key of Object.keys(process.env)) {
if (/^(?:http_proxy|https_proxy|NO_PROXY)$/i.test(key)) delete process.env[key]
@@ -10,4 +10,5 @@ const names = {
for (const key of Object.keys(names)) {
names[key] = `userApi_${key}`
}
module.exports = names
export default names
@@ -104,7 +104,7 @@ export const loadApi = async(apiId: string) => {
// // const path = require('path')
// // // eslint-disable-next-line no-undef
// // userApi.script = require('fs').readFileSync(join(global.isDev ? __userApi : __dirname, 'renderer/test-api.js')).toString()
// // userApi.script = require('fs').readFileSync(join(process.env.NODE_ENV !== 'production' ? __userApi : __dirname, 'renderer/test-api.js')).toString()
// console.log('load api', userApi.name)
// mainSend(global.modules.userApiWindow, USER_API_RENDERER_EVENT_NAME.init, { userApi })
}
+2 -2
View File
@@ -1,4 +1,4 @@
import { join } from 'path'
import path from 'node:path'
import { BrowserWindow } from 'electron'
import { debounce, isLinux, isWin } from '@common/utils'
import { initWindowSize } from './utils'
@@ -139,7 +139,7 @@ export const createWindow = () => {
},
})
const winURL = global.isDev ? 'http://localhost:9081/lyric.html' : `file://${join(encodePath(__dirname), 'lyric.html')}`
const winURL = process.env.NODE_ENV !== 'production' ? 'http://localhost:9081' : `file://${path.join(encodePath(__dirname), '../renderer-lyric/index.html')}`
void browserWindow.loadURL(winURL + `?dark=${shouldUseDarkColors}&theme=${encodeURIComponent(JSON.stringify(theme))}`)
winEvent()
+2 -2
View File
@@ -1,5 +1,5 @@
import { BrowserWindow, dialog } from 'electron'
import { join } from 'path'
import path from 'node:path'
import { createTaskBarButtons, getWindowSizeInfo } from './utils'
import { isLinux, isWin } from '@common/utils'
import { openDevTools as handleOpenDevTools } from '@main/utils'
@@ -96,7 +96,7 @@ export const createWindow = () => {
}
browserWindow = new BrowserWindow(options)
const winURL = global.isDev ? 'http://localhost:9080' : `file://${join(encodePath(__dirname), 'index.html')}`
const winURL = process.env.NODE_ENV !== 'production' ? 'http://localhost:9080' : `file://${path.join(encodePath(__dirname), '../renderer/index.html')}`
void browserWindow.loadURL(winURL + `?dt=${!!global.envParams.cmdParams.dt}&dark=${shouldUseDarkColors}&theme=${encodeURIComponent(JSON.stringify(theme))}`)
winEvent()
@@ -1,5 +1,5 @@
import { createInflate, constants as zlibConstants } from 'node:zlib'
// import path from 'path'
import path from 'node:path'
import { mainHandle } from '@common/mainIpc'
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
@@ -38,9 +38,9 @@ const decode = async(str: string): Promise<string> => {
const handleDecode = async(lrc: string, tlrc: string, rlrc: string) => {
if (!qrc_decode) {
// const nativeBindingPath = path.join(__dirname, '../build/Release/qrc_decode.node')
// const nativeBindingPath = isDev ? path.join(__dirname, '../build/Release/qrc_decode.node')
// const nativeBindingPath = process.env.NODE_ENV !== 'production' ? path.join(__dirname, '../build/Release/qrc_decode.node')
// eslint-disable-next-line @typescript-eslint/no-var-requires
const addon = require('qrc_decode.node')
const addon = require(path.join(__QRC_DECODE_NODE_PATH__, 'qrc_decode.node'))
// console.log(addon)
qrc_decode = addon.qrc_decode
}
+2 -2
View File
@@ -1,5 +1,5 @@
// import fs from 'fs'
import { join } from 'path'
import path from 'node:path'
import { type WindowSize, windowSizeList } from '@common/config'
import { nativeImage } from 'electron'
@@ -8,7 +8,7 @@ export const getWindowSizeInfo = (windowSizeId: number | string): WindowSize =>
}
const getIconPath = (name: string): Electron.NativeImage => {
return nativeImage.createFromPath(join(global.staticPath, 'images/taskbar', name + '.png'))
return nativeImage.createFromPath(path.join(global.staticPath, 'images/taskbar', name + '.png'))
}
export const createTaskBarButtons = ({
+1 -1
View File
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
// "module": "esnext",
"resolveJsonModule": true,
"typeRoots": [
"./types"
],
+1 -1
View File
@@ -43,7 +43,7 @@ declare global {
// }
// }
var isDev: boolean
// var isDev: boolean
var envParams: LX.EnvParams
var staticPath: string
var lxDataPath: string
+3 -2
View File
@@ -9,6 +9,7 @@
// // }
// // }
declare const webpackStaticPath: string
declare const webpackUserApiPath: string
declare const __STATIC_PATH__: string
declare const __USER_API_PATH__: string
declare const __QRC_DECODE_NODE_PATH__: string
+4
View File
@@ -0,0 +1,4 @@
import log from 'electron-log'
log.transports.file.level = 'info'
log.initialize()
+5 -5
View File
@@ -1,8 +1,8 @@
import Store from 'electron-store'
import { dialog, shell } from 'electron'
import { join } from 'path'
import fs from 'fs'
import log from 'electron-log'
import path from 'node:path'
import fs from 'node:fs'
import { log } from '@common/utils'
type Stores = Record<string, Store>
@@ -27,8 +27,8 @@ export default (name: string, isIgnoredError = true, isShowErrorAlert = true): S
if (!isIgnoredError) throw error
const backPath = join(global.lxDataPath, name + '.json.bak')
fs.renameSync(join(global.lxDataPath, name + '.json'), backPath)
const backPath = path.join(global.lxDataPath, name + '.json.bak')
fs.renameSync(path.join(global.lxDataPath, name + '.json'), backPath)
if (isShowErrorAlert) {
dialog.showMessageBoxSync({
type: 'error',
+3 -3
View File
@@ -18,20 +18,20 @@ const initTables = (db: Database.Database) => {
// 打开、初始化数据库
export const init = (lxDataPath: string): boolean | null => {
const databasePath = path.join(lxDataPath, 'lx.data.db')
const nativeBinding = path.join(__dirname, '../node_modules/better-sqlite3/build/Release/better_sqlite3.node')
const nativeBinding = path.join(__dirname, '../../node_modules/better-sqlite3/build/Release/better_sqlite3.node')
let dbFileExists = true
try {
db = new Database(databasePath, {
fileMustExist: true,
nativeBinding,
verbose: global.isDev ? console.log : undefined,
verbose: process.env.NODE_ENV !== 'production' ? console.log : undefined,
})
} catch (error) {
console.log(error)
db = new Database(databasePath, {
nativeBinding,
verbose: global.isDev ? console.log : undefined,
verbose: process.env.NODE_ENV !== 'production' ? console.log : undefined,
})
initTables(db)
dbFileExists = false
+7 -7
View File
@@ -1,15 +1,15 @@
import { Worker } from 'worker_threads'
import { Worker } from 'node:worker_threads'
import * as Comlink from 'comlink'
import nodeEndpoint from 'comlink/dist/esm/node-adapter'
import path from 'node:path'
// import dbService from '../dbService/index'
export type DBSeriveTypes = Comlink.Remote<LX.WorkerDBSeriveListTypes>
export declare type DBSeriveTypes = Comlink.Remote<LX.WorkerDBSeriveListTypes>
export const createDBServiceWorker = () => {
const worker: Worker = new Worker(new URL(
/* webpackChunkName: 'dbService.worker' */
'../dbService',
import.meta.url,
))
// console.log(new URL('../dbService', import.meta.url))
// console.log(__dirname)
const worker: Worker = new Worker(path.join(__dirname, './dbService.worker'))
return Comlink.wrap<LX.WorkerDBSeriveListTypes>(nodeEndpoint(worker))
}
+3 -3
View File
@@ -1,9 +1,9 @@
import { parentPort } from 'worker_threads'
import worker from 'node:worker_threads'
import * as Comlink from 'comlink'
import nodeEndpoint from 'comlink/dist/esm/node-adapter'
export const exposeWorker = (obj: any) => {
if (parentPort == null) return
Comlink.expose(obj, nodeEndpoint(parentPort))
if (worker.parentPort == null) return
Comlink.expose(obj, nodeEndpoint(worker.parentPort))
}
+16
View File
@@ -0,0 +1,16 @@
/* eslint-env node */
const { base, typescript, vue } = require('../../.eslintrc.base.cjs')
module.exports = {
root: true,
...base,
overrides: [
vue,
{
...typescript,
parserOptions: {
project: './tsconfig.json',
},
},
],
}
+11 -14
View File
@@ -1,25 +1,22 @@
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context('./', true, /\.vue$/)
const requireComponent = import.meta.glob(['./**/*.vue', '!./**/components/**/*.vue'], { eager: true })
const vueFileRxp = /\.vue$/
const vueIndexFileRxp = /\/index\.vue$/
export default app => {
requireComponent.keys().forEach(fileName => {
const filePath = fileName.replace(/^\.\//, '')
Object.entries(requireComponent).forEach(([path, module]) => {
path = path.replace(/^\.\//, '')
let fileName = vueIndexFileRxp.test(path)
? path.replace(vueIndexFileRxp, '')
: path.replace(vueFileRxp, '')
if (!filePath.split('/').every((path, index, arr) => {
const char = path.charAt(0)
return vueFileRxp.test(path) || char.toUpperCase() !== char || arr[index + 1] == 'index.vue'
})) return
let componentName = upperFirst(camelCase(fileName))
const componentConfig = requireComponent(fileName)
// console.log(componentName)
let componentName = upperFirst(camelCase(filePath.replace(/\.\w+$/, '')))
if (componentName.endsWith('Index')) componentName = componentName.replace(/Index$/, '')
app.component(componentName, componentConfig.default || componentConfig)
app.component(componentName, module.default)
})
}
@@ -58,7 +58,7 @@
<script>
import { ref } from '@common/utils/vueTools'
import { setting, themeList } from '@lyric/store/state'
import { setting } from '@lyric/store/state'
import { updateSetting } from '@lyric/store/action'
export default {
@@ -105,7 +105,6 @@ export default {
}
return {
setting,
themeList,
isShowThemeList,
handleClose,
@@ -198,7 +198,7 @@ export default {
// // -webkit-text-fill-color: #fff;
// // -webkit-text-stroke: thin #124628;
// }
.lyric-space {
.lyricSpace {
height: 80%;
}
// .lyric-text {
@@ -224,7 +224,7 @@ export default {
}
}
}
.lrc-active-zoom {
.lrcActiveZoom {
:global {
.line-content {
&.active {
@@ -246,21 +246,21 @@ export default {
}
}
}
.font-weight-font {
.fontWeightFont {
:global {
.font-mode > .line {
font-weight: bold;
}
}
}
.font-weight-line {
.fontWeightLine {
:global {
.line-mode > .line {
font-weight: bold;
}
}
}
.font-weight-extended {
.fontWeightExtended {
:global {
.extended {
font-weight: bold;
@@ -196,7 +196,7 @@ export default {
// // -webkit-text-fill-color: #fff;
// // -webkit-text-stroke: thin #124628;
// }
.lyric-space {
.lyricSpace {
width: 80%;
height: 100%;
}
@@ -223,7 +223,7 @@ export default {
}
}
}
.lrc-active-zoom {
.lrcActiveZoom {
:global {
.line-content {
&.active {
@@ -245,21 +245,21 @@ export default {
}
}
}
.font-weight-font {
.fontWeightFont {
:global {
.font-mode > .line {
font-weight: bold;
}
}
}
.font-weight-line {
.fontWeightLine {
:global {
.line-mode > .line {
font-weight: bold;
}
}
}
.font-weight-extended {
.fontWeightExtended {
:global {
.extended {
font-weight: bold;
+1
View File
@@ -27,5 +27,6 @@
}
if (/theme=(.+)(#|$)/.test(window.location.search)) applyThemeColor(RegExp.$1)
</script>
<script type="module" src="/main.ts"></script>
</body>
</html>
+1 -1
View File
@@ -6,7 +6,7 @@ import mountComponents from './components'
import App from './App.vue'
import '@/common/error'
import '@root/common/error'
import { getSetting, onMainWindowInited, onSettingChanged, sendConnectMainWindowEvent } from './utils/ipc'
import { initSetting, mergeSetting } from './store/action'
import { init as initMainWindowChannel } from './core/mainWindowChannel'
+2 -2
View File
@@ -1,5 +1,5 @@
import type { I18n } from '@/lang'
import { createI18n, i18nPlugin, useI18n } from '@/lang'
import type { I18n } from '@root/lang'
import { createI18n, i18nPlugin, useI18n } from '@root/lang'
window.i18n = createI18n()
+2 -1
View File
@@ -2,12 +2,13 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"isolatedModules": true,
"moduleResolution": "nodenext",
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
"@common/*": ["common/*"],
// "@renderer/*": ["renderer/*"],
"@lyric/*": ["renderer-lyric/*"],
"@static/*": ["static/*"],
"@/*": ["./*"],
"@root/*": ["./*"],
},
"typeRoots": [ /* Specify multiple folders that act like './node_modules/@types'. */
"./types"
+19
View File
@@ -0,0 +1,19 @@
/* eslint-env node */
const { base, typescript, vue } = require('../../.eslintrc.base.cjs')
module.exports = {
root: true,
...base,
overrides: [
vue,
{
...typescript,
parserOptions: {
project: './tsconfig.json',
},
},
],
ignorePatterns: [
'vendors',
],
}
+2 -2
View File
@@ -51,7 +51,7 @@ export default {
default: false,
},
},
emits: ['update:model-value', 'change'],
emits: ['update:modelValue', 'change'],
data() {
return {
checked: false,
@@ -80,7 +80,7 @@ export default {
modelValue = checked
} else modelValue = checked ? this.value : ''
}
this.$emit('update:model-value', modelValue)
this.$emit('update:modelValue', modelValue)
this.$emit('change', modelValue)
},
setValue(value) {
+3 -3
View File
@@ -52,7 +52,7 @@ export default {
// default: true,
// },
},
emits: ['update:model-value', 'submit', 'change'],
emits: ['update:modelValue', 'submit', 'change'],
methods: {
handleInput(event) {
let value = event.target.value
@@ -60,7 +60,7 @@ export default {
value = value.trim()
event.target.value = value
}
this.$emit('update:model-value', value)
this.$emit('update:modelValue', value)
},
focus() {
this.$refs.dom_input.focus()
@@ -78,7 +78,7 @@ export default {
// if (dom_input.selectionStart == dom_input.selectionEnd) {
const value = text.substring(0, dom_input.selectionStart) + str + text.substring(dom_input.selectionEnd, text.length)
event.target.value = value
this.$emit('update:model-value', value)
this.$emit('update:modelValue', value)
// } else {
// clipboardWriteText(text.substring(dom_input.selectionStart, dom_input.selectionEnd))
// }
+2 -2
View File
@@ -48,13 +48,13 @@ export default {
default: 'name',
},
},
emits: ['update:model-value', 'menu-click'],
emits: ['update:modelValue', 'menu-click'],
setup(props, { emit }) {
const visible = computed(() => props.modelValue)
const location = computed(() => props.xy)
const onHide = () => {
emit('update:model-value', false)
emit('update:modelValue', false)
menuClick(null)
}
+2 -2
View File
@@ -42,7 +42,7 @@ export default {
default: '',
},
},
emits: ['update:model-value', 'change'],
emits: ['update:modelValue', 'change'],
data() {
return {
show: false,
@@ -84,7 +84,7 @@ export default {
handleClick(item) {
// console.log(this.modelValue)
if (item === this.modelValue) return
this.$emit('update:model-value', this.itemKey ? item[this.itemKey] : item)
this.$emit('update:modelValue', this.itemKey ? item[this.itemKey] : item)
this.$emit('change', item)
},
handleShow() {
+3 -3
View File
@@ -1,6 +1,6 @@
<template>
<div :class="[$style.sliderContent, { [$style.disabled]: disabled }, className]">
<div :class="[$style.slider ]">
<div :class="[$style.slider]">
<div ref="dom_sliderBar" :class="$style.sliderBar" :style="{ transform: `scaleX(${(value - min) / (max - min) || 0})` }" />
</div>
<div :class="$style.sliderMask" @mousedown="handleSliderMsDown" />
@@ -86,7 +86,7 @@ export default {
<style lang="less" module>
@import '@renderer/assets/styles/layout.less';
.slider-content {
.sliderContent {
flex: none;
position: relative;
width: 100px;
@@ -125,7 +125,7 @@ export default {
// opacity: .5;
// }
.slider-bar {
.sliderBar {
position: absolute;
left: 0;
top: 0;
+5 -5
View File
@@ -1,9 +1,9 @@
<template>
<ul :class="[$style.list, $style[align]]" role="tablist">
<li
v-for="item in list" :key="item[itemKey]" :class="[$style.listItem, {[$style.active]: modelValue == item[itemKey]}]" tabindex="-1"
role="tab" :aria-label="item[itemLabel]" ignore-tip :aria-selected="modelValue == item[itemKey]"
@click="handleToggle(item[itemKey])"
v-for="item in list"
:key="item[itemKey]" :class="[$style.listItem, {[$style.active]: modelValue == item[itemKey]}]" tabindex="-1" role="tab"
:aria-label="item[itemLabel]" ignore-tip :aria-selected="modelValue == item[itemKey]" @click="handleToggle(item[itemKey])"
>
<span :class="$style.label">{{ item[itemLabel] }}</span>
</li>
@@ -37,11 +37,11 @@ export default {
default: '',
},
},
emits: ['update:model-value', 'change'],
emits: ['update:modelValue', 'change'],
setup(props, { emit }) {
const handleToggle = id => {
if (id == props.modelValue) return
emit('update:model-value', id)
emit('update:modelValue', id)
emit('change', id)
}
@@ -54,19 +54,19 @@
// // div.list-item(@click="handleListItemClick($event, index)" @contextmenu="handleListItemRightClick($event, index)"
// // :class="[{ selected: rightClickSelectedIndex == index }, { active: selectedList.includes(item) }]")
// // div.list-item-cell.nobreak.center(:style="{ width: rowWidth.r1 }" style="padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}}
// // div.list-item-cell.nobreak.center(:style="{ width: rowWidth.r1 }" style="padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{ index + 1 }}
// // div.list-item-cell.auto(:style="{ width: rowWidth.r2 }" :aria-label="item.name + (item.meta._qualitys.flac32bit ? ` - ${$t('tag__lossless_24bit')}` : (item.meta._qualitys.ape || item.meta._qualitys.flac || item.meta._qualitys.wav) ? ` - ${$t('tag__lossless')}` : item.meta._qualitys['320k'] ? ` - ${$t('tag__high_quality')}` : '') + (sourceTag ? ` - ${item.source}` : '')")
// // span.select {{item.name}}
// // span.badge.badge-theme-primary(:class="[$style.labelQuality, $style.noSelect]" v-if="item.meta._qualitys.flac32bit") {{$t('tag__lossless_24bit')}}
// // span.badge.badge-theme-primary(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item.meta._qualitys.ape || item.meta._qualitys.flac || item.meta._qualitys.wav") {{$t('tag__lossless')}}
// // span.badge.badge-theme-secondary(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item.meta._qualitys['320k']") {{$t('tag__high_quality')}}
// // span.badge.badge-theme-tertiary(:class="[$style.labelQuality, $style.noSelect]" v-if="sourceTag") {{item.source}}
// // span.select {{ item.name }}
// // span.badge.badge-theme-primary(:class="[$style.labelQuality, $style.noSelect]" v-if="item.meta._qualitys.flac32bit") {{ $t('tag__lossless_24bit') }}
// // span.badge.badge-theme-primary(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item.meta._qualitys.ape || item.meta._qualitys.flac || item.meta._qualitys.wav") {{ $t('tag__lossless') }}
// // span.badge.badge-theme-secondary(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item.meta._qualitys['320k']") {{ $t('tag__high_quality') }}
// // span.badge.badge-theme-tertiary(:class="[$style.labelQuality, $style.noSelect]" v-if="sourceTag") {{ item.source }}
// // div.list-item-cell(:style="{ width: rowWidth.r3 }" :aria-label="item.singer")
// // span.select {{item.singer}}
// // span.select {{ item.singer }}
// // div.list-item-cell(:style="{ width: rowWidth.r4 }" :aria-label="item.albumName")
// // span.select {{item.meta.albumName}}
// // span.select {{ item.meta.albumName }}
// // div.list-item-cell(:style="{ width: rowWidth.r5 }")
// // span(:class="[$style.time, $style.noSelect]") {{item.meta.interval || '--/--'}}
// // span(:class="[$style.time, $style.noSelect]") {{ item.meta.interval || '--/--' }}
// // div.list-item-cell(:style="{ width: rowWidth.r6 }" style="padding-left: 0; padding-right: 0;")
// // material-list-buttons(:index="index" :class="$style.btns"
// // :remove-btn="false" @btn-click="handleListBtnClick"
@@ -22,7 +22,7 @@ import { watch, ref, onBeforeUnmount } from '@common/utils/vueTools'
import { defaultList, loveList, userLists } from '@renderer/store/list/state'
import { addListMusics, moveListMusics, createUserList, getMusicExistListIds } from '@renderer/store/list/action'
import useKeyDown from '@renderer/utils/compositions/useKeyDown'
import { useI18n } from '@/lang'
import { useI18n } from '@root/lang'
export default {
props: {
@@ -207,7 +207,7 @@ export default {
color: var(--color-primary);
}
.btn-content {
.btnContent {
flex: auto;
max-height: 100%;
padding-right: 15px;
@@ -21,7 +21,7 @@ import { computed } from '@common/utils/vueTools'
import { defaultList, loveList, userLists } from '@renderer/store/list/state'
import { addListMusics, moveListMusics, createUserList } from '@renderer/store/list/action'
import useKeyDown from '@renderer/utils/compositions/useKeyDown'
import { useI18n } from '@/lang'
import { useI18n } from '@root/lang'
export default {
props: {
@@ -164,7 +164,7 @@ export default {
}
}
.btn-content {
.btnContent {
flex: auto;
max-height: 100%;
padding-right: 15px;
@@ -104,7 +104,7 @@ export default {
position: relative;
border-radius: 40px;
}
.progress-mask {
.progressMask {
position: absolute;
left: 0;
top: 0;
@@ -112,7 +112,7 @@ export default {
height: 100%;
cursor: pointer;
}
.progress-bar {
.progressBar {
position: absolute;
left: 0;
top: 0;
@@ -120,22 +120,22 @@ export default {
height: 100%;
transform-origin: 0;
}
.progress-bar1 {
.progressBar1 {
background-color: var(--color-primary-light-100-alpha-600);
}
.progress-bar2 {
.progressBar2 {
background-color: var(--color-primary-light-100-alpha-400);
will-change: transform;
}
.progress-bar3 {
.progressBar3 {
background-color: var(--color-primary-light-100-alpha-200);
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
opacity: 0.5;
}
.bar-transition {
.barTransition {
transition-property: transform;
transition-timing-function: ease-out;
transition-duration: 0.2s;
@@ -1,90 +0,0 @@
<template>
<base-btn min :class="[$style.newPreset, {[$style.editing]: isEditing}]" :aria-label="$t('player__sound_effect_biquad_filter_save_btn')" @click="handleEditing($event)">
<svg-icon name="plus" />
<base-input ref="input" :class="$style.newPresetInput" :value="newPresetName" :placeholder="$t('player__sound_effect_biquad_filter_save_input')" @keyup.enter="handleSave($event)" @blur="handleSave($event)" />
</base-btn>
</template>
<script setup>
import { ref, nextTick } from '@common/utils/vueTools'
import { appSetting } from '@renderer/store/setting'
import { saveUserPitchShifterPreset } from '@renderer/store/soundEffect'
const isEditing = ref(false)
const input = ref(false)
const newPresetName = ref('')
const handleEditing = () => {
if (isEditing.value) return
// if (!this.newPresetName) this.newPresetName = this.listName
isEditing.value = true
nextTick(() => {
input.value.$el.focus()
})
}
const handleSave = (event) => {
let name = event.target.value.trim()
newPresetName.value = event.target.value = ''
isEditing.value = false
if (!name) return
if (name.length > 20) name = name.substring(0, 20)
saveUserPitchShifterPreset({
id: Date.now().toString(),
name,
playbackRate: appSetting['player.soundEffect.pitchShifter.playbackRate'],
})
}
</script>
<style lang="less" module>
@import '@renderer/assets/styles/layout.less';
.newPreset {
position: relative;
border: 1px dashed var(--color-primary-font-hover);
// background-color: var(--color-main-background);
color: var(--color-primary-font-hover);
opacity: .7;
height: 22px;
&.editing {
opacity: 1;
width: 90px;
svg {
display: none;
}
.newPresetInput {
display: block;
}
}
:global {
.svg-icon {
vertical-align: 0;
}
}
}
.newPresetInput {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
// line-height: 16px;
background: none !important;
font-size: 12px;
text-align: center;
font-family: inherit;
box-sizing: border-box;
padding: 0 3px;
border-radius: 0;
display: none;
&::placeholder {
font-size: 12px;
}
}
</style>
@@ -38,7 +38,7 @@
import { ref, onMounted, computed } from '@common/utils/vueTools'
import { appSetting, updateSetting } from '@renderer/store/setting'
import { convolutions } from '@renderer/plugins/player'
import AddConvolutionPresetBtn from './AddConvolutionPresetBtn'
import AddConvolutionPresetBtn from './AddConvolutionPresetBtn.vue'
import { getUserConvolutionPresetList, removeUserConvolutionPreset } from '@renderer/store/soundEffect'
const updateConvolution = val => {
@@ -27,7 +27,7 @@
import { onMounted, ref } from '@common/utils/vueTools'
import { freqs, freqsPreset } from '@renderer/plugins/player'
import { appSetting, updateSetting } from '@renderer/store/setting'
import AddEQPresetBtn from './AddEQPresetBtn'
import AddEQPresetBtn from './AddEQPresetBtn.vue'
import { getUserEQPresetList, removeUserEQPreset } from '@renderer/store/soundEffect'
const labels = freqs.map(num => num < 1000 ? num : `${num / 1000}k`)
@@ -29,9 +29,9 @@ import { ref } from '@common/utils/vueTools'
// import { saveVolumeIsMute } from '@renderer/store/setting'
// import { volume, isMute } from '@renderer/store/player/volume'
// import fs from 'node:fs'
import BiquadFilter from './BiquadFilter'
import AudioPanner from './AudioPanner'
import AudioConvolution from './AudioConvolution'
import BiquadFilter from './BiquadFilter.vue'
import AudioPanner from './AudioPanner.vue'
import AudioConvolution from './AudioConvolution.vue'
import PitchShifter from './PitchShifter.vue'
defineProps({
+11 -14
View File
@@ -1,25 +1,22 @@
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context('./', true, /\.vue$/)
const requireComponent = import.meta.glob(['./**/*.vue', '!./**/components/**/*.vue'], { eager: true })
const vueFileRxp = /\.vue$/
const vueIndexFileRxp = /\/index\.vue$/
export default app => {
requireComponent.keys().forEach(fileName => {
const filePath = fileName.replace(/^\.\//, '')
Object.entries(requireComponent).forEach(([path, module]) => {
path = path.replace(/^\.\//, '')
let fileName = vueIndexFileRxp.test(path)
? path.replace(vueIndexFileRxp, '')
: path.replace(vueFileRxp, '')
if (!filePath.split('/').every((path, index, arr) => {
const char = path.charAt(0)
return vueFileRxp.test(path) || char.toUpperCase() !== char || arr[index + 1] == 'index.vue'
})) return
let componentName = upperFirst(camelCase(fileName))
const componentConfig = requireComponent(fileName)
// console.log(componentName)
let componentName = upperFirst(camelCase(filePath.replace(/\.\w+$/, '')))
if (componentName.endsWith('Index')) componentName = componentName.replace(/Index$/, '')
app.component(componentName, componentConfig.default || componentConfig)
app.component(componentName, module.default)
})
}
@@ -10,8 +10,8 @@
import { isFullscreen } from '@renderer/store'
import { appSetting } from '@renderer/store/setting'
import ControlBtns from './ControlBtns'
import NavBar from './NavBar'
import ControlBtns from './ControlBtns.vue'
import NavBar from './NavBar.vue'
</script>
@@ -47,7 +47,7 @@
import { computed } from '@common/utils/vueTools'
import { useRouter } from '@common/utils/vueRouter'
import { clipboardWriteText } from '@common/utils/electron'
import ControlBtns from './ControlBtns'
import ControlBtns from './ControlBtns.vue'
// import PlayProgress from './PlayProgress'
import usePlayProgress from '@renderer/utils/compositions/usePlayProgress'
// import { lyric } from '@renderer/core/share/lyric'
@@ -284,7 +284,7 @@ export default {
gap: 18px;
}
.play-btn {
.playBtn {
flex: none;
height: 52%;
// margin-top: -2px;
@@ -47,7 +47,7 @@
import { computed } from '@common/utils/vueTools'
import { useRouter } from '@common/utils/vueRouter'
import { clipboardWriteText } from '@common/utils/electron'
import ControlBtns from './ControlBtns'
import ControlBtns from './ControlBtns.vue'
// import PlayProgress from './PlayProgress'
import usePlayProgress from '@renderer/utils/compositions/usePlayProgress'
// import { lyric } from '@renderer/core/share/lyric'
@@ -49,8 +49,8 @@
import { computed } from '@common/utils/vueTools'
import { useRouter } from '@common/utils/vueRouter'
import { clipboardWriteText } from '@common/utils/electron'
import ControlBtns from './ControlBtns'
import PlayProgress from './PlayProgress'
import ControlBtns from './ControlBtns.vue'
import PlayProgress from './PlayProgress.vue'
import usePlayProgress from '@renderer/utils/compositions/usePlayProgress'
// import { lyric } from '@renderer/core/share/lyric'
import {
@@ -314,7 +314,7 @@ export default {
gap: 18px;
}
.play-btn {
.playBtn {
flex: none;
height: 52%;
// margin-top: -2px;
@@ -56,7 +56,7 @@ import {
} from '@renderer/store/player/action'
import { onMounted, onBeforeUnmount, computed, reactive, ref, nextTick, watch } from '@common/utils/vueTools'
import useLyric from '@renderer/utils/compositions/useLyric'
import LyricMenu from './components/LyricMenu'
import LyricMenu from './components/LyricMenu.vue'
import { appSetting } from '@renderer/store/setting'
import { setLyricOffset } from '@renderer/core/lyric'
@@ -263,7 +263,7 @@ export default {
// font-size: 1.2em;
// }
}
.lrc-active-zoom {
.lrcActiveZoom {
:global {
.line-content {
&.active {
@@ -343,12 +343,12 @@ export default {
.lyricSelectlineExtended {
font-size: 14px;
}
.lrc-active {
.lrcActive {
color: var(--color-primary);
}
}
.lyric-space {
.lyricSpace {
height: 70%;
}
@@ -42,7 +42,7 @@ import { playNext, playPrev, togglePlay } from '@renderer/core/player'
import { status, isPlay } from '@renderer/store/player/state'
import usePlayProgress from '@renderer/utils/compositions/usePlayProgress'
import ControlBtns from './components/ControlBtns'
import ControlBtns from './components/ControlBtns.vue'
const {
nowPlayTimeStr,
@@ -71,13 +71,13 @@ const {
overflow: hidden;
}
.progress-container {
.progressContainer {
width: 100%;
position: relative;
padding: 3px 0;
}
.progress-content {
.progressContent {
position: relative;
height: 16px;
padding: 5px 0;
@@ -87,12 +87,12 @@ const {
height: 100%;
}
.bar-transition {
.barTransition {
transition-property: transform;
transition-timing-function: ease-out;
transition-duration: 0.2s;
}
.time-label {
.timeLabel {
width: 100%;
height: 18px;
display: flex;
@@ -104,7 +104,7 @@ const {
flex: auto;
}
.play-control {
.playControl {
flex: none;
height: 100%;
display: flex;
@@ -113,7 +113,7 @@ const {
padding: 0 25px;
color: var(--color-button-font);
}
.play-btn {
.playBtn {
height: 40%;
padding: 5px;
cursor: pointer;
@@ -125,7 +125,7 @@ const {
opacity: 1;
cursor: pointer;
+.play-btn {
+.playBtn {
margin-left: 10px;
}
svg {
@@ -2,10 +2,10 @@
<teleport to="#root">
<div ref="dom_menu" :class="$style.container" :style="menuStyles" :aria-hidden="!modelValue">
<!-- <div :class="$style.group">
<div :class="$style.title">{{$t('lyric_menu__align')}}</div>
<div :class="$style.title">{{ $t('lyric_menu__align') }}</div>
<div :class="$style.subGroup">
<div :class="[$style.btn, { [$style.active]: appSetting['playDetail.style.align'] == 'left' }]" role="button" @click="setFontAlign('left')" ignore-tip :aria-label="$t('lyric_menu__align_left')">{{$t('lyric_menu__align_left')}}</div>
<div :class="[$style.btn, { [$style.active]: appSetting['playDetail.style.align'] == 'center' }]" role="button" @click="setFontAlign('center')" ignore-tip :aria-label="$t('lyric_menu__align_center')">{{$t('lyric_menu__align_center')}}</div>
<div :class="[$style.btn, { [$style.active]: appSetting['playDetail.style.align'] == 'left' }]" role="button" @click="setFontAlign('left')" ignore-tip :aria-label="$t('lyric_menu__align_left')">{{ $t('lyric_menu__align_left') }}</div>
<div :class="[$style.btn, { [$style.active]: appSetting['playDetail.style.align'] == 'center' }]" role="button" @click="setFontAlign('center')" ignore-tip :aria-label="$t('lyric_menu__align_center')">{{ $t('lyric_menu__align_center') }}</div>
</div>
</div> -->
<div :class="$style.group">
@@ -83,7 +83,7 @@ export default {
required: true,
},
},
emits: ['updateLyric', 'update:model-value'],
emits: ['updateLyric', 'update:modelValue'],
setup(props, { emit }) {
// const appSetting = useRefGetter('appSetting')
// const playDetailSetting = useRefGetter('playDetailSetting')
@@ -98,7 +98,7 @@ export default {
const location = computed(() => props.xy)
const onHide = () => {
emit('update:model-value', false)
emit('update:modelValue', false)
}
const setFontAlign = val => {
@@ -11,11 +11,11 @@ div(:class="$style.container")
div.select(:class="$style.name") {{ item.userName }}
div(:class="$style.metaInfo")
time(v-if="item.timeStr" :class="$style.label") {{ timeFormat(item.timeStr) }}
div(v-if="item.location" :class="$style.label") {{$t('comment__location', { location: item.location })}}
div(v-if="item.location" :class="$style.label") {{ $t('comment__location', { location: item.location }) }}
div(v-if="item.likedCount != null" :class="$style.likes")
svg(:class="$style.likesIcon" version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" space="preserve")
use(xlink:href="#icon-thumbs-up")
| {{item.likedCount}}
| {{ item.likedCount }}
p.select(:class="$style.comment_text") {{ item.text }}
div(v-if="item.images?.length" :class="$style.comment_images")
img(v-for="(url, index) in item.images" :key="index" :src="url" loading="lazy" decoding="async")
@@ -33,14 +33,14 @@ div.comment(ref="dom_container" :class="$style.comment")
div(:class="$style.pagination")
material-pagination(:count="newComment.total" :btn-length="5" :limit="newComment.limit" :page="newComment.page" @btn-click="handleToggleCommentPage")
div(v-else :class="$style.unavailable")
p {{$t('comment__unavailable')}}
p {{ $t('comment__unavailable') }}
</template>
<script>
import { appSetting } from '@renderer/store/setting'
import { toOldMusicInfo } from '@renderer/utils'
import music from '@renderer/utils/musicSdk'
import CommentFloor from './CommentFloor'
import CommentFloor from './CommentFloor.vue'
export default {
name: 'MusicComment',
@@ -44,7 +44,7 @@ transition(enter-active-class="animated slideInRight" leave-active-class="animat
div.description(:class="$style.description")
p {{ $t('player__music_name') }}{{ musicInfo.name }}
p {{ $t('player__music_singer') }}{{ musicInfo.singer }}
p(v-if="musicInfo.album") {{ $t('player__music_album') }}{{musicInfo.album}}
p(v-if="musicInfo.album") {{ $t('player__music_album') }}{{ musicInfo.album }}
transition(enter-active-class="animated fadeIn" leave-active-class="animated fadeOut")
LyricPlayer(v-if="visibled")
@@ -70,9 +70,9 @@ import {
setShowPlayComment,
setShowPlayLrcSelectContentLrc,
} from '@renderer/store/player/action'
import LyricPlayer from './LyricPlayer'
import PlayBar from './PlayBar'
import MusicComment from './components/MusicComment'
import LyricPlayer from './LyricPlayer.vue'
import PlayBar from './PlayBar.vue'
import MusicComment from './components/MusicComment/index.vue'
import { registerAutoHideMounse, unregisterAutoHideMounse } from './autoHideMounse'
import { appSetting } from '@renderer/store/setting'
import { closeWindow, maxWindow, minWindow, setFullScreen } from '@renderer/utils/ipc'
@@ -9,8 +9,8 @@
<script setup>
import { isFullscreen } from '@renderer/store'
import { appSetting } from '@renderer/store/setting'
import ControlBtns from './ControlBtns'
import SearchInput from './SearchInput'
import ControlBtns from './ControlBtns.vue'
import SearchInput from './SearchInput.vue'
</script>
@@ -285,7 +285,7 @@ export default {
<style lang="less" module>
@import '@renderer/assets/styles/layout.less';
.song-list {
.songList {
overflow: hidden;
height: 100%;
display: flex;
@@ -8,7 +8,7 @@
:placeholder="placeholder"
@focus="handleFocus"
@blur="handleBlur"
@input="$emit('update:model-value', text)"
@input="$emit('update:modelValue', text)"
@change="sendEvent('change')"
@keyup.enter="handleSearch"
@keydown.arrow-down.arrow-up.prevent
@@ -82,7 +82,7 @@ export default {
default: false,
},
},
emits: ['update:model-value', 'event'],
emits: ['update:modelValue', 'event'],
data() {
return {
isShow: false,
@@ -182,11 +182,11 @@ export default {
str = str.replace(/\s+/g, ' ')
let dom_input = this.$refs.dom_input
this.text = this.text.substring(0, dom_input.selectionStart) + str + this.text.substring(dom_input.selectionEnd, this.text.length)
this.$emit('update:model-value', this.text)
this.$emit('update:modelValue', this.text)
},
handleClearList() {
this.text = ''
this.$emit('update:model-value', this.text)
this.$emit('update:modelValue', this.text)
this.sendEvent('submit')
},
},
@@ -3,12 +3,13 @@ import { getDuration, getPlaybackRate, getCurrentTime } from '@renderer/plugins/
import { isPlay, musicInfo, playMusicInfo } from '@renderer/store/player/state'
import { playProgress } from '@renderer/store/player/playProgress'
import { pause, play, playNext, playPrev, stop } from '@renderer/core/player'
import silenceAudio from '@renderer/assets/medias/Silence02s.mp3'
export default () => {
// 创建一个空白音频以保持对 Media Session 的注册
const emptyAudio = new Audio()
emptyAudio.autoplay = false
emptyAudio.src = require('@renderer/assets/medias/Silence02s.mp3')
emptyAudio.src = silenceAudio
emptyAudio.controls = false
emptyAudio.preload = 'auto'
emptyAudio.onplaying = () => {
@@ -16,40 +16,41 @@ import {
import { appSetting } from '@renderer/store/setting'
const cache = new Map<string, AudioBuffer>()
const loadBuffer = async(name: string) => new Promise<AudioBuffer>((resolve, reject) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('@renderer/assets/medias/filters/' + name) as string
if (cache.has(path)) {
resolve(cache.get(path) as AudioBuffer)
return
}
// Load buffer asynchronously
let request = new XMLHttpRequest()
request.open('GET', path, true)
request.responseType = 'arraybuffer'
const loadBuffer = async(name: string) => {
const path = (await import(`../../../assets/medias/filters/${name.replace('.wav', '')}.wav`)).default as string
return new Promise<AudioBuffer>((resolve, reject) => {
if (cache.has(path)) {
resolve(cache.get(path) as AudioBuffer)
return
}
// Load buffer asynchronously
let request = new XMLHttpRequest()
request.open('GET', path, true)
request.responseType = 'arraybuffer'
request.onload = function() {
// Asynchronously decode the audio file data in request.response
void getAudioContext().decodeAudioData(request.response, (buffer) => {
if (!buffer) {
reject(new Error('error decoding file data: ' + path))
return
}
cache.set(path, buffer)
resolve(buffer)
},
function(error) {
reject(error)
console.error('decodeAudioData error', error)
})
}
request.onload = function() {
// Asynchronously decode the audio file data in request.response
void getAudioContext().decodeAudioData(request.response, (buffer) => {
if (!buffer) {
reject(new Error('error decoding file data: ' + path))
return
}
cache.set(path, buffer)
resolve(buffer)
},
function(error) {
reject(error)
console.error('decodeAudioData error', error)
})
}
request.onerror = function() {
reject(new Error('XHR error'))
}
request.onerror = function() {
reject(new Error('XHR error'))
}
request.send()
})
request.send()
})
}
export default () => {
// console.log(appSetting['player.soundEffect.panner.enable'])
+2 -2
View File
@@ -2,14 +2,14 @@ import { watch } from '@common/utils/vueTools'
import { isFullscreen, proxy, sync, windowSizeList } from '@renderer/store'
import { appSetting } from '@renderer/store/setting'
import { sendSyncAction, setTaskBarProgress, setWindowSize } from '@renderer/utils/ipc'
import { setLanguage } from '@/lang'
import { setLanguage } from '@root/lang'
import { setUserApi } from '../apiSource'
// import { applyTheme, getThemes } from '@renderer/store/utils'
export default () => {
watch(() => appSetting['common.windowSizeId'], (index) => {
let info = index == null ? windowSizeList[2] : windowSizeList[index]
const info = index == null ? windowSizeList[2] : windowSizeList[index]
setWindowSize(info.width, info.height)
})
watch(() => appSetting['common.fontSize'], (fontSize) => {
+1
View File
@@ -31,5 +31,6 @@
}
if (/theme=(.+)(#|$)/.test(window.location.search)) applyThemeColor(RegExp.$1)
</script>
<script type="module" src="/main.ts"></script>
</body>
</html>
+4 -3
View File
@@ -1,4 +1,5 @@
import '@common/error'
import 'virtual:svg-icons-register'
import { createApp } from 'vue'
import './core/globalData'
@@ -18,8 +19,8 @@ import router from './router'
import { getSetting, updateSetting } from './utils/ipc'
import { langList } from '@/lang'
import type { I18n } from '@/lang/i18n'
import { langList } from '@root/lang'
import type { I18n } from '@root/lang/i18n'
import { initSetting } from './store/setting'
// import { bubbleCursor } from './utils/cursor-effects/bubbleCursor'
@@ -43,7 +44,7 @@ void getSetting().then(setting => {
// Set language automatically
if (!setting['common.langId'] || !window.i18n.availableLocales.includes(setting['common.langId'])) {
let langId: I18n['locale'] | null = null
let locale = window.navigator.language.toLocaleLowerCase() as I18n['locale']
const locale = window.navigator.language.toLocaleLowerCase() as I18n['locale']
if (window.i18n.availableLocales.includes(locale)) {
langId = locale
} else {
+2 -2
View File
@@ -9,8 +9,8 @@
</template>
<script>
import Modal from '@renderer/components/material/Modal'
import Btn from '@renderer/components/base/Btn'
import Modal from '@renderer/components/material/Modal.vue'
import Btn from '@renderer/components/base/Btn.vue'
import { useI18n } from '@renderer/plugins/i18n'
import { computed } from '@common/utils/vueTools'
export default {
+1 -1
View File
@@ -1,4 +1,4 @@
import Dialog from './Dialog'
import Dialog from './Dialog.vue'
import { createApp } from 'vue'
+4 -4
View File
@@ -1,8 +1,8 @@
import SvgIcon from './SvgIcon'
import SvgIcon from './SvgIcon.vue'
const req = require.context('@renderer/assets/svgs', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
// const req = require.context('@renderer/assets/svgs', false, /\.svg$/)
// const requireAll = requireContext => requireContext.keys().map(requireContext)
// requireAll(req)
export default app => {
app.component('svg-icon', SvgIcon)
+2 -2
View File
@@ -1,5 +1,5 @@
import type { I18n } from '@/lang'
import { createI18n, i18nPlugin, useI18n } from '@/lang'
import type { I18n } from '@root/lang'
import { createI18n, i18nPlugin, useI18n } from '@root/lang'
window.i18n = createI18n()
+3 -5
View File
@@ -1,3 +1,5 @@
import PhaseVocoder from './pitch-shifter/phase-vocoder.js?worker&url'
let audio: HTMLAudioElement | null = null
let audioContext: AudioContext
let mediaSource: MediaElementAudioSourceNode
@@ -276,11 +278,7 @@ const loadPitchShifterNode = () => {
pitchShifterNodeLoadStatus = 'loading'
initAdvancedAudioFeatures()
// source -> analyser -> biquadFilter -> audioWorklet(pitch shifter) -> [(convolver & convolverSource)->convolverDynamicsCompressor] -> panner -> gain
void audioContext.audioWorklet.addModule(new URL(
/* webpackChunkName: 'pitch_shifter.audioWorklet' */
'./pitch-shifter/phase-vocoder.js',
import.meta.url,
)).then(() => {
void audioContext.audioWorklet.addModule(PhaseVocoder).then(() => {
console.log('pitch shifter audio worklet loaded')
// https://github.com/olvb/phaze/issues/26#issuecomment-1574629971
pitchShifterNode = new AudioWorkletNode(audioContext, 'phase-vocoder-processor', { outputChannelCount: [2] })
+15 -7
View File
@@ -2,6 +2,14 @@
// import Vue from 'vue'
import { createRouter, createWebHashHistory } from 'vue-router'
import SearchView from './views/Search/index.vue'
import SongListView from './views/songList/List/index.vue'
import SongListDetailView from './views/songList/Detail/index.vue'
import LeaderboardView from './views/Leaderboard/index.vue'
import ListView from './views/List/index.vue'
import DownloadView from './views/Download/index.vue'
import SettingView from './views/Setting/index.vue'
const router = createRouter({
history: createWebHashHistory(),
@@ -9,7 +17,7 @@ const router = createRouter({
{
path: '/search',
name: 'Search',
component: require('./views/Search/index.vue').default,
component: SearchView,
meta: {
name: 'Search',
},
@@ -17,7 +25,7 @@ const router = createRouter({
{
path: '/songList/list',
name: 'SongList',
component: require('./views/songList/List/index.vue').default,
component: SongListView,
meta: {
name: 'SongList',
},
@@ -25,7 +33,7 @@ const router = createRouter({
{
path: '/songList/detail',
name: 'SongListDetail',
component: require('./views/songList/Detail/index.vue').default,
component: SongListDetailView,
meta: {
name: 'SongList',
},
@@ -33,7 +41,7 @@ const router = createRouter({
{
path: '/leaderboard',
name: 'Leaderboard',
component: require('./views/Leaderboard/index.vue').default,
component: LeaderboardView,
meta: {
name: 'Leaderboard',
},
@@ -41,7 +49,7 @@ const router = createRouter({
{
path: '/list',
name: 'List',
component: require('./views/List/index.vue').default,
component: ListView,
meta: {
name: 'List',
},
@@ -49,7 +57,7 @@ const router = createRouter({
{
path: '/download',
name: 'Download',
component: require('./views/Download/index.vue').default,
component: DownloadView,
meta: {
name: 'Download',
},
@@ -57,7 +65,7 @@ const router = createRouter({
{
path: '/setting',
name: 'Setting',
component: require('./views/Setting/index.vue').default,
component: SettingView,
meta: {
name: 'Setting',
},
+3 -3
View File
@@ -1,10 +1,10 @@
import { ref, reactive, shallowRef, markRaw, computed, watch } from '@common/utils/vueTools'
import { windowSizeList as configWindowSizeList } from '@common/config'
import { appSetting } from './setting'
import pkg from '../../../package.json'
import { version } from '../../../package.json'
import { type ProgressInfo } from 'electron-updater'
import music from '@renderer/utils/musicSdk'
process.versions.app = pkg.version
process.versions.app = version
export const apiSource = ref<string | null>(null)
export const proxy: {
@@ -115,7 +115,7 @@ export const versionInfo = window.lxData.versionInfo = reactive<{
status: LX.UpdateStatus
downloadProgress: ProgressInfo | null
}>({
version: pkg.version,
version,
newVersion: null,
showModal: false,
reCheck: false,
+10 -7
View File
@@ -2,22 +2,25 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"isolatedModules": true,
"module": "ESNext",
"moduleResolution": "nodenext",
"resolveJsonModule": true,
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
"@common/*": ["common/*"],
"@renderer/*": ["renderer/*"],
// "@lyric/*": ["renderer-lyric/*"],
"@static/*": ["static/*"],
"@/*": ["./*"],
"@root/*": ["./*"],
},
"typeRoots": [ /* Specify multiple folders that act like './node_modules/@types'. */
"renderer/types"
"./types"
],
},
// "vueCompilerOptions": {
// "plugins": [
// "@volar/vue-language-plugin-pug"
// ]
// }
"vueCompilerOptions": {
"plugins": [
"@vue/language-plugin-pug"
]
}
// "include": [
// "./**/*.ts",
// // "./**/*.js",
+2
View File
@@ -0,0 +1,2 @@
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference types="vite/client" />
+1 -1
View File
@@ -1,4 +1,4 @@
import { type I18n } from '@/lang'
import { type I18n } from '@root/lang'
declare module 'vue' {
interface ComponentCustomProperties {
@@ -1,6 +1,6 @@
// Support qualitys: 128k 320k flac wav
module.exports = [
export default [
{
id: 'test',
name: '测试接口',

Some files were not shown because too many files have changed in this diff Show More