diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 703d4bd..31652c7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,41 +92,9 @@ jobs: node-version: 16 # Need for npm >=7.7 cache: 'npm' - ## Yarn dir - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - ## Cache - - name: Cache yarn cache - uses: actions/cache@v2 - id: cache-yarn-cache2 - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn2-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn2- - - ## Cache - - name: Cache node_modules - id: cache-node-modules2 - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules2-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.node-version }}-nodemodules2- - - - name: Download standalone Python - run: | - ./downloadPython.sh - ## Dependencies - name: Install Dependencies - if: | - steps.cache-yarn-cache.outputs.cache-hit != 'true' || - steps.cache-node-modules.outputs.cache-hit != 'true' - run: yarn --frozen-lockfile + - uses: bahmutov/npm-install@v1 # The easiest way to transfer release notes to a compiled application is create `release-notes.md` in the build resources. # See https://github.com/electron-userland/electron-builder/issues/1511#issuecomment-310160119 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f1a9fa3..2d0b4d0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,58 +23,14 @@ jobs: e2e: strategy: matrix: - os: [macos-latest] + os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - - ## Yarn dir - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - ## Cache - - name: Cache yarn cache - uses: actions/cache@v2 - id: cache-yarn-cache2 - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn2-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn2- - - ## Cache - - name: Cache node_modules - id: cache-node-modules2 - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules2-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.node-version }}-nodemodules2- - - - name: Configure pip caching - id: cache-pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Download standalone Python - run: | - ./downloadPython.sh - - ## Dependencies - - name: Install Dependencies - if: | - steps.cache-yarn-cache.outputs.cache-hit != 'true' || - steps.cache-node-modules.outputs.cache-hit != 'true' - run: yarn --frozen-lockfile + - uses: bahmutov/npm-install@v1 ## Test - name: Run tests - run: yarn test + run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test diff --git a/.github/workflows/typechecking.yml b/.github/workflows/typechecking.yml index e8c8cb7..1236e57 100644 --- a/.github/workflows/typechecking.yml +++ b/.github/workflows/typechecking.yml @@ -63,4 +63,4 @@ jobs: # Type checking is divided into three separate commands for more convenient logs - run: yarn typecheck-main - run: yarn typecheck-preload - - run: yarn typecheck-rendere + - run: yarn typecheck-renderer diff --git a/.vscode/launch.json b/.vscode/launch.json index e38cd4c..c677085 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,16 +2,28 @@ "version": "0.2.0", "configurations": [ { - "name": "Debug Main Process", "type": "node", "request": "launch", - "cwd": "${workspaceFolder}", + "name": "Electron: Main", "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron", + "runtimeArgs": ["--remote-debugging-port=5858", "."], "windows": { "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd" - }, - "args": ["."], - "outputCapture": "std" + } + }, + { + "name": "Electron: Renderer", + "type": "chrome", + "request": "attach", + "port": 5858, + "webRoot": "${workspaceFolder}", + "timeout": 30000 + } + ], + "compounds": [ + { + "name": "Electron: All", + "configurations": ["Electron: Main", "Electron: Renderer"] } ] } diff --git a/README.md b/README.md index 67ebd11..18ffe49 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,10 @@ # Slipmat Soundboard -An opinionated template for running Python with Electron. Uses TypeScript, Vue 3, and Vite for bundling. Based on [vite-electron-builder](https://github.com/cawa-93/vite-electron-builder) by Alex Kozack and [datasette-app](https://github.com/simonw/datasette-app) by Simon Willison. - -The JavaScript/TypeScript parts are configured to work as closely with Vue best practises, and they follow the same principles than my [vite-ts-tailwind-starter](https://github.com/Uninen/vite-ts-tailwind-starter). - +Simple soundboard for DJs. ## Getting started #### Install Dependencies -- Install wget: `brew install wget` -- Install Python: `./downloadPython` - Install Electron dependencies: `yarn` #### Start Development Environment diff --git a/package.json b/package.json index 717c2da..9c6e6d2 100644 --- a/package.json +++ b/package.json @@ -20,14 +20,15 @@ "compilearm": "yarn build && SKIP_NOTARIZATION=1 electron-builder build --config .electron-builder.config.js --dir --config.asar=false --arm64", "compilex64": "yarn build && SKIP_NOTARIZATION=1 electron-builder build --config .electron-builder.config.js --dir --config.asar=false --x64", "pretest": "npm run build", + "test:ci": "xvfb-run tests/app.spec.js", "test": "node tests/app.spec.js", "watch": "node scripts/watch.js", "dev": "yarn watch", "lint": "eslint . --ext js,ts,vue", "typecheck-main": "tsc --noEmit -p packages/main/tsconfig.json", "typecheck-preload": "tsc --noEmit -p packages/preload/tsconfig.json", - "typecheck-rendere": "vue-tsc --noEmit -p packages/renderer/tsconfig.json", - "typecheck": "yarn typecheck-main && yarn typecheck-preload && yarn typecheck-rendere", + "typecheck-renderer": "vue-tsc --noEmit -p packages/renderer/tsconfig.json", + "typecheck": "yarn typecheck-main && yarn typecheck-preload && yarn typecheck-renderer", "pretypecheck-renderer": "dts-cb -i packages/preload/src/**/*.ts -o packages/preload/exposedInMainWorld.d.ts" }, "browserslist": [ @@ -37,14 +38,21 @@ "pre-push": "yarn typecheck" }, "dependencies": { + "@nuxt/devalue": "^2.0.0", + "add-filename-increment": "^1.0.0", "autoprefixer": "^10.4", "electron-settings": "^4.0", + "electron-store": "^8.0.1", "electron-updater": "^4.6", "electron-util": "^0.17", "events": "^3.3", "pinia": "^2.0", "postcss": "^8.4", + "postcss-import": "^14.0.2", + "rambda": "^7.0.2", "tailwindcss": "^3.0", + "uniquefilename": "^1.1.2", + "unused-filename": "^4.0.0", "v8-compile-cache": "^2.3.0", "vue": "^3.2", "vue-router": "^4.0" diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts index 30b2f2c..b8440f8 100644 --- a/packages/main/src/index.ts +++ b/packages/main/src/index.ts @@ -1,8 +1,16 @@ -import { app, BrowserWindow } from 'electron' +import { app, BrowserWindow, ipcMain, dialog } from 'electron' import { join } from 'path' +import { mkdir } from 'fs/promises' import { URL } from 'url' -import { enforceApplicationFolder } from './utils' -// import 'v8-compile-cache' +// import devalue from '@nuxt/devalue' +import Store from 'electron-store' +import 'v8-compile-cache' + +import { + enforceApplicationFolder, + pathAvailable, + filepathsToSamples, +} from './utils' import './security' // import settings from 'electron-settings' @@ -10,10 +18,29 @@ import './security' // See https://www.electronjs.org/docs/latest/tutorial/offscreen-rendering app.disableHardwareAcceleration() +const store = new Store({ + name: 'pinia', + // serialize: devalue, +}) const isSingleInstance = app.requestSingleInstanceLock() +const userDataPath = app.getPath('userData') +const samplePath = join(userDataPath, 'samples') +console.log('userDataPath', userDataPath) +pathAvailable(samplePath).then((available) => { + if (available) { + mkdir(samplePath).then(() => { + console.log('Created sample folder') + }) + } +}) + const isDevelopment = import.meta.env.MODE === 'development' +const openDevtools = isDevelopment +// const openDevtools = true + // const prodDebug = import.meta.env.VITE_PROD_DEBUG === '1' -let mainWindow: BrowserWindow | null = null +let rendererReady = false +let mainWindow: BrowserWindow const mainPageUrl = isDevelopment && import.meta.env.VITE_DEV_SERVER_URL !== undefined ? import.meta.env.VITE_DEV_SERVER_URL @@ -38,18 +65,78 @@ if (isDevelopment) { .catch((e) => console.error('Failed to install Vue devtools extension:', e)) } +ipcMain.on('electron-store-get', async (event, val) => { + event.returnValue = store.get(val) +}) +ipcMain.on('electron-store-set', async (event, key, val) => { + console.log('store set: ', val) + console.log('store set: ', typeof val) + store.set(key, val) +}) + +ipcMain.on('rendererReady', () => { + console.log('renderer is ready!') + rendererReady = true +}) + +ipcMain.on('openSamplesFilepicker', () => { + console.log('main -> openSamplesFilepicker') + // Opening file dialog more than once hangs without this + // https://github.com/electron/electron/issues/20533 + const interval = setInterval(() => { + /* nothing */ + }, 50) + + dialog + .showOpenDialog({ + filters: [{ name: 'Samples', extensions: ['mp3', 'wav', 'aac'] }], + properties: ['openFile', 'multiSelections'], + }) + .then((result) => { + clearInterval(interval) + filepathsToSamples(samplePath, result.filePaths).then((samples) => { + console.log('got samples: ', samples) + mainWindow.webContents.send('addedSamples', samples) + }) + }) + .catch((err) => { + console.error('main -> openSamplesFilepicker CATCH', err) + // return reject(err) + }) +}) + +function initMain() { + return new Promise((resolve) => { + resolve() + }) +} + const createWindow = async () => { mainWindow = new BrowserWindow({ width: 800, height: 600, show: false, // Use 'ready-to-show' event to show window titleBarStyle: 'hidden', + trafficLightPosition: { x: 18, y: 18 }, webPreferences: { webviewTag: false, nativeWindowOpen: true, preload: join(__dirname, '../../preload/dist/index.cjs'), }, }) + mainWindow.setBackgroundColor('#1C1E20') + + if (openDevtools) { + mainWindow.webContents.openDevTools() + } + + mainWindow.on('focus', () => { + mainWindow.webContents.send('focus') + }) + + mainWindow.on('blur', () => { + mainWindow.webContents.send('blur') + }) /** * If you install `show: true` then it can cause issues when trying to close the window. @@ -58,9 +145,8 @@ const createWindow = async () => { * @see https://github.com/electron/electron/issues/25012 */ mainWindow.on('ready-to-show', async () => { - mainWindow?.show() - if (isDevelopment) { - mainWindow?.webContents.openDevTools() + if (rendererReady) { + mainWindow.show() } }) @@ -70,6 +156,7 @@ const createWindow = async () => { app .whenReady() .then(enforceApplicationFolder) + .then(initMain) .then(createWindow) .catch((e) => { console.error('Failed create window:', e) diff --git a/packages/main/src/utils.ts b/packages/main/src/utils.ts index ffcaadf..82222f8 100644 --- a/packages/main/src/utils.ts +++ b/packages/main/src/utils.ts @@ -1,8 +1,13 @@ +import { join, dirname, extname, basename } from 'path' +import { stat, copyFile } from 'fs/promises' +import { createHash } from 'crypto' import { app, dialog } from 'electron' -import type { BrowserWindow } from 'electron' import { is } from 'electron-util' +import type { BrowserWindow } from 'electron' +import type { Sample } from 'root/types' const isDevelopment = import.meta.env.MODE === 'development' +const isTestRun = app.commandLine.getSwitchValue('testrun') export function fadeWindowOut(_window: BrowserWindow) { let opacity = _window.getOpacity() @@ -20,7 +25,7 @@ export function fadeWindowOut(_window: BrowserWindow) { export function enforceApplicationFolder() { return new Promise((resolve, reject) => { - if (isDevelopment || !is.macos) { + if (isTestRun || isDevelopment || !is.macos) { console.log('Skipping enforceApplicationFolder') return resolve() } @@ -61,3 +66,91 @@ export function enforceApplicationFolder() { } }) } + +export async function pathAvailable(path: string) { + return !(await stat(path).catch(() => false)) +} + +type fileDict = { + dir: string + ext: string + base: string + increment: number +} + +export function findUniqueFilename(filepath: string) { + const file: fileDict = { + dir: dirname(filepath), + ext: extname(filepath), + base: basename(filepath, extname(filepath)), + increment: 0, + } + + return new Promise(function (resolve) { + findIncrementalUniqueFilename(file, (filename: string) => { + resolve(filename) + }) + }) +} + +function findIncrementalUniqueFilename( + fdict: fileDict, + callback: (filename: string) => void +) { + let append = '' + + if (fdict.increment > 0) { + append = '_' + fdict.increment + } + + console.log('old filename: ', join(fdict.dir, fdict.base + fdict.ext)) + const newFilename = join(fdict.dir, fdict.base + append + fdict.ext) + pathAvailable(newFilename).then((available) => { + if (!available) { + console.log('not available: ', newFilename) + setImmediate(function () { + fdict.increment += 1 + return findIncrementalUniqueFilename(fdict, callback) + }) + } else { + console.log('is available: ', newFilename) + return callback(newFilename) + } + }) +} + +export async function asyncForEach( + array: T[], + callback: (item: T, index: number, allItems: T[]) => void +) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array) + } +} + +export async function filepathsToSamples( + samplePath: string, + filePaths: string[] +) { + const samples: Sample[] = [] + const md5 = createHash('md5') + + if (filePaths.length > 0) { + await asyncForEach(filePaths, async (originalPath) => { + const filename = basename(originalPath) + const sampleFilePath = join(samplePath, filename) + const unique = await findUniqueFilename(sampleFilePath) + await copyFile(originalPath, unique) + // console.log('copied FROM ', originalPath) + // console.log('copied TO ', unique) + samples.push({ + id: md5.update(unique).copy().digest('hex'), + name: filename, + path: unique, + mode: 'oneshot', + }) + // console.log('samples length: ', samples.length) + }) + } + return samples +} diff --git a/packages/main/tsconfig.json b/packages/main/tsconfig.json index 827a4ab..b74c2f4 100644 --- a/packages/main/tsconfig.json +++ b/packages/main/tsconfig.json @@ -4,7 +4,8 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "root/*": ["../../*"] } }, "files": ["src/index.ts"], diff --git a/packages/preload/exposedInMainWorld.d.ts b/packages/preload/exposedInMainWorld.d.ts index 100b1fa..f9cbd95 100644 --- a/packages/preload/exposedInMainWorld.d.ts +++ b/packages/preload/exposedInMainWorld.d.ts @@ -5,11 +5,5 @@ interface Window { * console.log( window.versions ) */ readonly versions: NodeJS.ProcessVersions; - /** - * Safe expose node.js API - * @example - * window.nodeCrypto('data') - */ - readonly nodeCrypto: { sha256sum(data: import("crypto").BinaryLike): string; }; - readonly api: { send: (channel: string, data: unknown) => void; sendSync: (channel: string, data: unknown) => void; receive: (channel: string, func: any) => void; }; + readonly api: { send: (channel: string, data?: unknown) => void; sendSync: (channel: string, data?: unknown) => void; receive: (channel: string, func: any) => void; store: { get(val: any): any; set(property: string, val: any): void; }; }; } diff --git a/packages/preload/src/index.ts b/packages/preload/src/index.ts index d02ced5..9971b89 100644 --- a/packages/preload/src/index.ts +++ b/packages/preload/src/index.ts @@ -1,8 +1,5 @@ import { contextBridge, ipcRenderer } from 'electron' -// import 'v8-compile-cache' - -import type { BinaryLike } from 'crypto' -import { createHash } from 'crypto' +import 'v8-compile-cache' /** * The "Main World" is the JavaScript context that your main renderer code runs in. @@ -27,27 +24,23 @@ import { createHash } from 'crypto' */ contextBridge.exposeInMainWorld('versions', process.versions) -/** - * Safe expose node.js API - * @example - * window.nodeCrypto('data') - */ -contextBridge.exposeInMainWorld('nodeCrypto', { - sha256sum(data: BinaryLike) { - const hash = createHash('sha256') - hash.update(data) - return hash.digest('hex') - }, -}) - contextBridge.exposeInMainWorld('api', { - send: (channel: string, data: unknown) => { + send: (channel: string, data?: unknown) => { ipcRenderer.send(channel, data) }, - sendSync: (channel: string, data: unknown) => { + sendSync: (channel: string, data?: unknown) => { ipcRenderer.sendSync(channel, data) }, receive: (channel: string, func: any) => { ipcRenderer.on(channel, (event, ...args) => func(...args)) }, + + store: { + get(val: any) { + return ipcRenderer.sendSync('electron-store-get', val) + }, + set(property: string, val: any) { + ipcRenderer.send('electron-store-set', property, val) + }, + }, }) diff --git a/packages/renderer/.eslintrc.json b/packages/renderer/.eslintrc.json index b404609..29acad4 100644 --- a/packages/renderer/.eslintrc.json +++ b/packages/renderer/.eslintrc.json @@ -16,6 +16,7 @@ }, "rules": { "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-explicit-any": "off", "semi": ["error", "never"] } } diff --git a/packages/renderer/assets/main.postcss b/packages/renderer/assets/main.postcss index 918727d..39bdcad 100644 --- a/packages/renderer/assets/main.postcss +++ b/packages/renderer/assets/main.postcss @@ -4,9 +4,69 @@ html { @apply bg-black text-gray-200; + font-size: 14px; } -#title { +*, +a, +button { + cursor: default; + user-select: none; +} + +::-webkit-scrollbar { + background-color: #2b2b2b; + border-left: 1px solid #3e3e3e; + width: 0.65rem; +} + +::-webkit-scrollbar-thumb { + background: #6b6b6b; + border-radius: 10px; +} + +h1 { + @apply font-semibold text-4xl mb-4; +} + +h2 { + @apply font-semibold text-2xl mb-4; +} + +h3 { + @apply font-semibold text-xl mb-4; +} + +.os-draggable { -webkit-app-region: drag; - height: 26px; +} + +.btn { + background: #5a5c5e; + color: #e6e7e7; + padding: 0px 10px; + border: 1px solid #222; + border-radius: 0.25rem; + display: inline-block; + text-align: center; + box-shadow: 0px 2px 3px -2px rgba(0, 0, 0, 0.3); + user-select: none; + font-size: 13px; + + &.big { + background: #5a5c5e; + color: #e6e7e7; + padding: 10px 20px; + border: 1px solid #222; + border-radius: 0.25rem; + display: inline-block; + text-align: center; + box-shadow: 0px 2px 3px -2px rgba(0, 0, 0, 0.3); + user-select: none; + font-size: 16px; + } + + &:active { + background: #6a6c6e; + } } diff --git a/packages/renderer/index.html b/packages/renderer/index.html index 822563b..f7a554f 100644 --- a/packages/renderer/index.html +++ b/packages/renderer/index.html @@ -1,5 +1,5 @@ - + -
+
diff --git a/packages/renderer/src/App.vue b/packages/renderer/src/App.vue index c6655b2..0116eea 100644 --- a/packages/renderer/src/App.vue +++ b/packages/renderer/src/App.vue @@ -1,26 +1,56 @@ - - + + + + diff --git a/packages/renderer/src/index.ts b/packages/renderer/src/index.ts index a857192..4e4c606 100644 --- a/packages/renderer/src/index.ts +++ b/packages/renderer/src/index.ts @@ -1,11 +1,13 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' + import App from '@/App.vue' import router from '@/router' import '../assets/main.postcss' +const pinia = createPinia() const app = createApp(App) -app.use(createPinia()) +app.use(pinia) app.use(router) app.mount('#app') diff --git a/packages/renderer/src/pages/EditMode.vue b/packages/renderer/src/pages/EditMode.vue new file mode 100644 index 0000000..29868ca --- /dev/null +++ b/packages/renderer/src/pages/EditMode.vue @@ -0,0 +1,66 @@ + + + diff --git a/packages/renderer/src/pages/Home.vue b/packages/renderer/src/pages/Home.vue index 0eece58..f18c9e0 100644 --- a/packages/renderer/src/pages/Home.vue +++ b/packages/renderer/src/pages/Home.vue @@ -1,20 +1,19 @@ + diff --git a/packages/renderer/src/pages/PlayMode.vue b/packages/renderer/src/pages/PlayMode.vue new file mode 100644 index 0000000..86d1688 --- /dev/null +++ b/packages/renderer/src/pages/PlayMode.vue @@ -0,0 +1,14 @@ + + + diff --git a/packages/renderer/src/store.ts b/packages/renderer/src/store.ts new file mode 100644 index 0000000..aa043d0 --- /dev/null +++ b/packages/renderer/src/store.ts @@ -0,0 +1,114 @@ +import { toRaw } from 'vue' +import { defineStore } from 'pinia' +import type { Sample, Board, UiMode } from 'root/types' +import { find, filter } from 'rambda' + +export const useStore = defineStore('main', { + state: () => ({ + debug: + import.meta.env.MODE === 'development' || + import.meta.env.VITE_PROD_DEBUG === '1', + isInited: false, + ui: { + firstStart: true, + mode: 'play' as UiMode, + inFocus: true, + }, + boards: [] as Board[], + samples: [] as Sample[], + files: [] as string[], + }), + actions: { + initApp() { + return new Promise((resolve) => { + const boards = window.api.store.get('boards') as undefined | Board[] + const samples = window.api.store.get('samples') as undefined | Sample[] + + if (!boards || boards.length === 0) { + this.ui.firstStart = true + this.ui.mode = 'edit' + console.log('this is first start!') + this.boards.push({ + id: 'default', + name: 'Default', + sampleIds: [], + }) + this.saveStore() + } else if (boards[0].sampleIds.length === 0) { + console.log('this is not first start but there are no samples') + this.ui.firstStart = true + this.ui.mode = 'edit' + this.boards.push(...boards) + } else { + console.log('this is not first start!') + this.ui.firstStart = false + this.boards.push(...boards) + if (samples && samples.length > 0) { + this.samples.push(...samples) + } + } + + this.isInited = true + window.api.send('rendererReady') + resolve() + }) + }, + + saveStore() { + const boards = toRaw(this.boards) + const samples = toRaw(this.samples) + console.log('saving boards: ', boards) + console.log('saving samples: ', samples) + window.api.store.set('boards', boards) + window.api.store.set('samples', samples) + }, + + changeFocus(inFocus: boolean) { + this.ui.inFocus = inFocus + }, + + addSamples(samples: Sample[]) { + this.samples.push(...samples) + for (const sample of samples) { + const board = this.boards[0] + if (board) { + board.sampleIds.push(sample.id) + } + } + this.saveStore() + }, + }, + getters: { + headerBgColor(state) { + if (state.ui.inFocus) { + if (state.ui.mode === 'play') { + return '#333537' + } else { + return '#6A3832' + } + } else { + return '#25282B' + } + }, + headerTextColor(state) { + if (state.ui.inFocus) { + return '#eaeaeb' + } else { + return '#66696C' + } + }, + + getSamplesForBoard(state) { + return (id: string) => { + const predicate = (board: Board) => board.id === id + const board = find(predicate, state.boards) + if (!board) { + return [] + } + const samplePredicate = (sample: Sample) => + board.sampleIds.includes(sample.id) + return filter(samplePredicate, state.samples) + } + }, + }, +}) diff --git a/packages/renderer/src/store/index.ts b/packages/renderer/src/store/index.ts deleted file mode 100644 index 8307033..0000000 --- a/packages/renderer/src/store/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { defineStore } from 'pinia' -import type { LogItem } from 'mainpkg/types' - -export const useStore = defineStore('main', { - state: () => ({ - debug: - import.meta.env.MODE === 'development' || - import.meta.env.VITE_PROD_DEBUG === '1', - isInited: false, - loadingMessages: [] as LogItem[], - }), - actions: { - initApp() { - // eslint-disable-next-line - return new Promise((resolve, reject) => { - this.isInited = true - console.log('store inited') - resolve() - }) - }, - }, -}) diff --git a/packages/renderer/tsconfig.json b/packages/renderer/tsconfig.json index d5c6896..1e17570 100644 --- a/packages/renderer/tsconfig.json +++ b/packages/renderer/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "paths": { "@/*": ["./src/*"], - "mainpkg/*": ["../main/src/*"] + "mainpkg/*": ["../main/src/*"], + "root/*": ["../../*"] }, "lib": ["ESNext", "dom", "dom.iterable"] }, diff --git a/postcss.config.js b/postcss.config.js index 2959567..dcb4751 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1 +1,8 @@ -module.exports = { plugins: { tailwindcss: {}, autoprefixer: {} } } +module.exports = { + plugins: [ + require('postcss-import'), + require('tailwindcss/nesting'), + require('tailwindcss'), + require('autoprefixer'), + ], +} diff --git a/scripts/watch.js b/scripts/watch.js index 512fbbe..31983e5 100644 --- a/scripts/watch.js +++ b/scripts/watch.js @@ -78,7 +78,11 @@ const setupMainPackageWatcher = (viteDevServer) => { spawnProcess = null } - spawnProcess = spawn(String(electronPath), ['--inspect=5858', '.']) + spawnProcess = spawn(String(electronPath), [ + '--testrun=1', + '--inspect=5858', + '.', + ]) spawnProcess.stdout.on( 'data', diff --git a/tailwind.config.js b/tailwind.config.js index a73647e..059435b 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,7 +1,11 @@ module.exports = { content: ['packages/**/*.{html,vue,ts,js}'], theme: { - extend: {}, + extend: { + borderRadius: { + native: '0.2rem', + }, + }, }, plugins: [], } diff --git a/tests/app.spec.js b/tests/app.spec.js index d7a8bc2..e658530 100644 --- a/tests/app.spec.js +++ b/tests/app.spec.js @@ -3,7 +3,7 @@ const { strict: assert } = require('assert') // Playwright has EXPERIMENTAL electron support. ;(async () => { - const electronApp = await electron.launch({ args: ['.'] }) + const electronApp = await electron.launch({ args: ['--testrun=1', '.'] }) /** * App main window state diff --git a/types/index.ts b/types/index.ts new file mode 100644 index 0000000..a9c0a18 --- /dev/null +++ b/types/index.ts @@ -0,0 +1,20 @@ +export type UiMode = 'play' | 'edit' +export interface SampleMetadata { + shortcutKey?: string + volume?: number + tags?: string[] + [key: string]: any +} +export interface Sample { + id: string + name: string + path: string + mode: 'oneshot' | 'loop' + metadata?: SampleMetadata +} + +export interface Board { + id: string + name: string + sampleIds: string[] +} diff --git a/yarn.lock b/yarn.lock index f342b81..6aa6bc1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -162,6 +162,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@nuxt/devalue@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@nuxt/devalue/-/devalue-2.0.0.tgz#c7bd7e9a516514e612d5d2e511ffc399e0eac322" + integrity sha512-YBI/6o2EBz02tdEJRBK8xkt3zvOFOWlLBf7WKYGBsSYSRtjjgrqPe2skp6VLLmKx5WbHHDNcW+6oACaurxGzeA== + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -589,6 +594,13 @@ acorn@^8.7.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== +add-filename-increment@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/add-filename-increment/-/add-filename-increment-1.0.0.tgz#106feea10dcda5d2bfe25a5e83d347382f9dd97d" + integrity sha512-pFV8VZX8jxuVMIycKvGZkWF/ihnUubu9lbQVnOnZWp7noVxbKQTNj7zG2y9fXdPcuZ6lAN3Drr517HaivGCjdQ== + dependencies: + strip-filename-increment "^2.0.1" + agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -596,6 +608,13 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.4.1: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" @@ -611,6 +630,16 @@ ajv@^6.10.0, ajv@^6.12.0, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.6.3: + version "8.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d" + integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ansi-align@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" @@ -773,6 +802,11 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +atomically@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/atomically/-/atomically-1.7.0.tgz#c07a0458432ea6dbc9a3506fffa424b48bccaafe" + integrity sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w== + autoprefixer@^10.4: version "10.4.2" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.2.tgz#25e1df09a31a9fba5c40b578936b90d35c9d4d3b" @@ -814,7 +848,7 @@ bluebird-lst@^1.0.9: dependencies: bluebird "^3.5.5" -bluebird@^3.5.0, bluebird@^3.5.5: +bluebird@^3.4.1, bluebird@^3.5.0, bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -1149,6 +1183,22 @@ concat-stream@^1.6.2: readable-stream "^2.2.2" typedarray "^0.0.6" +conf@^10.0.3: + version "10.1.1" + resolved "https://registry.yarnpkg.com/conf/-/conf-10.1.1.tgz#ff08046d5aeeee0eaff55d57f5b4319193c3dfda" + integrity sha512-z2civwq/k8TMYtcn3SVP0Peso4otIWnHtcTuHhQ0zDZDdP4NTxqEc8owfkz4zBsdMYdn/LFcE+ZhbCeqkhtq3Q== + dependencies: + ajv "^8.6.3" + ajv-formats "^2.1.1" + atomically "^1.7.0" + debounce-fn "^4.0.0" + dot-prop "^6.0.1" + env-paths "^2.2.1" + json-schema-typed "^7.0.3" + onetime "^5.1.2" + pkg-up "^3.1.0" + semver "^7.3.5" + config-chain@^1.1.11: version "1.1.13" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" @@ -1236,6 +1286,13 @@ csstype@^2.6.8: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.19.tgz#feeb5aae89020bb389e1f63669a5ed490e391caa" integrity sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ== +debounce-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-4.0.0.tgz#ed76d206d8a50e60de0dd66d494d82835ffe61c7" + integrity sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ== + dependencies: + mimic-fn "^3.0.0" + debug@4, debug@4.3.3, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" @@ -1421,6 +1478,13 @@ dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" +dot-prop@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== + dependencies: + is-obj "^2.0.0" + dotenv-expand@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" @@ -1529,6 +1593,14 @@ electron-settings@^4.0: mkdirp "^1.0.4" write-file-atomic "^3.0.3" +electron-store@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-8.0.1.tgz#9b598c1d2edeffebee9d8c1cd957ad368c528925" + integrity sha512-ZyLvNywiqSpbwC/pp89O/AycVWY/UJIkmtyzF2Bd0Nm/rLmcFc0NTGuLdg6+LE8mS8qsiK5JMoe4PnrecLHH5w== + dependencies: + conf "^10.0.3" + type-fest "^1.0.2" + electron-to-chromium@^1.4.17: version "1.4.68" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz#d79447b6bd1bec9183f166bb33d4bef0d5e4e568" @@ -1609,7 +1681,7 @@ entities@^3.0.1: resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== -env-paths@^2.2.0: +env-paths@^2.2.0, env-paths@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== @@ -1806,6 +1878,11 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + eslint-config-prettier@^8.3: version "8.3.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" @@ -2136,6 +2213,13 @@ find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -2771,6 +2855,16 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema-typed@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9" + integrity sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -2891,6 +2985,14 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + lodash.escaperegexp@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" @@ -3009,6 +3111,16 @@ mime@^2.5.2: resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" + integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== + mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -3154,6 +3266,13 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -3178,6 +3297,13 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -3185,11 +3311,23 @@ p-locate@^2.0.0: dependencies: p-limit "^1.1.0" +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" @@ -3232,6 +3370,11 @@ path-exists@^3.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= +path-exists@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -3267,6 +3410,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -3280,6 +3428,13 @@ pinia@^2.0: "@vue/devtools-api" "^6.0.0-beta.21" vue-demi "*" +pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + playwright-core@1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.19.0.tgz#54fb197d1820d6821b4e800857c7cda19e6b45fe" @@ -3322,6 +3477,15 @@ pngjs@6.0.0: resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821" integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg== +postcss-import@^14.0.2: + version "14.0.2" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.0.2.tgz#60eff77e6be92e7b67fe469ec797d9424cae1aa1" + integrity sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + postcss-js@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.0.tgz#31db79889531b80dc7bc9b0ad283e418dce0ac00" @@ -3352,7 +3516,7 @@ postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9: cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss-value-parser@^4.2.0: +postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== @@ -3550,6 +3714,11 @@ quick-lru@^5.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== +rambda@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/rambda/-/rambda-7.0.2.tgz#5a136cf41c869ab6bd5953cddfbd6dc27413a867" + integrity sha512-oM1In0rXyFIzhYDIwREhdOm+oTDwtGW5WIgo/m8TqzS5kSBvsu8UoqzQFrSA0SJCdWJQCqOsvK0pKEINTwcEIA== + rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -3560,6 +3729,13 @@ rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha1-5mTvMRYRZsl1HNvo28+GtftY93Q= + dependencies: + pify "^2.3.0" + read-config-file@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/read-config-file/-/read-config-file-6.2.0.tgz#71536072330bcd62ba814f91458b12add9fc7ade" @@ -3622,12 +3798,17 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.1.6, resolve@^1.10.1, resolve@^1.15.1, resolve@^1.20.0, resolve@^1.22.0: +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.1, resolve@^1.15.1, resolve@^1.20.0, resolve@^1.22.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -3909,6 +4090,11 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-filename-increment@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-filename-increment/-/strip-filename-increment-2.0.1.tgz#3feca2ebd529d4cdcad0281a9bbb2660b096fcc6" + integrity sha512-+v5xsiTTsdYqkPj7qz1zlngIsjZedhHDi3xp/9bMurV8kXe9DAr732gNVqtt4X8sI3hOqS3nlFfps5gyVcux6w== + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -4090,6 +4276,11 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +type-fest@^1.0.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -4124,6 +4315,13 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" +uniquefilename@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/uniquefilename/-/uniquefilename-1.1.2.tgz#d608595e96666aecf5f4fbbab3def5e46c587908" + integrity sha1-1ghZXpZmauz19Pu6s9715GxYeQg= + dependencies: + bluebird "^3.4.1" + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -4134,6 +4332,14 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +unused-filename@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unused-filename/-/unused-filename-4.0.0.tgz#5692d8a206e4cad91847bae69762e62d42064bbe" + integrity sha512-6CyVM8YwcGecQTSNhP6NaALlBzYvHLLrAlX9PWfZRA5jZJiUe5inwXwIUUmfBxzLAxXeZ4n9AxqyOTruRFrWkA== + dependencies: + escape-string-regexp "^5.0.0" + path-exists "^5.0.0" + unzip-crx-3@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/unzip-crx-3/-/unzip-crx-3-0.2.0.tgz#d5324147b104a8aed9ae8639c95521f6f7cda292"