diff --git a/.gitignore b/.gitignore index c0fc7a9..5592227 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ node_modules .next app dist +out/ +.DS_Store \ No newline at end of file diff --git a/electron-builder.yml b/electron-builder.yml index 6d4ba8a..533ff14 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -2,11 +2,42 @@ appId: com.blade.wled.manager productName: WLED Manager copyright: Copyright © 2021 YeonV aka Blade directories: - output: dist buildResources: resources files: - - from: . - filter: - - package.json - - app + - '!**/.vscode/*' + - '!src/*' + - '!electron.vite.config.{js,ts,mjs,cjs}' + - '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}' + - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' + - '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}' +asarUnpack: + - 'resources/**' +win: + executableName: app +nsis: + artifactName: ${name}-${version}-setup.${ext} + shortcutName: ${productName} + uninstallDisplayName: ${productName} + createDesktopShortcut: always +mac: + entitlementsInherit: build/entitlements.mac.plist + icon: build/icon.png + extendInfo: + - NSCameraUsageDescription: Application requests access to the device's camera. + - NSMicrophoneUsageDescription: Application requests access to the device's microphone. + - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. + - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. + notarize: false +dmg: + artifactName: ${name}-${version}.${ext} +linux: + target: + - AppImage + - snap + - deb + maintainer: electronjs.org + category: Utility +appImage: + artifactName: ${name}-${version}.${ext} +npmRebuild: false publish: null diff --git a/electron.vite.config.ts b/electron.vite.config.ts new file mode 100644 index 0000000..a017480 --- /dev/null +++ b/electron.vite.config.ts @@ -0,0 +1,23 @@ +import { resolve } from 'path' +import { defineConfig, externalizeDepsPlugin } from 'electron-vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + main: { + plugins: [externalizeDepsPlugin()] + }, + preload: { + plugins: [externalizeDepsPlugin()] + }, + renderer: { + build: { + assetsInlineLimit: 0 + }, + resolve: { + alias: { + '@renderer': resolve('src/renderer/src') + } + }, + plugins: [react()] + } +}) diff --git a/main/background.ts b/main/background.ts deleted file mode 100644 index ae3d2b9..0000000 --- a/main/background.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { - app, - Tray, - Menu, - nativeImage, - ipcMain -} from 'electron'; -import serve from 'electron-serve'; -import { createWindow } from './helpers'; -import { DgramAsPromised } from "dgram-as-promised" -import installExtension, { REDUX_DEVTOOLS } from 'electron-devtools-installer'; - - -const path = require('path'); -const isProd: boolean = process.env.NODE_ENV === 'production'; - - -if (isProd) { - serve({ directory: 'app' }); -} else { - app.setPath('userData', `${app.getPath('userData')} (development)`); -} - -let tray = null; - -(async () => { - await app.whenReady(); - - - app.commandLine.appendSwitch('disable-renderer-backgrounding'); - app.commandLine.appendSwitch('disable-background-timer-throttling'); - app.commandLine.appendSwitch('disable-backgrounding-occluded-windows'); - - const mainWindow = createWindow('main', { - width: 480, - height: 800, - titleBarStyle: "hidden", - webPreferences: { - // nodeIntegration: true, - contextIsolation: false, - // nodeIntegrationInWorker: true, - enableRemoteModule: true, - webSecurity: false, - backgroundThrottling: false - }, - }); - - ipcMain.on('resize-me-please', (event, arg) => { - mainWindow.setSize(arg[0], arg[1]) - }) - ipcMain.on('close', (event) => { - app.quit(); - }) - - let socket - let PORT - let message - - ipcMain.on('UDP-start', () => { - socket = DgramAsPromised.createSocket("udp4") - PORT = 21324 - }) - - ipcMain.on('UDP', async (event, arg) => { - message = Buffer.from(arg[1]) - await socket.send(message, 0, message.length, PORT, arg[0].ip) - }) - ipcMain.on('UDP-stop', async () => { - await socket.stop() - }) - - // ipcMain.on('UDPSR-start', () => { - // socket = DgramAsPromised.createSocket("udp4") - // PORT = 11988 - // }) - - // ipcMain.on('UDPSR', async (event, arg) => { - // console.log(arg[1]) - // message = Buffer.from(arg[1]) - // await socket.send(message, 0, message.length, PORT, '239.0.0.1') - // }) - // ipcMain.on('UDPSR-stop', async () => { - // await socket.stop() - // }) - - // tray = new Tray(nativeImage.createFromDataURL('data:image/x-icon;base64,AAABAAEAEBAAAAEAGACGAAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAE1JREFUOI1j/P//PwOxgNGeAUMxE9G6cQCKDWAhpADZ2f8PMjBS3QW08QK20KaZC2gfC9hCnqouoNgARgY7zMxAyNlUdQHlXiAlO2MDAD63EVqNHAe0AAAAAElFTkSuQmCC')) - - - const icon = nativeImage.createFromPath(path.join(__dirname, 'images/logo32.png')).resize({ width: 16, height: 16 }) - // const icon = isProd ? nativeImage.createFromPath('./images/logo256.png') : path.join(__dirname, 'images/logo256.png') - tray = new Tray(icon) - - const contextMenu = Menu.buildFromTemplate([ - { label: 'Show', click: () => mainWindow.show() }, - { label: 'Minimize', click: () => mainWindow.minimize() }, - { label: 'Minimize to tray', click: () => mainWindow.hide() }, - // { label: 'Test Notifiation', click: () => showNotification() }, - { label: 'seperator', type: 'separator' }, - { label: 'Dev', click: () => mainWindow.webContents.openDevTools() }, - { label: 'seperator', type: 'separator' }, - { label: 'Exit', click: () => app.quit() } - ]) - tray.setToolTip('WLED Manager') - tray.setContextMenu(contextMenu) - - if (isProd) { - await mainWindow.loadURL('app://./home.html'); - // mainWindow.webContents.openDevTools({ mode: 'detach' }); - } else { - const port = process.argv[2]; - await mainWindow.loadURL(`http://localhost:${port}/home`); - installExtension(REDUX_DEVTOOLS) - .then((name) => console.log(`Added Extension: ${name}`)) - .catch((err) => console.log('An error occurred: ', err)); - mainWindow.webContents.openDevTools({ mode: 'detach' }); - } -})(); - -app.on('window-all-closed', () => { - app.quit(); -}); diff --git a/main/helpers/create-window.ts b/main/helpers/create-window.ts deleted file mode 100644 index 9c9abe0..0000000 --- a/main/helpers/create-window.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - screen, - BrowserWindow, - BrowserWindowConstructorOptions, -} from 'electron'; -import Store from 'electron-store'; - -export default (windowName: string, options: BrowserWindowConstructorOptions): BrowserWindow => { - const key = 'window-state'; - const name = `window-state-${windowName}`; - const store = new Store({ name }); - const defaultSize = { - width: options.width, - height: options.height, - }; - let state = {}; - let win; - - const restore = () => store.get(key, defaultSize); - - const getCurrentPosition = () => { - const position = win.getPosition(); - const size = win.getSize(); - return { - x: position[0], - y: position[1], - width: size[0], - height: size[1], - }; - }; - - const windowWithinBounds = (windowState, bounds) => { - return ( - windowState.x >= bounds.x && - windowState.y >= bounds.y && - windowState.x + windowState.width <= bounds.x + bounds.width && - windowState.y + windowState.height <= bounds.y + bounds.height - ); - }; - - const resetToDefaults = () => { - const bounds = screen.getPrimaryDisplay().bounds; - return Object.assign({}, defaultSize, { - x: (bounds.width - defaultSize.width) / 2, - y: (bounds.height - defaultSize.height) / 2, - }); - }; - - const ensureVisibleOnSomeDisplay = windowState => { - const visible = screen.getAllDisplays().some(display => { - return windowWithinBounds(windowState, display.bounds); - }); - if (!visible) { - // Window is partially or fully not visible now. - // Reset it to safe defaults. - return resetToDefaults(); - } - return windowState; - }; - - const saveState = () => { - if (!win.isMinimized() && !win.isMaximized()) { - Object.assign(state, getCurrentPosition()); - } - store.set(key, state); - }; - - state = ensureVisibleOnSomeDisplay(restore()); - - const browserOptions: BrowserWindowConstructorOptions = { - ...options, - ...state, - width: options.width, - height: options.height, - autoHideMenuBar: true, - frame: false, - titleBarStyle: "hidden", - webPreferences: { - nodeIntegration: true, - // nodeIntegrationInWorker: true, - contextIsolation: false, - enableRemoteModule: true, - ...options.webPreferences, - }, - }; - win = new BrowserWindow(browserOptions); - - win.on('close', saveState); - - return win; -}; diff --git a/main/helpers/index.ts b/main/helpers/index.ts deleted file mode 100644 index da26903..0000000 --- a/main/helpers/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import createWindow from './create-window'; - -export { - createWindow, -}; diff --git a/package.json b/package.json index 49d277d..20d83e5 100644 --- a/package.json +++ b/package.json @@ -1,39 +1,63 @@ { - "private": true, "name": "wled-manager", + "version": "0.2.0", "description": "WLED Manager by Blade", - "version": "0.1.0", - "author": "YeonV aka Blade ", - "main": "app/background.js", + "main": "./out/main/index.js", + "author": { + "name": "YeonV aka Blade ", + "email": "dev@yeonv.com" + }, + "homepage": "https://electron-vite.org", + "license": "MIT", "scripts": { - "dev": "nextron", - "build": "nextron build", - "dist": "electron-builder -w zip -w portable -w nsis", - "postinstall": "electron-builder install-app-deps" + "format": "prettier --write .", + "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix", + "typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", + "typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false", + "typecheck": "npm run typecheck:node && npm run typecheck:web", + "start": "electron-vite preview", + "dev": "electron-vite dev", + "build": "npm run typecheck && electron-vite build", + "postinstall": "electron-builder install-app-deps", + "build:win": "npm run build && electron-builder --win --config", + "build:mac": "electron-vite build && electron-builder --mac --config", + "build:linux": "electron-vite build && electron-builder --linux --config" }, "dependencies": { - "@types/bonjour": "^3.5.9", + "@electron-toolkit/preload": "^2.0.0", + "@electron-toolkit/utils": "^2.0.0", + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@fontsource/roboto": "^5.0.8", + "@mui/icons-material": "^5.14.16", + "@mui/material": "^5.14.16", + "@mui/styles": "^5.14.16", "bonjour": "^3.5.0", - "custom-electron-titlebar": "^3.2.7", + "custom-electron-titlebar": "^4.2.7", "dgram-as-promised": "^5.0.1", - "electron-serve": "^1.1.0", - "electron-store": "^8.0.0", - "react-colorful": "^5.5.0", - "react-gcolor-picker": "^1.2.4", - "zustand": "^3.5.11" + "react-colorful": "^5.6.1", + "react-gcolor-picker": "^1.3.1", + "react-router-dom": "^6.18.0", + "zustand": "^4.4.6" }, "devDependencies": { - "@material-ui/core": "^4.12.2", - "@material-ui/icons": "^4.11.2", - "@types/node": "^14.14.31", - "@types/react": "^16.9.49", - "electron": "^13.1.7", - "electron-builder": "^22.11.7", - "electron-devtools-installer": "^3.2.0", - "next": "^11.0.1", - "nextron": "^7.0.0", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "typescript": "^4.3.5" + "@electron-toolkit/eslint-config-prettier": "^1.0.1", + "@electron-toolkit/eslint-config-ts": "^1.0.0", + "@electron-toolkit/tsconfig": "^1.0.1", + "@types/bonjour": "^3.5.12", + "@types/node": "^20.8.10", + "@types/react": "^18.2.35", + "@types/react-dom": "^18.2.14", + "@vitejs/plugin-react": "^4.0.4", + "electron": "^27.0.3", + "electron-builder": "^24.6.3", + "electron-vite": "^1.0.27", + "eslint": "^8.53.0", + "eslint-plugin-react": "^7.33.2", + "prettier": "^3.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "typescript": "^5.1.6", + "vite": "^4.4.9" } } diff --git a/public/index.html b/public/index.html deleted file mode 100644 index 1791ec0..0000000 --- a/public/index.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - React App - - - - -
- - - - - \ No newline at end of file diff --git a/public/logo192.png b/public/logo192.png deleted file mode 100644 index 2b7adf1..0000000 Binary files a/public/logo192.png and /dev/null differ diff --git a/public/manifest.json b/public/manifest.json deleted file mode 100644 index 080d6c7..0000000 --- a/public/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/public/robots.txt b/public/robots.txt deleted file mode 100644 index e9e57dc..0000000 --- a/public/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/renderer/components/AudioContainer.jsx b/renderer/components/AudioContainer.jsx deleted file mode 100644 index e7b8a73..0000000 --- a/renderer/components/AudioContainer.jsx +++ /dev/null @@ -1,212 +0,0 @@ -import React, { useState, useRef, useEffect } from 'react'; -import Visualizer from './Visualizer'; -// const aubio = require('../../../aubiojs') -// import Essentia from './essentia.js-core.es.js'; -// // import essentia-wasm-module -// import { EssentiaWASM } from './essentia-wasm.es.js'; - -// create essentia object with all the methods to run various algorithms -// by loading the wasm back-end. -// here, `EssentiaModule` is an emscripten module object imported to the global namespace - - - -const AudioDataContainer = ({ audioDeviceId, fft, bandCount, drawerBottomHeight }) => { - const [frequencyBandArray] = useState([...Array(bandCount).keys()]); - const audioData = useRef(null); - const audioContext = useRef(new AudioContext()); - const theStream = useRef(null); - const theGain = useRef(null); - - // const theTempo = useRef(null); - // const theAubio = useRef(null); - - const initializeAudioAnalyser = () => { - getMedia(audioDeviceId).then((stream) => { - stream.current = stream; - if (!audioContext.current || audioContext.current.state === 'closed') { - return; - } - const source = audioContext.current.createMediaStreamSource(stream); - const analyser = audioContext.current.createAnalyser(); - - // const audioBuffer = audioContext.current.createBuffer(1, 1024, audioContext.current.sampleRate); - // let esPkg = require('essentia.js'); - - // let essentia = new esPkg.Essentia(esPkg.EssentiaWASM); - // const inputSignalVector = essentia.arrayToVector(audioBuffer.getChannelData(0)); - // let outputRG = essentia.ReplayGain(inputSignalVector, // input - // 44100); // sampleRate (parameter optional) - - - // console.log(outputRG.replayGain); - - // const scriptProcessor = audioContext.current.createScriptProcessor( - // 1024, - // 1, - // 1 - // ); - // const buf = audioContext.current.createBuffer(1, 1024, audioCtx.sampleRate); - // if (theTempo.current) { - // if (theTempo.current.do(buf.getChannelData(0))) { - // console.log('confidence', theTempo.current.getConfidence()); - // beat(theTempo.current.getBpm()); - // } - // } - // console.log("WTF0", scriptProcessor) - // scriptProcessor.onaudioprocess = (event) => { - // console.log("WTF", event) - // if (theTempo.current) { - // if (theTempo.current.do(event.inputBuffer.getChannelData(0))) { - // console.log('confidence', theTempo.current.getConfidence()); - // beat(theTempo.current.getBpm()); - // } - // } - // scriptProcessor.addEventListener('audioprocess', function (event) { - // console.log("WTF", event) - // if (theTempo.current) { - // if (theTempo.current.do(event.inputBuffer.getChannelData(0))) { - // console.log('confidence', theTempo.current.getConfidence()); - // beat(theTempo.current.getBpm()); - // } - // } - // } - // ); - // } - - - analyser.fftSize = fft; - const gain = audioContext.current.createGain(); - theGain.current = gain.gain; - source.connect(gain); - gain.connect(analyser); - // source.connect(scriptProcessor); - audioData.current = analyser; - }); - }; - - const getFrequencyData = (styleAdjuster) => { - if (!audioData.current) { - return; - } - // const worker = new Worker('./audioWorker.js', { type: 'module' }); - // await worker.postMessage(audioData.current); - - // worker.onmessage = ({ data }) => { - // console.log(`page got message: ${data}`); - // }; - const bufferLength = audioData.current.frequencyBinCount; - const amplitudeArray = new Uint8Array(bufferLength); - - audioData.current.getByteFrequencyData(amplitudeArray); - styleAdjuster(amplitudeArray); - }; - - const getMedia = async (clientDevice) => { - const ad = await navigator.mediaDevices - .enumerateDevices() - .then((devices) => - !!( - clientDevice !== null && - devices.find((d) => d.deviceId === clientDevice) - ) - ? clientDevice - : null - ); - if (ad) { - try { - return await navigator.mediaDevices.getUserMedia({ - audio: { deviceId: { exact: ad } }, - video: false, - }); - } catch (err) { - console.log('Error:', err); - } - } else { - try { - return await navigator.mediaDevices.getUserMedia({ - audio: true, - video: false, - }); - } catch (err) { - console.log('Error:', err); - } - } - }; - - // useEffect(() => { - // const init = async () => { - // const { Tempo } = await aubio(); - // const tempo = new Tempo(4096, 1024, audioContext.current.sampleRate); - // theTempo.current = tempo; - // }; - // init(); - // }, []); - - // useEffect(() => { - // console.log("YZ2", theTempo.current) - // const audioBuffer = audioContext.current.createBuffer(1, 1024, audioContext.current.sampleRate); - // theTempo.current && theTempo.current.do(audioBuffer); - // const bpm = theTempo.current && theTempo.current.getBpm(); - // console.log(bpm) - // }, [theTempo.current, audioContext.current]); - return ( -
- { - if ( - audioContext.current && - audioContext.current.state === 'running' - ) { - audioContext.current.state !== 'closed' && - theStream.current && - theStream.current.getTracks().forEach((track) => track.stop()); - - audioContext.current.state !== 'closed' && - audioContext.current.suspend(); - - audioContext.current.state !== 'closed' && - audioContext.current.resume(); - audioData.current = null; - } - }} - stop={() => { - if ( - audioContext.current && - audioContext.current.state === 'running' - ) { - if (theGain.current) { - theGain.current.value = 0; - } - setTimeout(() => { - if (audioContext.current) { - audioContext.current.state !== 'closed' && - theStream.current && - theStream.current - .getTracks() - .forEach((track) => track.stop()); - - audioContext.current.state !== 'closed' && - audioContext.current.suspend(); - - audioContext.current.state !== 'closed' && - audioContext.current.resume(); - - audioData.current = null; - } - }, 800); - } - }} - /> -
- ); -}; - -export default AudioDataContainer; diff --git a/renderer/components/ColorPicker.jsx b/renderer/components/ColorPicker.jsx deleted file mode 100644 index c0eb16c..0000000 --- a/renderer/components/ColorPicker.jsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useCallback, useRef, useState } from 'react'; -import { RgbColorPicker } from 'react-colorful'; -import useClickOutside from '../lib/useClickOutside'; -import ReactGPicker from 'react-gcolor-picker'; -import useStyles from '../styles/GradientPicker.styles'; - -const ColorPicker = ({ color, onChange, disabled, label, gradient,setDrawerBottomHeight }) => { - const popover = useRef(); - const classes = useStyles() - const [isOpen, toggle] = useState(false); - - const [gcolors, setGcolors] = useState([ - "linear-gradient(90deg, rgb(255, 0, 0) 0%, rgb(255, 120, 0) 14%, rgb(255, 200, 0) 28%, rgb(0, 255, 0) 42%, rgb(0, 199, 140) 56%, rgb(0, 0, 255) 70%, rgb(128, 0, 128) 84%, rgb(255, 0, 178) 98%)", - "linear-gradient(90deg, rgb(255, 0, 0) 0%, rgb(255, 0, 178) 50%, rgb(0, 0, 255) 100%)", - "linear-gradient(90deg, rgb(0, 0, 255) 0%, rgb(128, 0, 128) 25%, rgb(255, 0, 0) 50%, rgb(255, 40, 0) 75%, rgb(255, 200, 0) 100%)", - "linear-gradient(90deg, rgb(0, 255, 255) 0%, rgb(0, 0, 255) 100%)", - "linear-gradient(90deg, rgb(128, 0, 128) 0%, rgb(0, 0, 255) 25%, rgb(0, 128, 128) 50%, rgb(0, 255, 0) 75%, rgb(255, 200, 0) 100%)", - "linear-gradient(90deg, rgb(0, 255, 0) 0%, rgb(34, 139, 34) 50%, rgb(255, 120, 0) 100%)", - "linear-gradient(90deg, rgb(255, 0, 178) 0%, rgb(255, 40, 0) 50%, rgb(255, 200, 0) 100%)", - "linear-gradient(90deg, rgb(0, 199, 140) 0%, rgb(0, 255, 50) 100%)", - "linear-gradient(90deg, rgb(0, 0, 255) 0%, rgb(0, 255, 255) 33%, rgb(128, 0, 128) 66%, rgb(255, 0, 178) 99%)", - "linear-gradient(90deg, rgb(0, 0, 128) 0%, rgb(255, 120, 0) 50%, rgb(255, 0, 0) 100%)", - "linear-gradient(90deg, rgb(255, 40, 0) 0%, rgb(128, 0, 128) 33%, rgb(0, 199, 140) 66%, rgb(0, 255, 0) 99%)", - "linear-gradient(90deg, rgb(255, 40, 0) 0%, rgb(255, 0, 0) 100%)", - "linear-gradient(90deg, rgb(0, 255, 0) 0%, rgb(255, 200, 0) 25%, rgb(255, 120, 0) 50%, rgb(255, 40, 0) 75%, rgb(255, 0, 0) 100%)" -]) - - const close = useCallback(() => toggle(false), []); - useClickOutside(popover, close); - - return ( -
-
{ - setDrawerBottomHeight(800) - toggle(true) - }}> - - {label} - -
- {isOpen && ( -
- {gradient ? ( - - ) : ( - - )} -
- )} -
- ); -}; -export default ColorPicker; diff --git a/renderer/components/Link.tsx b/renderer/components/Link.tsx deleted file mode 100644 index 6d5f1b0..0000000 --- a/renderer/components/Link.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import clsx from 'clsx'; -import { useRouter } from 'next/router'; -import NextLink, { LinkProps as NextLinkProps } from 'next/link'; -import MuiLink, { LinkProps as MuiLinkProps } from '@material-ui/core/Link'; - -type NextComposedProps = React.AnchorHTMLAttributes & NextLinkProps; - -const NextComposed = React.forwardRef(function NextComposed(props: NextComposedProps, ref: React.Ref) { - const { as, href, ...other } = props; - - return ( - - - - ); -}); - -interface LinkPropsBase { - activeClassName?: string; - innerRef?: React.Ref; - naked?: boolean; -} - -type LinkProps = LinkPropsBase & NextComposedProps & Omit; - -function Link(props: LinkProps) { - const { - href, - activeClassName = 'active', - className: classNameProps, - prefetch, - innerRef, - naked, - ...other - } = props; - - const router = useRouter(); - const pathname = href; - // const pathname = typeof href === 'string' ? href : href.pathname; - const className = clsx(classNameProps, { - [activeClassName]: router.pathname === pathname && activeClassName, - }); - - if (naked) { - return ; - } - - return ( - - ); -} - -export default React.forwardRef((props, ref) => ); diff --git a/renderer/components/MenuTemplate.js b/renderer/components/MenuTemplate.js deleted file mode 100644 index 7bd70c5..0000000 --- a/renderer/components/MenuTemplate.js +++ /dev/null @@ -1,161 +0,0 @@ - - -export const MenuFile = { - label: 'File', - submenu: [ - { - label: 'Exit', - click: () => console.log('exit'), - }, - ], -} - - -export const MenuTools = { - label: 'Tools', - submenu: [ - { - label: 'Testing checkbox', - type: 'checkbox', - checked: true, - }, - { - type: 'separator', - }, - { - label: 'Testing submenu', - submenu: [ - { - label: 'Submenu &item 1', - accelerator: 'Ctrl+T', - }, - ], - }, - { - type: 'separator', - }, - { - label: 'Settings', - click: () => console.log('Click on settings'), - }, - ], -} - - - -export const template = () => { - const isMac = process.platform === 'darwin' - - return ([ - // { role: 'appMenu' } - ...(isMac ? [{ - label: "WLED Manager", - submenu: [ - { role: 'about' }, - { type: 'separator' }, - { role: 'services' }, - { type: 'separator' }, - { role: 'hide' }, - { role: 'hideOthers' }, - { role: 'unhide' }, - { type: 'separator' }, - { role: 'quit' } - ] - }] : []), - // { role: 'fileMenu' } - { - label: 'File', - submenu: [ - isMac ? { role: 'close' } : { role: 'quit' } - ] - }, - // { role: 'editMenu' } - { - label: 'Edit', - submenu: [ - { role: 'undo' }, - { role: 'redo' }, - { type: 'separator' }, - { role: 'cut' }, - { role: 'copy' }, - { role: 'paste' }, - ...(isMac ? [ - { role: 'pasteAndMatchStyle' }, - { role: 'delete' }, - { role: 'selectAll' }, - { type: 'separator' }, - { - label: 'Speech', - submenu: [ - { role: 'startSpeaking' }, - { role: 'stopSpeaking' } - ] - } - ] : [ - { role: 'delete' }, - { type: 'separator' }, - { role: 'selectAll' } - ]) - ] - }, - // { role: 'viewMenu' } - { - label: 'View', - submenu: [ - { role: 'reload' }, - { role: 'forceReload' }, - { role: 'toggleDevTools' }, - { type: 'separator' }, - { role: 'resetZoom' }, - { role: 'zoomIn' }, - { role: 'zoomOut' }, - { type: 'separator' }, - { role: 'togglefullscreen' } - ] - }, - // { role: 'windowMenu' } - { - label: 'Window', - submenu: [ - { role: 'minimize' }, - { role: 'zoom' }, - ...(isMac ? [ - { type: 'separator' }, - { role: 'front' }, - { type: 'separator' }, - { role: 'window' } - ] : [ - { role: 'close' } - ]) - ] - }, - { - role: 'help', - submenu: [ - { - label: 'Author', - sublabel: 'YeonV', - // icon: nativeImage.createFromPath(('images/github32.png')).resize({ width: 16 }), - click: async () => { - const { shell } = require('electron') - await shell.openExternal('https://github.com/YeonV') - } - } - ] - }, - { - label: 'Tools', - submenu: [ - { - label: 'Settings', - sublabel: 'not yet', - click: async () => { - // const { shell } = require('electron') - // await shell.openExternal('https://github.com/YeonV') - alert("Coming soon...") - } - } - ] - } - ]) -} diff --git a/renderer/components/Toggle.jsx b/renderer/components/Toggle.jsx deleted file mode 100644 index 3015722..0000000 --- a/renderer/components/Toggle.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react' - -const Toggle = ({ value, setValue, label }) => { - return ( -
setValue(!value)} - > - {label} -
- ) -} - -export default Toggle diff --git a/renderer/components/Visualizer.jsx b/renderer/components/Visualizer.jsx deleted file mode 100644 index 556fd58..0000000 --- a/renderer/components/Visualizer.jsx +++ /dev/null @@ -1,452 +0,0 @@ -import React, { useRef, useState, useEffect } from 'react'; -import { useTheme, styled } from '@material-ui/core/styles'; -import { ipcRenderer } from 'electron'; -import { PlayArrow, Stop } from '@material-ui/icons'; -import { Button, MenuItem, TextField, Typography, Paper, Box, Slider } from '@material-ui/core'; -import ColorPicker from './ColorPicker'; -import useStore from '../store/store'; -import Effect, { effects } from '../effects/effects'; -import Toggle from './Toggle'; -import useVisualizerStyles from './Visualizer.styles'; - - -const YZslider = styled(Slider)({ - color: 'white', - height: 8, - '& .MuiSlider-track': { - border: 'none', - width: 0, - }, - '& .MuiSlider-rail': { - border: 'none', - width: 0, - }, - '& .MuiSlider-thumb': { - height: 20, - width: 20, - marginLeft: -9, - backgroundColor: '#fff0', - '&:focus, &:hover, &.Mui-active, &.Mui-focusVisible': { - boxShadow: 'inherit', - }, - '&:before': { - content: '"⬍"', - position: 'absolute', - left: 0, - top: -8, - width: 20, - height: 20, - fontSize: 30, - color: '#444', - }, - '&:after': { - top: 12, - left: -20, - content: "", - position: 'absolute', - height: 10, - width: '100vw', - borderTop: '1px dashed #444', - borderRadius: 0, - right: 'unset', - bottom: 'unset', - }, - '&:hover': { - '&:after': { - borderColor: '#aaa' - }, - '&:before': { - color: '#aaa' - } - } - }, -}); - - -export default function Visualizer({ - frequencyBandArray, - getFrequencyData, - initializeAudioAnalyser, - stop, - refresh, - audioContext, - fft, - bandCount -}) { - - const classes = useVisualizerStyles(); - const theme = useTheme(); - const amplitudeValues = useRef(null); - const timeStarted = useRef(null); - const lastShift = useRef(null); - const lastAudio = useRef(null); - - const device = useStore(state => state.device) - const devices = useStore(state => state.devices) - const audioDevice = useStore(state => state.audioDevice) - const setAudioDevice = useStore(state => state.setAudioDevice) - const audioDevices = useStore(state => state.audioDevices) - const setAudioDevices = useStore(state => state.setAudioDevices) - const color = useStore(state => state.color) - const setColor = useStore(state => state.setColor) - const bgColor = useStore(state => state.bgColor) - const setBgColor = useStore(state => state.setBgColor) - const gcolor = useStore(state => state.gcolor) - const setGcolor = useStore(state => state.setGcolor) - const setAudioSettings = useStore(state => state.setAudioSettings) - const setLeftFb = useStore(state => state.setLeftFb) - const setRightFb = useStore(state => state.setRightFb) - - const [activeFb, setActiveFb] = useState(-1) - const [activeRightFb, setActiveRightFb] = useState(-1) - const [playing, setPlaying] = useState(false) - const [flipped, setFlipped] = useState(false) - const [effect, setEffect] = useState("Power (Left FB)") - const [volume, setVolume] = useState(0) - const [innerVolume, setInnerVolume] = useState(0) - const virtualView = useStore(state => state.virtualView) - const virtual = useStore(state => state.virtual) - const setDrawerBottomHeight = useStore(state => state.setDrawerBottomHeight) - - - const settingColor = (clr) => { - setColor(clr) - if (playing) { - refresh() - } - } - const settingBgColor = (clr) => { - setBgColor(clr) - if (playing) { - refresh() - } - } - const settingGcolor = (clr) => { - setGcolor(clr) - if (playing) { - console.log("refreshing") - handleStopButtonClick() - } - - } - - const settingFlipped = (flp) => { - setFlipped(flp) - if (playing) { - refresh() - } - } - const settingActiveFb = (act) => { - setActiveFb(act) - if (playing) { - refresh() - } - } - const settingActiveRightFb = (act) => { - setActiveRightFb(act) - if (playing) { - refresh() - } - } - const settingVolume = () => { - setInnerVolume(volume) - if (playing) { - refresh() - } - } - - function adjustFreqBandStyle(newAmplitudeData) { - if (audioContext.state === 'closed') { - cancelAnimationFrame(runSpectrum) - return - } - amplitudeValues.current = newAmplitudeData; - if (frequencyBandArray.length > 0) { - let domElements = frequencyBandArray.map((num) => - document.getElementById(num)) - if (domElements.length > 0) { - for (let i = 0; i < frequencyBandArray.length; i++) { - let num = frequencyBandArray[i] - if (domElements[num]) { - domElements[num].style.backgroundColor = `rgb(${color.r}, ${color.g}, ${color.b})` - domElements[num].style.height = `${amplitudeValues.current[num]}px` - } - } - if (activeFb > -1) { - const ledDataPrefix = [2, 1]; - const ledData = Effect({ - type: effect, - config: { - ampValues: amplitudeValues.current, - pixel_count: virtualView ? virtual.pixel_count : device.pixel_count, - color, - bgColor, - gcolor, - activeFb, - activeRightFb, - volume: volume, - timeStarted: timeStarted, - lastShift, - lastAudio - } - }) - // console.log(ledData) - // const header = "VER_YZ" - // const myVals = "00000000000000000000000000000000" - // const sampleAvc = "0001" - // const sample = "0001" - // const sampleAvg = "0001" - // const samplePeak = "1" - // const fftResult = "0000000000000000" - // const FFT_Magnitude = "00000000" - // const FFT_MajorPeak = "00000000" - - // ledData && ledData.length > 1 && ipcRenderer.send('UDPSR', [{ ip: device.ip }, - // `${header}${myVals}${sampleAvc}${sample}${sampleAvg}${samplePeak}${fftResult}${FFT_Magnitude}${FFT_MajorPeak}`]) - - if (virtualView) { - virtual.seg && virtual.seg.length && virtual.seg.map(s=>{ - ledData && ledData.length > 1 && ipcRenderer.send('UDP', [{ ip: devices.find(d=>d.name === s.device).ip }, flipped - ? [...ledDataPrefix, ...ledData.reverse().flat()].splice(s.seg[0]*3, s.seg[1]*3) - : [...ledDataPrefix, ...ledData.flat()].splice(s.seg[0]*3, s.seg[1]*3)]) - }) - } else { - ledData && ledData.length > 1 && ipcRenderer.send('UDP', [{ ip: device.ip }, flipped - ? [...ledDataPrefix, ...ledData.reverse().flat()] - : [...ledDataPrefix, ...ledData.flat()]]) - } - - // ledData && ledData.length > 1 && ipcRenderer.send('UDP', [{ ip: device.ip }, (amplitudeValues.current[activeFb] - volume * 2.55) > 0 - // ? [...ledDataPrefix, ...tmp.reverse().flat()] - // : [...ledDataPrefix, ...tmp.flat()]]) - } - } - } - }; - - function runSpectrum() { - if (audioContext.state === 'running') { - getFrequencyData(adjustFreqBandStyle) - requestAnimationFrame(runSpectrum) - } - } - - function handleStartButtonClick() { - ipcRenderer.send('UDP-start') - // ipcRenderer.send('UDPSR-start') - timeStarted.current = performance.now(); - setPlaying(true) - initializeAudioAnalyser() - requestAnimationFrame(runSpectrum) - } - - function handleStopButtonClick() { - setPlaying(false) - ipcRenderer.send('UDP-stop') - // console.log(performance.now() - timeStarted.current) - // ipcRenderer.send('UDPSR-stop') - if (frequencyBandArray.length > 0) { - let domElements = frequencyBandArray.map((num) => - document.getElementById(num)) - for (let i = 0; i < frequencyBandArray.length; i++) { - let num = frequencyBandArray[i] - domElements[num].style.backgroundColor = theme.palette.background.paper - } - } - stop(800) - } - - function handleFreqBandClick(num) { - if (activeFb === num) { - settingActiveFb(-1) - return - } - if (activeRightFb > -1) { - if (num > activeRightFb) { - settingActiveRightFb(-1) - settingActiveFb(num) - return - } - settingActiveFb(num) - } else { - settingActiveFb(num) - } - } - - function handleFreqBandRightClick(num) { - if (activeRightFb === num) { - settingActiveRightFb(-1) - return - } - if (activeFb > -1) { - if (activeFb > num) { - settingActiveRightFb(num) - settingActiveFb(-1) - return - } - settingActiveRightFb(num) - } else { - settingActiveRightFb(num) - } - } - - function preventHorizontalKeyboardNavigation(event) { - if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { - event.preventDefault(); - } - } - - useEffect(() => { - if (playing) { - setTimeout(() => { - initializeAudioAnalyser() - requestAnimationFrame(runSpectrum) - }, 100) - } - setLeftFb(activeFb) - setRightFb(activeRightFb) - }, [color, bgColor, flipped, innerVolume, activeFb, activeRightFb]) - - useEffect(() => { - handleStopButtonClick() - }, [fft, bandCount]) - - useEffect(() => { - setAudioSettings({ - sampleRate: audioContext.sampleRate - }) - navigator.mediaDevices.enumerateDevices() - .then(function (adevices) { - setAudioDevices(adevices) - }) - .catch(function (err) { - console.log(err.name + ": " + err.message); - }) - }, []) - - return ( -
-
-
- - - { setAudioDevice(e.target.value) }} - > - {audioDevices.filter(cd => cd.kind === 'audioinput').map((d, i) => - - {d.label} - - )} - - { - if (e.target.value === 'BladeWave (Range)') { - setBgColor({r: 0, g: 0, b:0}) - } - if (e.target.value === 'BladePower (Left FB)') { - setActiveRightFb(-1) - } - setEffect(e.target.value) - }} - > - {effects.map((d, i) => - - {d} - - )} - -
-
- {effect.indexOf("radient") === -1 && - } - {effect !== "BladeWave (Range)" && effect.indexOf("radient") === -1 && - - } - {effect.indexOf("radient") > -1 && } - -
-
- - - setVolume(v)} - onChangeCommitted={settingVolume} - min={0} - max={100} - aria-label="Temperature" - onKeyDown={preventHorizontalKeyboardNavigation} - /> - - {(activeFb === -1 && effect === 'BladePower') && - - Please select a band at the bottom - - } -
-1 || activeRightFb > -1) ? 'selection-active' : ''}`}> - {frequencyBandArray.map((num) => -
- -1 && activeRightFb > -1) - ? ((activeFb <= num && activeRightFb >= num) - ? 'selected' - : '') - : (activeFb === num || activeRightFb === num) - ? 'selected' - : ''}`} - style={{ background: `rgb(${color.r}, ${color.g}, ${color.b})`, padding: `calc(100vw / ${(frequencyBandArray.length) * 4} )` }} - elevation={4} - id={num} - key={num} - onClick={() => handleFreqBandClick(num)} - onContextMenu={() => handleFreqBandRightClick(num)} - /> -
-1 && activeRightFb > -1) - ? ((activeFb <= num && activeRightFb >= num) - ? 'selected' - : '') - : (activeFb === num || activeRightFb === num) - ? 'selected' - : ''}`} - style={{ backgroundColor: `rgb(${bgColor.r}, ${bgColor.g}, ${bgColor.b})`, padding: `calc(100vw / ${frequencyBandArray.length * 4} )` }} - onClick={() => handleFreqBandClick(num)} - onContextMenu={() => handleFreqBandRightClick(num)} - /> -
- )} -
-
- - ); - -} diff --git a/renderer/components/Visualizer.styles.js b/renderer/components/Visualizer.styles.js deleted file mode 100644 index 2ba1cd3..0000000 --- a/renderer/components/Visualizer.styles.js +++ /dev/null @@ -1,65 +0,0 @@ -import { makeStyles } from '@material-ui/core/styles'; - -const useVisualizerStyles = makeStyles(theme => ({ - flexContainer: { - display: 'flex', - justifyContent: 'center', - paddingTop: '275px' - }, - frequencyBands: { - padding: 'calc(100vw / 260)', - flexShrink: 1, - margin: 'calc(100vw / 500)', - transform: 'rotateX(180deg)', - transformOrigin: 'top', - border: '1px solid transparent', - cursor: 'pointer', - zIndex: 10, - '.selection-active &': { - opacity: 0.3, - }, - '&:hover': { - borderColor: '#999', - opacity: 1, - }, - '&.selected': { - borderColor: '#bbb', - opacity: 1, - }, - }, - frequencyBandsBg: { - position: 'absolute', - top: 0, - height: '255px', - zIndex: -1, - - flexShrink: 1, - margin: 'calc(100vw / 500)', - transform: 'rotateX(180deg)', - transformOrigin: 'top', - border: '1px solid transparent', - cursor: 'pointer', - '.selection-active &': { - opacity: 0.3, - }, - '&:hover': { - opacity: 1, - }, - '&.selected': { - opacity: 1, - }, - '&&:not(.selected)': { - backgroundColor: 'transparent !important' - }, - }, - effectNote: { - position: 'absolute', - left: '50%', - top: '50%', - transform: 'translateX(-50%)', - textAlign: 'center', - color: '#444' - } -})); - -export default useVisualizerStyles \ No newline at end of file diff --git a/renderer/components/audioWorker.js b/renderer/components/audioWorker.js deleted file mode 100644 index c15d0f1..0000000 --- a/renderer/components/audioWorker.js +++ /dev/null @@ -1,19 +0,0 @@ -const essentia = require('essentia.js'); - -addEventListener('message', e => { - console.log(e.data); - - let audio = e.data.channelData[0]; - let inputSignalVector = essentia.arrayToVector(audio); - let key = essentia.KeyExtractor(inputSignalVector); - let bpm = essentia.RhythmExtractor(inputSignalVector); - - let data = { - bpm : bpm.bpm, - key : key.key, - scale : key.scale - } - - postMessage(data); - -}); \ No newline at end of file diff --git a/renderer/effects/effects.js b/renderer/effects/effects.js deleted file mode 100644 index b385566..0000000 --- a/renderer/effects/effects.js +++ /dev/null @@ -1,47 +0,0 @@ -import GradientRolling from "./gradientRolling"; -import GradientAudio from "./gradientsAudio"; -import GradientsAudioInv from "./gradientsAudioInv"; -import GradientStatic from "./gradientStatic"; -import Power from "./power"; -import Wavelength from "./wavelength"; -import WavelengthBg from "./wavelengthBg"; - -export const effects = [ - 'Power (Left FB)', - 'Wavelength (Range)', - 'WavelengthBg (Range)', - 'GradientStatic', - 'GradientRolling', - 'GradientAudio', - 'GradientsAudioInv', -] - -const Effect = ({ type, config }) => { - switch (type) { - case 'Power (Left FB)': - return Power(config) - - case 'Wavelength (Range)': - return Wavelength(config) - - case 'WavelengthBg (Range)': - return WavelengthBg(config) - - case 'GradientStatic': - return GradientStatic(config) - - case 'GradientRolling': - return GradientRolling(config) - - case 'GradientAudio': - return GradientAudio(config) - - case 'GradientsAudioInv': - return GradientsAudioInv(config) - - default: - return Power(config) - } -} - -export default Effect \ No newline at end of file diff --git a/renderer/effects/gradientRolling.js b/renderer/effects/gradientRolling.js deleted file mode 100644 index 8b7ab53..0000000 --- a/renderer/effects/gradientRolling.js +++ /dev/null @@ -1,12 +0,0 @@ -import { getMultipleGradientSteps } from "./utils" - -const GradientRolling = ({ ampValues, pixel_count, color, bgColor, activeFb, volume, timeStarted, gcolor }) => { - const tmp = getMultipleGradientSteps(gcolor.match(/rgb\([^()]*\)|#\w+/g).map(c=>c.match(/\d+/g)), pixel_count) - let speed = 8 - - const sliceA = tmp.slice(0,parseInt(((performance.now() - timeStarted.current)/speed) )% pixel_count) - const sliceB = tmp.slice(parseInt(((performance.now() - timeStarted.current)/speed) )% pixel_count) - return [...sliceB, ...sliceA] -} - -export default GradientRolling diff --git a/renderer/effects/gradientStatic.js b/renderer/effects/gradientStatic.js deleted file mode 100644 index e052128..0000000 --- a/renderer/effects/gradientStatic.js +++ /dev/null @@ -1,7 +0,0 @@ -import { getMultipleGradientSteps } from "./utils" - -const GradientStatic = ({ ampValues, pixel_count, color, bgColor, activeFb, volume, timeStarted, gcolor }) => { - getMultipleGradientSteps(gcolor.match(/rgb\([^()]*\)|#\w+/g).map(c=>c.match(/\d+/g)), pixel_count) -} - -export default GradientStatic diff --git a/renderer/effects/gradientsAudio.js b/renderer/effects/gradientsAudio.js deleted file mode 100644 index 477eccc..0000000 --- a/renderer/effects/gradientsAudio.js +++ /dev/null @@ -1,29 +0,0 @@ -import { getMultipleGradientSteps } from "./utils" - -let shift = 0; - -export const shifting = (pixel_count) => { - if (shift >= pixel_count) { - shift = 0; - } else { - shift++ - } -} - -const GradientAudio = ({ ampValues, pixel_count, color, bgColor, activeFb, volume, timeStarted, gcolor, lastShift, lastAudio }) => { - let tmp = getMultipleGradientSteps(gcolor.match(/rgb\([^()]*\)|#\w+/g).map(c=>c.match(/\d+/g)), pixel_count) - let audio = (ampValues[activeFb] - volume * 2.55) > 0 - let speed = audio ? 0 : 5 - - if (performance.now() - timeStarted.current >= 16+speed*9.84) { - shifting(pixel_count) - timeStarted.current = performance.now() - } - - const sliceA = tmp.slice(0,shift) - const sliceB = tmp.slice(shift) - - return [...sliceB, ...sliceA] -} - -export default GradientAudio \ No newline at end of file diff --git a/renderer/effects/gradientsAudioInv.js b/renderer/effects/gradientsAudioInv.js deleted file mode 100644 index d1ece0b..0000000 --- a/renderer/effects/gradientsAudioInv.js +++ /dev/null @@ -1,29 +0,0 @@ -import { getMultipleGradientStepsInverted } from "./utils" - -let shift = 0; - -export const shifting = (pixel_count) => { - if (shift >= pixel_count) { - shift = 0; - } else { - shift++ - } -} - -const GradientAudioInv = ({ ampValues, pixel_count, color, bgColor, activeFb, volume, timeStarted, gcolor, lastShift, lastAudio }) => { - let tmp = getMultipleGradientStepsInverted(gcolor.match(/rgb\([^()]*\)|#\w+/g).map(c=>c.match(/\d+/g)), pixel_count) - let audio = (ampValues[activeFb] - volume * 2.55) > 0 - let speed = audio ? 0 : 5 - - if (performance.now() - timeStarted.current >= 16+speed*9.84) { - shifting(pixel_count) - timeStarted.current = performance.now() - } - - const sliceA = tmp.slice(0,shift) - const sliceB = tmp.slice(shift) - - return [...sliceB, ...sliceA] -} - -export default GradientAudioInv \ No newline at end of file diff --git a/renderer/effects/power.js b/renderer/effects/power.js deleted file mode 100644 index 36cf15d..0000000 --- a/renderer/effects/power.js +++ /dev/null @@ -1,12 +0,0 @@ -const Power = ({ ampValues, pixel_count, color, bgColor, activeFb, volume }) => - activeFb > -1 - ? Array(pixel_count) - .fill([color.r, color.g, color.b]) - .fill( - [bgColor.r, bgColor.g, bgColor.b], - (ampValues[activeFb] - volume * 2.55) > 0 - ? parseInt(pixel_count * ((ampValues[activeFb] - volume * 2.55) / 255)) - : 0) - : null - -export default Power \ No newline at end of file diff --git a/renderer/effects/utils.js b/renderer/effects/utils.js deleted file mode 100644 index 0d1c85d..0000000 --- a/renderer/effects/utils.js +++ /dev/null @@ -1,43 +0,0 @@ -export const getGradientSteps = (colorStart,colorEnd,colorCount) => { - let alpha = 0.0; - const color = []; - for (let i = 0; i < colorCount; i++) { - var c = []; - alpha += (1.0/colorCount); - c[0] = parseInt(colorStart[0] * alpha + (1 - alpha) * colorEnd[0]); - c[1] = parseInt(colorStart[1] * alpha + (1 - alpha) * colorEnd[1]); - c[2] = parseInt(colorStart[2] * alpha + (1 - alpha) * colorEnd[2]); - color.push(c); - } - return color; -} - -export const getMultipleGradientSteps = (colors, count) => { - const output = [] - for (let i = 0; i < colors.length - 2; i++) { - const gradient = getGradientSteps( - colors[i+1], - colors[i], - i === colors.length - 1 - ? count - ((colors.length - 2) * Math.floor(count / (colors.length - 1))) - : Math.floor(count / (colors.length - 1)) - ) - output.push(gradient) - } - return output.flat() -} - -export const getMultipleGradientStepsInverted = (colors, count) => { - const output = [] - for (let i = 0; i < colors.length - 2; i++) { - const gradient = getGradientSteps( - colors[i], - colors[i+1], - i === colors.length - 1 - ? count - ((colors.length - 2) * Math.floor(count / (colors.length - 1))) - : Math.floor(count / (colors.length - 1)) - ) - output.push(gradient) - } - return output.flat() -} \ No newline at end of file diff --git a/renderer/effects/wavelength.js b/renderer/effects/wavelength.js deleted file mode 100644 index 8362849..0000000 --- a/renderer/effects/wavelength.js +++ /dev/null @@ -1,7 +0,0 @@ -const Wavelength = ({ ampValues, pixel_count, color, bgColor, activeFb, activeRightFb, volume }) => - [...Array(pixel_count).keys()].map(v => [ - ((ampValues.slice(activeFb, activeRightFb + 1)[v] - volume * 2.55) / 255) * color.r, - ((ampValues.slice(activeFb, activeRightFb + 1)[v] - volume * 2.55) / 255) * color.g, - ((ampValues.slice(activeFb, activeRightFb + 1)[v] - volume * 2.55) / 255) * color.b]) - -export default Wavelength \ No newline at end of file diff --git a/renderer/effects/wavelengthBg.js b/renderer/effects/wavelengthBg.js deleted file mode 100644 index ab2d5b5..0000000 --- a/renderer/effects/wavelengthBg.js +++ /dev/null @@ -1,7 +0,0 @@ -const WavelengthBg = ({ ampValues, pixel_count, color, bgColor, activeFb, activeRightFb, volume }) => - [...Array(pixel_count).keys()].map(v => [ - (((ampValues.slice(activeFb, activeRightFb + 1)[v] - volume * 2.55) / 255) * color.r + bgColor.r) / 2, - (((ampValues.slice(activeFb, activeRightFb + 1)[v] - volume * 2.55) / 255) * color.g + bgColor.g) / 2, - (((ampValues.slice(activeFb, activeRightFb + 1)[v] - volume * 2.55) / 255) * color.b + bgColor.b) / 2]) - -export default WavelengthBg \ No newline at end of file diff --git a/renderer/lib/theme.ts b/renderer/lib/theme.ts deleted file mode 100644 index 20ac4c7..0000000 --- a/renderer/lib/theme.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createTheme } from '@material-ui/core/styles'; -import red from '@material-ui/core/colors/red'; - -export const theme = createTheme({ - palette: { - type: 'dark', - primary: { - main: '#004dff', - }, - secondary: { - main: '#800000', - }, - error: { - main: '#e40303', - }, - warning: { - main: '#ffaa00', - }, - success: { - main: '#008026', - }, - background: { - default: '#222', - }, - }, -}); diff --git a/renderer/lib/useClickOutside.js b/renderer/lib/useClickOutside.js deleted file mode 100644 index e5643c8..0000000 --- a/renderer/lib/useClickOutside.js +++ /dev/null @@ -1,35 +0,0 @@ -import { useEffect } from "react"; - -// Improved version of https://usehooks.com/useOnClickOutside/ -const useClickOutside = (ref, handler) => { - useEffect(() => { - let startedInside = false; - let startedWhenMounted = false; - - const listener = (event) => { - // Do nothing if `mousedown` or `touchstart` started inside ref element - if (startedInside || !startedWhenMounted) return; - // Do nothing if clicking ref's element or descendent elements - if (!ref.current || ref.current.contains(event.target)) return; - - handler(event); - }; - - const validateEventStart = (event) => { - startedWhenMounted = ref.current; - startedInside = ref.current && ref.current.contains(event.target); - }; - - document.addEventListener("mousedown", validateEventStart); - document.addEventListener("touchstart", validateEventStart); - document.addEventListener("click", listener); - - return () => { - document.removeEventListener("mousedown", validateEventStart); - document.removeEventListener("touchstart", validateEventStart); - document.removeEventListener("click", listener); - }; - }, [ref, handler]); -}; - -export default useClickOutside; diff --git a/renderer/next-env.d.ts b/renderer/next-env.d.ts deleted file mode 100644 index 9bc3dd4..0000000 --- a/renderer/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/renderer/next.config.js b/renderer/next.config.js deleted file mode 100644 index a046706..0000000 --- a/renderer/next.config.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - webpack: (config, { isServer }) => { - if (!isServer) { - config.target = 'electron-renderer'; - } - - return config; - }, -}; diff --git a/renderer/pages/_app.tsx b/renderer/pages/_app.tsx deleted file mode 100644 index 438c1b2..0000000 --- a/renderer/pages/_app.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import Head from 'next/head'; -import { ThemeProvider } from '@material-ui/core/styles'; -import CssBaseline from '@material-ui/core/CssBaseline'; -import { theme } from '../lib/theme'; -import type { AppProps } from 'next/app'; - -export default function App(props: AppProps) { - const { Component, pageProps } = props; - - React.useEffect(() => { - const jssStyles = document.querySelector('#jss-server-side'); - if (jssStyles) { - jssStyles.parentElement.removeChild(jssStyles); - } - }, []); - - return ( - - - - - - - - - - ); -} diff --git a/renderer/pages/_document.tsx b/renderer/pages/_document.tsx deleted file mode 100644 index 2c3957b..0000000 --- a/renderer/pages/_document.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import Document, { Html, Head, Main, NextScript } from 'next/document'; -import { ServerStyleSheets } from '@material-ui/styles'; -import { theme } from '../lib/theme'; - -export default class MyDocument extends Document { - render() { - return ( - - - - - - - -
- - - - - ); - } -} - -MyDocument.getInitialProps = async ctx => { - const sheets = new ServerStyleSheets(); - const originalRenderPage = ctx.renderPage; - - ctx.renderPage = () => - originalRenderPage({ - enhanceApp: (App) => (props) => sheets.collect(), - }); - - const initialProps = await Document.getInitialProps(ctx); - - return { - ...initialProps, - styles: [ - ...React.Children.toArray(initialProps.styles), - sheets.getStyleElement(), - ], - }; -}; diff --git a/renderer/pages/home.jsx b/renderer/pages/home.jsx deleted file mode 100644 index e3f2e41..0000000 --- a/renderer/pages/home.jsx +++ /dev/null @@ -1,219 +0,0 @@ -import React, { useEffect } from 'react'; -import Head from 'next/head'; -import { ipcRenderer } from 'electron'; -import { makeStyles, createStyles, styled } from '@material-ui/core/styles'; -import { Check, Close, Error, HourglassEmpty, PlayArrow } from '@material-ui/icons'; -import Typography from '@material-ui/core/Typography'; - -import { Box, CircularProgress, Fab, IconButton, TextField } from '@material-ui/core'; -import { useRouter } from 'next/router'; -import useStore from '../store/store'; - -const CssTextField = styled(TextField, { - shouldForwardProp: (props) => props !== "focuscolor" -})((p) => ({ - "& label.Mui-focused": { - color: p.focuscolor - }, - "& .MuiInput-underline:after": { - borderBottomColor: p.focuscolor - }, - "& .MuiFilledInput-underline:after": { - borderBottomColor: p.focuscolor - }, - "& .MuiOutlinedInput-root": { - "&.Mui-focused fieldset": { - borderColor: p.focuscolor - } - } -})); - -const useStyles = makeStyles((theme) => - createStyles({ - root: { - textAlign: 'center', - paddingTop: 0, - backgroundColor: '#222', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'space-between', - '& img': { - width: 256 - } - }, - - }) -); - -function Home() { - const classes = useStyles({}); - const router = useRouter(); - - const iframe = useStore(state => state.iframe) - const setIframe = useStore(state => state.setIframe) - const setDevice = useStore(state => state.setDevice) - - const [loading, setLoading] = React.useState(false); - const [success, setSuccess] = React.useState(false); - const [error, setError] = React.useState(false); - const [warning, setWarning] = React.useState(); - - const handleButtonClick = (newIp, zeroconf) => { - setSuccess(false) - setError(false) - setLoading(true) - fetch(`http://${newIp}/json`) - .then(r => r.json()) - .then((res) => { - if (res.info.name) { - setSuccess(true) - window && window.localStorage.setItem("wled-manager-ip", newIp) - setDevice({ - "name": res.info.name, - "type": res.info.arch === "esp8266" ? 82 : 32, - "ip": newIp, - "vid": res.info.vid, - "pixel_count": res.info.leds.count, - "seg": res.state.seg - }) - - if (zeroconf) { - window && window.localStorage.setItem("wled-manager-zeroconf", zeroconf) - setTimeout(() => { - router.push(`/yz?ip=${newIp}${zeroconf && '&zeroconf=true' || ''}`) - }, 1000) - return - } else { - fetch(`http://${newIp}/json/nodes`) - .then((r) => { - if (r.status === 501) { - setTimeout(() => { - router.push(`/yz?ip=${newIp}&singlemode=true`) - }, 5000) - return setWarning(true) - } - setTimeout(() => { - router.push(`/yz?ip=${newIp}}`) - }, 1000) - return r.json() - }) - .catch((error) => { - console.log(error) - }) - - } - - - } - }).catch((error) => { - setLoading(false) - setError(true) - window && window.localStorage.removeItem("wled-manager-ip") - }) - } - - useEffect(() => { - ipcRenderer.send('resize-me-please', [480, 800]) - }, []) - - let bonjour = null; - - useEffect(() => { - bonjour = require('bonjour')() - bonjour.find({ type: 'wled' }, (service) => { - if (service.referer && service.referer.address) { - bonjour.destroy() - setIframe(service.referer.address) - handleButtonClick(service.referer.address, true) - } - }) - return () => { - bonjour.destroy() - } - }, []) - - return ( - - - Home - -
-
- ipcRenderer.send('close')}> - - -
-
- {warning ? : success ? : error ? : loading ? : } - window.location.reload()}> - WLED Manager - -
- { - if (ev.key === 'Enter') { - ev.preventDefault(); - handleButtonClick(iframe) - } - }} focused focuscolor={warning ? '#ffaa00' : success ? '#00a32e' : error ? '#e40303' : loading ? '#ffaa00' : '#004dff'} id="ip" label="WLED IP" style={{ width: 256 }} variant="outlined" value={iframe} onChange={(e) => setIframe(e.target.value)} /> -
-
- - handleButtonClick(iframe)} - > - {warning ? : success ? : error ? : loading ? : } - - {loading && ( - - )} - -
- {warning && - - No zeroconf available and WLED too old.
- Entering Single-Device-Mode -
} -
- - by Blade - -
-
- ); -}; - -export default Home; diff --git a/renderer/pages/yz.jsx b/renderer/pages/yz.jsx deleted file mode 100644 index 8a6d0dd..0000000 --- a/renderer/pages/yz.jsx +++ /dev/null @@ -1,455 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import Head from 'next/head'; -import clsx from 'clsx'; -import { remote } from 'electron'; -import { useRouter } from 'next/router'; -import { ipcRenderer } from 'electron'; -import { ArrowDownward, ArrowUpward, ChevronLeft, ChevronRight, Close, Equalizer, Refresh, Settings } from '@material-ui/icons'; -import { Drawer, List, Divider, Card, Typography, Button, IconButton, Tooltip, TextField } from '@material-ui/core'; -import useLeftBarStyles from '../styles/yz.styles'; -import { template } from '../components/MenuTemplate'; -import AudioDataContainer from '../components/AudioContainer'; -import useStore from '../store/store'; -import AddVirtual from '../components/AddVirtual'; -import AddSegment from '../components/AddSegment'; - -const LeftBar = () => { - if (typeof window === 'undefined') { - return <>server-side-rendered - } - - - const router = useRouter() - const leftBarOpen = useStore(state => state.leftBarOpen) - const setLeftBarOpen = useStore(state => state.setLeftBarOpen) - const bottomBarOpen = useStore(state => state.bottomBarOpen) - const setBottomBarOpen = useStore(state => state.setBottomBarOpen) - const iframe = useStore(state => state.iframe) - const setIframe = useStore(state => state.setIframe) - const devices = useStore(state => state.devices) - const setDevices = useStore(state => state.setDevices) - const device = useStore(state => state.device) - const setDevice = useStore(state => state.setDevice) - const virtuals = useStore(state => state.virtuals) - const setVirtuals = useStore(state => state.setVirtuals) - const virtual = useStore(state => state.virtual) - const setVirtual = useStore(state => state.setVirtual) - const audioDevice = useStore(state => state.audioDevice) - const setDrawerBottomHeight = useStore(state => state.setDrawerBottomHeight) - const drawerWidth = useStore(state => state.drawerWidth) - const drawerBottomHeight = useStore(state => state.drawerBottomHeight) - const audioSettings = useStore(state => state.audioSettings) - // const fft = useStore(state => state.audioSettings.fft) - // const bands = useStore(state => state.audioSettings.bands) - const setAudioSettings = useStore(state => state.setAudioSettings) - const leftFb = useStore(state => state.leftFb) - const rightFb = useStore(state => state.rightFb) - const classes = useLeftBarStyles({ drawerWidth, drawerBottomHeight, bottomBarOpen }); - - const [combNodes, setCombNodes] = useState([]) - const [isZeroConf, setIsZeroConf] = useState(router.query.zeroconf || (typeof window !== 'undefined' && window.localStorage.getItem("wled-manager-zeroconf") === 'true') || false) - const [singleMode, setSingleMode] = useState(router.query.singlemode || false) - const [error, setError] = useState("") - - const virtualView = useStore(state => state.virtualView) - const setVirtualView = useStore(state => state.setVirtualView) - const removeVirtual = useStore(state => state.removeVirtual) - const addVirtual = useStore(state => state.addVirtual) - - const openVirtual = (virtual) => { - setVirtualView(virtual.name) - } - - const handleRemoveVirtual = (v) => { - if (v.name === virtualView) { - setVirtualView(false) - } - removeVirtual(v) - }; - const handleSegments = (segs) => { - addVirtual({ - name: virtual.name, - type: 'span', - pixel_count: 0, - seg: [...virtual.seg || [], segs] - }); - }; - const removeSeg = (i) => { - const segs = [ ...virtual.seg ] - segs.splice(i, 1) - - addVirtual({ - name: virtual.name, - type: 'span', - pixel_count: 0, - seg: [...segs ] - }); - }; - - useEffect(() => { - const { Menu } = remote; - const customTitleBar = require('custom-electron-titlebar'); - const titlebar = new customTitleBar.Titlebar({ - backgroundColor: customTitleBar.Color.fromHex('#444'), - icon: '/images/logo.png', - }); - const temp = template() - const menu = Menu.buildFromTemplate(temp) - titlebar.updateMenu(menu); - - return () => { - titlebar.dispose(); - }; - }, []); - - useEffect(() => { - setVirtual(virtuals.find(v=>v.name === virtualView)) - }, [virtualView, virtuals]) - - - useEffect(() => { - virtuals.map(v=>{ - if (v.seg && v.seg.length) { - v.pixel_count = v.seg.map(s=>(s.seg && s.seg.length) ? s.seg[1] - s.seg[0] : 0).reduce((a,b)=>a+b) - } - return v - }) - }, [virtuals]) - - useEffect(() => { - ipcRenderer.send('resize-me-please', [1024, 1080]) - }, []) - - // useEffect(() => { - // const virt = virtuals.find(v => v.name === virtualView) - // if (virt) { - // setVirtual(virt) - // } - // }, [virtuals, virtualView]) - - useEffect(() => { - if (router.query && router.query.zeroconf) { - setIsZeroConf(true) - } - }, [router.query.zeroconf]) - - useEffect(() => { - if (router.query && router.query.singlemode) { - setSingleMode(true) - } - }, [router.query.singlemode]) - - useEffect(() => { - if (!isZeroConf && device) { - console.log(device) - if (!combNodes.filter(n => n.ip === device.ip).length > 0) { - setCombNodes((comb) => [...comb, { - "name": device.name, - "type": device.type, - "ip": device.ip, - "vid": device.vid, - "pixel_count": device.pixel_count - }]) - } - if (!devices.filter(n => n.ip === device.ip).length) { - setDevices([...devices, { - "name": device.name, - "type": device.type, - "ip": device.ip, - "vid": device.vid, - "pixel_count": device.pixel_count - }]) - } - } - }, [devices, device]) - - let bonjour = null; - useEffect(() => { - if (isZeroConf) { - bonjour = require('bonjour')() - bonjour.find({ type: 'wled' }, async (service) => { - if (service.referer && service.referer.address) { - if ((!combNodes.filter(n => n.ip === service.referer.address).length > 0) || (!devices.filter(n => n.ip === service.referer.address).length)) { - console.log("wled found:", service.name) - await fetch(`http://${service.referer.address}/json`) - .then(r => r.json()) - .then((re) => { - if (!combNodes.filter(n => n.ip === service.referer.address).length > 0) { - setCombNodes((comb) => [...comb, { - "name": service.name, - "type": re.info.arch === "esp8266" ? 82 : 32, - "ip": service.referer.address, - "vid": re.info.vid, - "pixel_count": re.info.leds.count, - "seg": re.state.seg - }]) - } - if (!devices.filter(n => n.ip === service.referer.address).length) { - setDevices([...devices, { - "name": service.name, - "type": re.info.arch === "esp8266" ? 82 : 32, - "ip": service.referer.address, - "vid": re.info.vid, - "pixel_count": re.info.leds.count, - "seg": re.state.seg - }]) - } - }) - } - } - }) - } else { - fetch(`http://${iframe}/json/nodes`) - .then(r => { - if (r.status === 501) { - console.log("No zeroconf for autodiscovery available. Also WLED version should be updated") - } - return r.json() - }) - .then((res) => { - if (res.nodes) { - res.nodes.forEach((node) => { - if ((!combNodes.filter(n => n.ip === node.ip).length > 0) || (!devices.filter(n => n.ip === node.ip).length)) { - console.log("wled found:", node) - fetch(`http://${node.ip}/json`) - .then(r => r.json()) - .then((re) => { - if (!combNodes.filter(n => n.ip === node.ip).length > 0) { - setCombNodes((comb) => [...comb, { - "name": node.name, - "type": re.info.arch === "esp8266" ? 82 : 32, - "ip": node.ip, - "vid": re.info.vid, - "pixel_count": re.info.leds.count, - "seg": re.state.seg - }]) - } - if (!devices.filter(n => n.ip === node.ip).length) { - setDevices([...devices, { - "name": node.name, - "type": re.info.arch === "esp8266" ? 82 : 32, - "ip": node.ip, - "vid": re.info.vid, - "pixel_count": re.info.leds.count, - "seg": re.state.seg - }]) - } - }) - } - }) - } - }) - .catch((error) => { - console.log("error: ", error) - }) - - if (router.query && router.query.ip) { - setIframe(router.query.ip) - } - } - - return () => { - if (isZeroConf) { - bonjour.destroy() - } - } - }, []) - - - return (<> - - WLED Manager - by Blade - - - -
-
-
- {singleMode && - - } -
- -
- - Devices - - router.push('/home')} style={{ color: '#999', padding: '3px', marginRight: '16px' }}> - - -
- - - {combNodes.length > 0 && combNodes.map((d, i) => ( - - { - setIframe(combNodes[i].ip) - setDevice(combNodes[i]) - setVirtualView(false) - }} style={{ cursor: 'pointer', margin: '0.5rem', padding: '0.5rem 0.25rem 0.5rem 0.5rem', background: (combNodes[i].ip === iframe && !virtualView) ? '#404040' : '#202020' }}> -
- - {combNodes[i].name} - -
- - -
-
-
- ))} - -
- - Virtuals - -
- - {virtuals.length > 0 && virtuals.map((v, i) => ( - - handleRemoveVirtual(v)} onClick={() => { - openVirtual(v) - }} style={{ cursor: 'pointer', margin: '0.5rem', padding: '0.5rem 0.25rem 0.5rem 0.5rem', background: virtualView === v.name ? '#404040' : '#202020' }}> -
- - {virtuals[i].name} - -
- - -
-
-
- ))} - -
- {/* - */} - - -
-
- router.push('/home')} gutterBottom variant="subtitle2" style={{ color: "#444" }}> - {'.'} - - - {'...'} - - window && window.localStorage.removeItem("wled-manager-ip")} gutterBottom variant="subtitle2" style={{ color: "#444" }}> - {'.'} - -
- - - by Blade - - -
-
- -
- - - {drawerBottomHeight !== 350 && <> - WebAudio settings -
- - { - if ((parseInt(e.target.value) != 0) && ((parseInt(e.target.value) & (parseInt(e.target.value) - 1)) == 0) && (parseInt(e.target.value) >= 32) && (parseInt(e.target.value) <= 32768)) { - setAudioSettings({ fft: parseInt(e.target.value) }); - setError("") - } else { - setError("fft") - } - }} /> - { - if (parseInt(e.target.value) > 0) { - setAudioSettings({ bands: parseInt(e.target.value) }); - setError("") - } else { - setError("bands") - } - }} /> - - - -
- - } - - -
- -
- -
-
setLeftBarOpen(!leftBarOpen)} className={clsx(classes.menuButton, { [classes.contentBottomShift]: !leftBarOpen })}> - {/*
setLeftBarOpen(!leftBarOpen)} style={{ flex: 1, minWidth: 'unset' }}> */} - {leftBarOpen ? : } - {/*
*/} -
- {virtualView ?
-
- {virtualView} -
-
- {virtual && virtual.seg && virtual.seg.length && virtual.seg.map((s,i)=> - removeSeg(i)} key={i} style={{ cursor: 'pointer', margin: '0.5rem', padding: '0.5rem 0.25rem 0.5rem 0.5rem', background: '#202020' }}> -
- - {`${s.device} - ${s.name}`} - - -
-
- )} - -
-
: