Skip to content

Commit

Permalink
migration to composition api (#37)
Browse files Browse the repository at this point in the history
* chore(*): migration to composition api

update deps
update technical stack with https://github.com/electron-vite/electron-vite-vue
migration to composition api

* fix(*): click button

* fix(*): watch props

* proofread a comment

* run linter

* merge new code into window_config, use window_config

* revise variable names for consistency

* do some manual lint resolution

* revise method call to match new method name

* revise some comments for consistency

* fix "Maximum recursive updates exceeded"

* fix "log.transcript.trim is not a function"

* fix "Cannot find name '$emit'."

* fix data type mismatches

* fix "possibly 'null'"

* clean up some comments

* restore word replace

* remove duplicate and unused code

* fix some inconsistent formatting

* fix edit function

* revise some comments for consistency

* fix "Write operation failed: computed value is readonly"

* clean up some code

* restore value

* temporarily insert some debugging (revert this later)

* clean up some code

* fix web app failing to send messages to desktop app

window.ipcRenderer.receive('channel', (data) => { methodName(data) }) → window.ipcRenderer.on('message', (event, data) => { methodName(data) }) (See: electron\preload\index.ts)

* fix broadcast behavior on desktop app

* fix update check

* implement $reset() for stores, fix button "Reset all settings"

* clean up some code

* restore code, fix electron window config persistence

* Revert "temporarily insert some debugging"

This reverts commit d318543.

* move some code

* route the user to '/' to apply theme on reset

* update resetStore() function

* remove unnecessary assign in resetStore()

* remove a fix that became unnecessary

* remove unused dep

* update dep, move pages settings

* v-for and router improvs

* chore(*): delete comment lines

* chore(text-field): add rules

* chore(*): formatting

* chore(*): rename interfaces

* chore(*): regroup plugins

* chore(footer): formatting

* chore(*): scoped css few sfc, and convert v-html

* chore(*) default language value

* chore(settings): move language into store

* fix(appearance): deep css

* chore(appareance): add key

* chore(*): fix refacto interface

* declare type

* update base router url

* properly import logo in welcome overlay

* remove console log

* add icon in electron builder config

* update requirements

* use @xenova/transformers in the Electron process to avoid error

addresses error: Uncaught Error: Dynamic require of "fs" is not supported

* try to fix some build errors

* fix build errors

* run lint:fix

* ipcrenderer only runs on electron

* fix issue where ignoring case on word replace would lowercase entire msg

* version 0.4.0

---------

Co-authored-by: Saya <[email protected]>
Co-authored-by: nae <[email protected]>
  • Loading branch information
3 people authored Jul 13, 2024
1 parent 2d87a55 commit bb210a4
Show file tree
Hide file tree
Showing 52 changed files with 7,196 additions and 13,794 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ See the [release page](https://github.com/naeruru/mimiuchi/releases) to install

### Requirements

- [NodeJS 18.x+](https://nodejs.org/en/)
- [NodeJS 20.x+](https://nodejs.org/en/)

### Setup

Expand Down
88 changes: 75 additions & 13 deletions electron-builder.json5
Original file line number Diff line number Diff line change
@@ -1,26 +1,80 @@
/**
* @see https://www.electron.build/configuration/configuration
*/
// @see https://www.electron.build/configuration/configuration
{
"$schema": "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json",
"appId": "Mimiuchi",
"asar": true,
"icon": "public/favicon.ico",
"productName": "mimiuchi",
"directories": {
"output": "release/${version}"
},
"files": [
"dist-electron",
"dist"
"dist",
"dist-electron"
],
"extraResources": [
{
"from": "node_modules/@huggingface",
"to": "app.asar.unpacked/node_modules/@huggingface"
},
{
"from": "node_modules/@xenova/transformers",
"to": "app.asar.unpacked/node_modules/@xenova/transformers"
},
{
"from": "node_modules/color",
"to": "app.asar.unpacked/node_modules/color"
},
{
"from": "node_modules/color-convert",
"to": "app.asar.unpacked/node_modules/color-convert"
},
{
"from": "node_modules/color-name",
"to": "app.asar.unpacked/node_modules/color-name"
},
{
"from": "node_modules/color-string",
"to": "app.asar.unpacked/node_modules/color-string"
},
{
"from": "node_modules/detect-libc",
"to": "app.asar.unpacked/node_modules/detect-libc"
},
{
"from": "node_modules/is-arrayish",
"to": "app.asar.unpacked/node_modules/is-arrayish"
},
{
"from": "node_modules/onnxruntime-common",
"to": "app.asar.unpacked/node_modules/onnxruntime-common"
},
{
"from": "node_modules/onnxruntime-node",
"to": "app.asar.unpacked/node_modules/onnxruntime-node"
},
{
"from": "node_modules/onnxruntime-web",
"to": "app.asar.unpacked/node_modules/onnxruntime-web"
},
{
"from": "node_modules/sharp",
"to": "app.asar.unpacked/node_modules/sharp"
},
{
"from": "node_modules/simple-swizzle",
"to": "app.asar.unpacked/node_modules/simple-swizzle"
},
{
"from": "src/worker.mjs",
"to": "app.asar.unpacked/src/worker.mjs"
}
],
"mac": {
"artifactName": "${productName}_${version}.${ext}",
"target": [
"dmg"
]
},
"linux": {
"icon": "public/logo-256x256.png",
"category": "utility"
],
"artifactName": "${productName}-Mac-${version}-Installer.${ext}",
"icon": "public/logo-256x256.png"
},
"win": {
"target": [
Expand All @@ -31,12 +85,20 @@
]
}
],
"artifactName": "${productName}_${version}.${ext}"
"artifactName": "${productName}-Windows-${version}-Setup.${ext}",
"icon": "public/favicon.ico"
},
"nsis": {
"oneClick": false,
"perMachine": false,
"allowToChangeInstallationDirectory": true,
"deleteAppDataOnUninstall": false
},
"linux": {
"target": [
"AppImage"
],
"artifactName": "${productName}-Linux-${version}.${ext}",
"icon": "public/logo-256x256.png"
}
}
18 changes: 15 additions & 3 deletions electron/electron-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,21 @@
declare namespace NodeJS {
interface ProcessEnv {
VSCODE_DEBUG?: 'true'
DIST_ELECTRON: string
DIST: string
/**
* The built directory structure
*
* ```tree
* ├─┬ dist-electron
* │ ├─┬ main
* │ │ └── index.js > Electron-Main
* │ └─┬ preload
* │ └── index.mjs > Preload-Scripts
* ├─┬ dist
* │ └── index.html > Electron-Renderer
* ```
*/
APP_ROOT: string
/** /dist/ or /public/ */
PUBLIC: string
VITE_PUBLIC: string
}
}
135 changes: 84 additions & 51 deletions electron/main/index.ts
Original file line number Diff line number Diff line change
@@ -1,81 +1,87 @@
import { join } from 'node:path'
import path from 'node:path'
import { createRequire } from 'node:module'
import { fileURLToPath } from 'node:url'
import os from 'node:os'
import { Worker } from 'node:worker_threads'
import { BrowserWindow, app, ipcMain, shell } from 'electron'

import Store from 'electron-store'

import { WebSocketServer } from 'ws'
import Store from 'electron-store'
import { emit_osc, empty_queue } from './modules/osc'
import { initialize_ws } from './modules/ws'
import { check_update } from './modules/check_update'

const store = new Store()

const require = createRequire(import.meta.url)
const __dirname = path.dirname(fileURLToPath(import.meta.url))

// The built directory structure
//
// ├─┬ dist-electron
// │ ├─┬ main
// │ │ └── index.js > Electron-Main
// │ └─┬ preload
// │ └── index.js > Preload-Scripts
// │ └── index.mjs > Preload-Scripts
// ├─┬ dist
// │ └── index.html > Electron-Renderer
//
process.env.DIST_ELECTRON = join(__dirname, '..')
process.env.DIST = join(process.env.DIST_ELECTRON, '../dist')
process.env.PUBLIC = process.env.VITE_DEV_SERVER_URL
? join(process.env.DIST_ELECTRON, '../public')
: process.env.DIST

// Remove electron security warnings
// This warning only shows in development mode
// Read more on https://www.electronjs.org/docs/latest/tutorial/security
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'

// export const ROOT_PATH = {
// // /dist
// dist: join(__dirname, '../..'),
// // /dist or /public
// public: join(__dirname, app.isPackaged ? '../..' : '../../../public'),
// }
process.env.APP_ROOT = path.join(__dirname, '../..')

let win: BrowserWindow | null = null
// Here, you can also use other preload
const preload = join(__dirname, '../preload/index.js')
const url = process.env.VITE_DEV_SERVER_URL
const indexHtml = join(process.env.DIST, 'index.html')
export const MAIN_DIST = path.join(process.env.APP_ROOT, 'dist-electron')
export const RENDERER_DIST = path.join(process.env.APP_ROOT, 'dist')
export const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL

process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL
? path.join(process.env.APP_ROOT, 'public')
: RENDERER_DIST

// Disable GPU Acceleration for Windows 7
if (os.release().startsWith('6.1'))
app.disableHardwareAcceleration()

let wss: any
// Set application name for Windows 10+ notifications
if (process.platform === 'win32')
app.setAppUserModelId(app.getName())

if (!app.requestSingleInstanceLock()) {
app.quit()
process.exit(0)
}

let win: BrowserWindow | null = null
const preload = path.join(__dirname, '../preload/index.mjs')
const indexHtml = path.join(RENDERER_DIST, 'index.html')

const window_config: any = {
title: 'Main window',
width: 1000,
height: 700,
icon: join(process.env.PUBLIC, 'favicon.ico'),
icon: path.join(process.env.VITE_PUBLIC, 'favicon.ico'),
frame: false,
webPreferences: {
preload,
// Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
// nodeIntegration: true,

// Consider using contextBridge.exposeInMainWorld
// Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
// contextIsolation: false,
nodeIntegration: true,
contextIsolation: true, // was false
contextIsolation: true,
},
}

async function createWindow() {
Object.assign(window_config, store.get('win_bounds'))
win = new BrowserWindow(window_config)

if (window_config.isMaximized)
win.maximize()

if (process.env.VITE_DEV_SERVER_URL) {
win.loadURL(url)
if (VITE_DEV_SERVER_URL) { // #298
win.loadURL(VITE_DEV_SERVER_URL)
// Open devTool if the app is not packaged
win.webContents.openDevTools()
}
else {
// win.removeMenu()
win.loadFile(indexHtml)
}

Expand All @@ -86,8 +92,7 @@ async function createWindow() {

// Make all links open with the browser, not with the application
win.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith('https:'))
shell.openExternal(url)
if (url.startsWith('https:')) shell.openExternal(url)
return { action: 'deny' }
})
// win.webContents.on('will-navigate', (event, url) => { }) #344
Expand All @@ -104,32 +109,38 @@ async function createWindow() {
})
}

const transformersWorkerPath = `file://${path.join(process.env.APP_ROOT, 'src', 'worker.mjs').replace('app.asar', 'app.asar.unpacked')}`
const transformersWorker = new Worker(new URL(transformersWorkerPath, import.meta.url))

const transformersPath = `file://${path.join(process.env.APP_ROOT, 'node_modules', '@xenova', 'transformers', 'src', 'transformers.js').replace('app.asar', 'app.asar.unpacked')}`
transformersWorker.postMessage({ type: 'transformers-init', data: transformersPath })

app.whenReady().then(createWindow)

app.on('window-all-closed', () => {
win = null
if (process.platform !== 'darwin')
app.quit()
if (process.platform !== 'darwin') app.quit()
})

app.on('second-instance', () => {
if (win) {
// Focus on the main window if the user tried to open another
if (win.isMinimized())
win.restore()
if (win.isMinimized()) win.restore()
win.focus()
}
})

app.on('activate', () => {
const allWindows = BrowserWindow.getAllWindows()
if (allWindows.length)
if (allWindows.length) {
allWindows[0].focus()
else
}
else {
createWindow()
}
})

// new window example arg: new windows url
// New window example arg: new windows url
ipcMain.handle('open-win', (_, arg) => {
const childWindow = new BrowserWindow({
webPreferences: {
Expand All @@ -139,8 +150,8 @@ ipcMain.handle('open-win', (_, arg) => {
},
})

if (process.env.VITE_DEV_SERVER_URL)
childWindow.loadURL(`${url}#${arg}`)
if (VITE_DEV_SERVER_URL)
childWindow.loadURL(`${VITE_DEV_SERVER_URL}#${arg}`)
else
childWindow.loadFile(indexHtml, { hash: arg })
})
Expand All @@ -167,7 +178,7 @@ ipcMain.on('typing-text-event', (event, args) => {
})

// event for sending text
export let text_queue = []
let text_queue = []
ipcMain.on('send-text-event', (event, args) => {
args = JSON.parse(args)
const new_text = args.transcript.includes(' ') ? args.transcript.match(/.{1,140}(\s|$)/g) : args.transcript.match(/.{1,140}/g)
Expand All @@ -181,22 +192,44 @@ ipcMain.on('send-param-event', (event, args) => {
emit_osc([args.route, args.value], args.ip, args.port)
})

let wss: WebSocketServer = null
// websocket events
ipcMain.on('start-ws', (event, args) => {
wss = new WebSocketServer({ port: args })
initialize_ws(win, wss, args)
.then((ws: WebSocket) => {
.then(() => {
win.webContents.send('websocket-started', true)
})
.catch((error: any) => {
.catch(() => {
win.webContents.send('websocket-error', true)
})
})
ipcMain.on('close-ws', (event, args) => {

ipcMain.on('close-ws', () => {
wss.close()
})

ipcMain.on('update-check', async (event) => {
ipcMain.on('update-check', async () => {
const latest = await check_update()
win.webContents.send('update-check', latest)
})

// Translations
//
// Footer (user submission)
// → Speech Store
// → [Condition: translations are enabled]
// → Electron ('transformers-translate')
// → Worker (worker thread)
// → Electron ('transformers-translate-output')
// → Footer ('transformers-translate-render')

ipcMain.on('transformers-translate', async (event, args) => {
transformersWorker.postMessage({ type: 'transformers-translate', data: args })
})

transformersWorker.on('message', async (message) => {
if (message.type === 'transformers-translate-output') {
win.webContents.send('transformers-translate-render', message.data)
}
})
Loading

0 comments on commit bb210a4

Please sign in to comment.