From dafbd6330fd3ff6872d545d428b0a3603340c2b7 Mon Sep 17 00:00:00 2001 From: Hocky Yudhiono Date: Mon, 24 Jul 2023 21:17:33 +0700 Subject: [PATCH 01/15] [#32] Add create with cache button fix and UI improvement --- main/background.ts | 18 +++-- package-lock.json | 63 ++++++++++++++- package.json | 4 + patches/jmdict-simplified-node+1.1.2.patch | 8 +- renderer/hooks/useMiteiruTokenizer.tsx | 12 +++ renderer/pages/home.tsx | 93 ++++++++-------------- 6 files changed, 130 insertions(+), 68 deletions(-) create mode 100644 renderer/hooks/useMiteiruTokenizer.tsx diff --git a/main/background.ts b/main/background.ts index ea3287f..1098410 100644 --- a/main/background.ts +++ b/main/background.ts @@ -12,6 +12,7 @@ import { } from 'jmdict-simplified-node'; import fs from "fs"; import path from "path"; +import {getTokenizer} from "kuromojin"; const isProd: boolean = process.env.NODE_ENV === 'production'; @@ -235,13 +236,10 @@ if (isProd) { return okSetup; } - ipcMain.handle('getMecabCommand', async (event, mecab, text) => { + ipcMain.handle('getMecabCommand', async () => { return mecabCommand }) ipcMain.handle('validateConfig', async (event, config) => { - - // const dicdirRes = checkDicdir(config); - // if (dicdirRes.ok !== 1) return dicdirRes let jmdictRes; if (!config.cached) { jmdictRes = await checkJMDict(config); @@ -259,9 +257,19 @@ if (isProd) { }) let packageJsonPath = path.join(app.getAppPath(), 'package.json'); let packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString()); - ipcMain.handle('getAppVersion', async (event, ...args) => { + ipcMain.handle('getAppVersion', async () => { return packageJson.version; }); + let tokenizer = null; + getTokenizer({dicPath: path.join(__dirname, 'dict/')}).then(loadedTokenizer => { + tokenizer = loadedTokenizer; + }).catch(e => { + console.log(e) + }) + ipcMain.handle('tokenizeUsingKuromoji', async (event, sentence) => { + console.log(sentence) + return tokenizer.tokenizeForSentence(sentence); + }); protocol.registerFileProtocol(scheme, requestHandler); /* eng-disable PROTOCOL_HANDLER_JS_CHECK */ if (isProd) { await mainWindow.loadURL('app://./home.html'); diff --git a/package-lock.json b/package-lock.json index 9076ed8..b276c0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,9 @@ "html-react-parser": "^3.0.16", "iconv-lite": "^0.6.3", "jmdict-simplified-node": "^1.1.2", + "kuromojin": "^3.0.0", "patch-package": "^7.0.0", + "react-awesome-button": "^7.0.5", "react-colorful": "^5.6.1", "react-dropzone": "^14.2.3", "react-smooth-collapse": "^2.1.2", @@ -2943,6 +2945,11 @@ "resolved": "https://registry.npmjs.org/@plussub/srt-vtt-parser/-/srt-vtt-parser-1.1.0.tgz", "integrity": "sha512-wDhuvXmTC6E9lxbwX/ashWpvFTH2v5MRT6LqTlGC6+fmragETMzcCubaEiVWWiNVT+HexKe2F6s421OFmfrjiw==" }, + "node_modules/@rcaferati/wac": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rcaferati/wac/-/wac-1.0.0.tgz", + "integrity": "sha512-7dVbzvcfL9o4UnrCaNeoIs7Ri9Ol+OMfpYYCJJV1dfm+5+pVM6DrvFjPzqifd2ACVABpXmwTQKy9KFxNm/7IdA==" + }, "node_modules/@rushstack/eslint-patch": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz", @@ -5507,6 +5514,11 @@ "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", "dev": true }, + "node_modules/doublearray": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/doublearray/-/doublearray-0.0.2.tgz", + "integrity": "sha512-aw55FtZzT6AmiamEj2kvmR6BuFqvYgKZUkfQ7teqVRNqD5UE0rw8IeW/3gieHNKQ5sPuDKlljWEn4bzv5+1bHw==" + }, "node_modules/duplexer3": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", @@ -8105,6 +8117,33 @@ "graceful-fs": "^4.1.11" } }, + "node_modules/kuromoji": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/kuromoji/-/kuromoji-0.1.2.tgz", + "integrity": "sha512-V0dUf+C2LpcPEXhoHLMAop/bOht16Dyr+mDiIE39yX3vqau7p80De/koFqpiTcL1zzdZlc3xuHZ8u5gjYRfFaQ==", + "dependencies": { + "async": "^2.0.1", + "doublearray": "0.0.2", + "zlibjs": "^0.3.1" + } + }, + "node_modules/kuromoji/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/kuromojin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/kuromojin/-/kuromojin-3.0.0.tgz", + "integrity": "sha512-3h3qnn/NVVhqoKFP4oc7e6apO2B01Atc056oiVlIY7Uoup4rhrnBe28g3y9lK1HTmLDQEejvXB+3I3qxAneF7A==", + "dependencies": { + "kuromoji": "0.1.2", + "lru_map": "^0.4.1" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.22", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", @@ -8251,8 +8290,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "devOptional": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -8302,6 +8340,11 @@ "node": ">=0.10.0" } }, + "node_modules/lru_map": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.4.1.tgz", + "integrity": "sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg==" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -9863,6 +9906,14 @@ "node": ">=0.10.0" } }, + "node_modules/react-awesome-button": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/react-awesome-button/-/react-awesome-button-7.0.5.tgz", + "integrity": "sha512-pNABRQsvnbd5rGL7mX9udWa0aPnXtP6hEL90/Cqjc/ERADpOMu3z6zix+AW/NOBp0NQoi9J+hhKoZS2dabs15g==", + "dependencies": { + "@rcaferati/wac": "^1.0.0" + } + }, "node_modules/react-colorful": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", @@ -11999,6 +12050,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zlibjs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", + "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", + "engines": { + "node": "*" + } + }, "node_modules/zod": { "version": "3.21.4", "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", diff --git a/package.json b/package.json index eef4106..49d4b02 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ } }, "scripts": { + "copy": "rsync -r renderer/public/ app/", + "predev": "npm run copy", "dev": "nextron", "build": "nextron build", "build:linux": "nextron build --linux", @@ -40,7 +42,9 @@ "html-react-parser": "^3.0.16", "iconv-lite": "^0.6.3", "jmdict-simplified-node": "^1.1.2", + "kuromojin": "^3.0.0", "patch-package": "^7.0.0", + "react-awesome-button": "^7.0.5", "react-colorful": "^5.6.1", "react-dropzone": "^14.2.3", "react-smooth-collapse": "^2.1.2", diff --git a/patches/jmdict-simplified-node+1.1.2.patch b/patches/jmdict-simplified-node+1.1.2.patch index f66034d..1ab0414 100644 --- a/patches/jmdict-simplified-node+1.1.2.patch +++ b/patches/jmdict-simplified-node+1.1.2.patch @@ -1,8 +1,12 @@ diff --git a/node_modules/jmdict-simplified-node/index.js b/node_modules/jmdict-simplified-node/index.js -index b50ce2d..233738c 100644 +index b50ce2d..d81da04 100644 --- a/node_modules/jmdict-simplified-node/index.js +++ b/node_modules/jmdict-simplified-node/index.js -@@ -34,43 +34,48 @@ function setup(DBNAME, filename = '', verbose = false) { +@@ -31,46 +31,52 @@ function setup(DBNAME, filename = '', verbose = false) { + // pass + } + if (!filename) { ++ db.close(); throw new Error('database not found but cannot create it if no `filename` given'); } const raw = JSON.parse(yield fs_1.promises.readFile(filename, 'utf8')); diff --git a/renderer/hooks/useMiteiruTokenizer.tsx b/renderer/hooks/useMiteiruTokenizer.tsx new file mode 100644 index 0000000..5fde924 --- /dev/null +++ b/renderer/hooks/useMiteiruTokenizer.tsx @@ -0,0 +1,12 @@ +import {useCallback, useEffect, useState} from "react"; +import {getTokenizer} from "kuromojin"; +import {ipcRenderer} from "electron"; + +const useMiteiruTokenizer = () => { + const tokenizeMiteiru = useCallback(async (sentence) => { + return await ipcRenderer.invoke('tokenizeUsingKuromoji', sentence); + }, []) + return {tokenizeMiteiru}; +} + +export default useMiteiruTokenizer; \ No newline at end of file diff --git a/renderer/pages/home.tsx b/renderer/pages/home.tsx index 0f14412..8cd53e6 100644 --- a/renderer/pages/home.tsx +++ b/renderer/pages/home.tsx @@ -5,6 +5,9 @@ import {ipcRenderer} from 'electron'; import {ContainerHome} from "../components/ContainerHome"; import {KeyboardHelp} from "../components/KeyboardHelp"; import useMiteiruVersion from "../hooks/useMiteiruVersion"; +import 'react-awesome-button/dist/styles.css'; +import useMiteiruTokenizer from "../hooks/useMiteiruTokenizer"; +import {AwesomeButton} from "react-awesome-button"; const checkSymbol = ['❌', 'βœ…', 'πŸ™ƒ'] const initialCheck = {ok: 0, message: 'Check is not run yet'} @@ -15,7 +18,6 @@ const mecabDefaultDirectory = { } function Home() { - const [dicdir, setDicdir] = useState(''); const [mecab, setMecab] = useState(mecabDefaultDirectory[process.platform] ?? mecabDefaultDirectory['linux']); const [jmdict, setJmdict] = useState(''); const [check, setCheck] = useState(initialCheck); @@ -38,11 +40,11 @@ function Home() { message: "checking..." }); ipcRenderer.invoke('validateConfig', { - mecab, dicdir, jmdict, cached + mecab, jmdict, cached }).then(val => { setCheck(val); }); - }, [mecab, dicdir, jmdict]); + }, [mecab, jmdict]); const handleRemoveJMDictCache = useCallback(() => { setCheck({ @@ -54,6 +56,15 @@ function Home() { }); }, []); const {miteiruVersion} = useMiteiruVersion(); + const {tokenizeMiteiru} = useMiteiruTokenizer(); + useEffect(() => { + const fetchData = async () => { + const test = await tokenizeMiteiru("οΌ‘οΌοΌε††γ§γ€€οΌ‘γƒγ‚€γƒ³γƒˆγ«ε€‰ζ›γ§γγ‚‹\nγ£γ¦θ¨€γ£γ¦γŸγ—γͺ。"); + console.log(test); + }; + + // fetchData() + }, []); return ( @@ -64,30 +75,11 @@ function Home() {
- {/*
*/} - {/* {*/} - {/* ipcRenderer.invoke('pickDirectory').then((val) => {*/} - {/* if (!val.canceled) setDicdir(val.filePaths[0])*/} - {/* })*/} - {/* }*/} - {/* }>*/} - {/* Select MeCab Dictionary Directory*/} - {/* */} - {/* {*/} - {/* setDicdir(val.target.value)*/} - {/* }}>*/} - {/*
*/}
- +
- +
- - +
{checkSymbol[check.ok]}{' '}{check.message} @@ -136,31 +127,15 @@ function Home() {
- - {/* {*/} - {/* const text = 'ζœ¨γƒεˆ‡γ£γ¦ 月収6万だろ~'*/} - {/* ipcRenderer.invoke('getShunou', mecab, text).then(val => {*/} - {/* console.log(val)*/} - {/* })*/} - {/* }*/} - {/* }>*/} - {/* tmp*/} - {/**/} + - +
From 0297c1968d285497baea06cfb564b05380db7a8a Mon Sep 17 00:00:00 2001 From: Hocky Yudhiono Date: Mon, 24 Jul 2023 21:28:39 +0700 Subject: [PATCH 02/15] [#32] Add button disabled effect on video --- renderer/pages/home.tsx | 18 ++++++++++++------ renderer/styles/globals.css | 6 ++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/renderer/pages/home.tsx b/renderer/pages/home.tsx index 8cd53e6..e54a45b 100644 --- a/renderer/pages/home.tsx +++ b/renderer/pages/home.tsx @@ -1,6 +1,5 @@ import React, {useCallback, useEffect, useState} from 'react'; import Head from 'next/head'; -import Link from 'next/link'; import {ipcRenderer} from 'electron'; import {ContainerHome} from "../components/ContainerHome"; import {KeyboardHelp} from "../components/KeyboardHelp"; @@ -8,6 +7,7 @@ import useMiteiruVersion from "../hooks/useMiteiruVersion"; import 'react-awesome-button/dist/styles.css'; import useMiteiruTokenizer from "../hooks/useMiteiruTokenizer"; import {AwesomeButton} from "react-awesome-button"; +import {useRouter} from "next/router"; const checkSymbol = ['❌', 'βœ…', 'πŸ™ƒ'] const initialCheck = {ok: 0, message: 'Check is not run yet'} @@ -18,9 +18,15 @@ const mecabDefaultDirectory = { } function Home() { + const router = useRouter(); const [mecab, setMecab] = useState(mecabDefaultDirectory[process.platform] ?? mecabDefaultDirectory['linux']); const [jmdict, setJmdict] = useState(''); const [check, setCheck] = useState(initialCheck); + const handleClick = useCallback(async () => { + if (check.ok === 1) { + await router.push('/video'); + } + }, [check, router]); const handleSelectMecabPath = useCallback(() => { ipcRenderer.invoke('pickFile', ['*']).then((val) => { @@ -132,11 +138,11 @@ function Home() { onPress={handleRemoveJMDictCache}> Remove JMDict Cache - - - Video - - + + Video +
diff --git a/renderer/styles/globals.css b/renderer/styles/globals.css index d4a01d9..7379552 100644 --- a/renderer/styles/globals.css +++ b/renderer/styles/globals.css @@ -141,3 +141,9 @@ button { border-radius: 9px; box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); } + + +.buttonDisabled { + cursor: not-allowed !important; + /* any other styles for the disabled state */ +} \ No newline at end of file From 6d1c7914b5bb96a5b945ac37661a393bfbde9d04 Mon Sep 17 00:00:00 2001 From: Hocky Yudhiono Date: Mon, 24 Jul 2023 21:36:41 +0700 Subject: [PATCH 03/15] [#32] Refactor Mecab home --- renderer/components/ContainerHome.tsx | 2 +- renderer/pages/home.tsx | 36 +++++++++++---------------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/renderer/components/ContainerHome.tsx b/renderer/components/ContainerHome.tsx index dec49ae..8e04e9e 100644 --- a/renderer/components/ContainerHome.tsx +++ b/renderer/components/ContainerHome.tsx @@ -1,6 +1,6 @@ export const ContainerHome = (props) => { return ( -
+
{props.children}
) diff --git a/renderer/pages/home.tsx b/renderer/pages/home.tsx index e54a45b..d0fa9f5 100644 --- a/renderer/pages/home.tsx +++ b/renderer/pages/home.tsx @@ -8,6 +8,7 @@ import 'react-awesome-button/dist/styles.css'; import useMiteiruTokenizer from "../hooks/useMiteiruTokenizer"; import {AwesomeButton} from "react-awesome-button"; import {useRouter} from "next/router"; +import Toggle from "../components/Toggle"; const checkSymbol = ['❌', 'βœ…', 'πŸ™ƒ'] const initialCheck = {ok: 0, message: 'Check is not run yet'} @@ -22,6 +23,7 @@ function Home() { const [mecab, setMecab] = useState(mecabDefaultDirectory[process.platform] ?? mecabDefaultDirectory['linux']); const [jmdict, setJmdict] = useState(''); const [check, setCheck] = useState(initialCheck); + const [isUsingKuromoji, setUsingKuromoji] = useState(true); const handleClick = useCallback(async () => { if (check.ok === 1) { await router.push('/video'); @@ -80,6 +82,9 @@ function Home() { className={"flex flex-col justify-center items-center bg-white min-h-screen w-[100vw]"}>
+ { + setUsingKuromoji(val); + }}/>
-
+
Select JMDict Json @@ -105,9 +110,7 @@ function Home() { setJmdict(val.target.value) }}>
- - -
+
-
- {checkSymbol[check.ok]}{' '}{check.message} -
- - -
- - - - -
Remove JMDict Cache - - Video - +
+ {checkSymbol[check.ok]}{' '}{check.message} +
- + + Video +
From 08c5bddd7fbc4feaec6b6d87c227a246e1284f2b Mon Sep 17 00:00:00 2001 From: Hocky Yudhiono Date: Mon, 24 Jul 2023 21:54:51 +0700 Subject: [PATCH 04/15] [#32] Add jokes ready video --- renderer/pages/home.tsx | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/renderer/pages/home.tsx b/renderer/pages/home.tsx index d0fa9f5..2d923be 100644 --- a/renderer/pages/home.tsx +++ b/renderer/pages/home.tsx @@ -9,6 +9,7 @@ import useMiteiruTokenizer from "../hooks/useMiteiruTokenizer"; import {AwesomeButton} from "react-awesome-button"; import {useRouter} from "next/router"; import Toggle from "../components/Toggle"; +import SmoothCollapse from "react-smooth-collapse"; const checkSymbol = ['❌', 'βœ…', 'πŸ™ƒ'] const initialCheck = {ok: 0, message: 'Check is not run yet'} @@ -23,7 +24,7 @@ function Home() { const [mecab, setMecab] = useState(mecabDefaultDirectory[process.platform] ?? mecabDefaultDirectory['linux']); const [jmdict, setJmdict] = useState(''); const [check, setCheck] = useState(initialCheck); - const [isUsingKuromoji, setUsingKuromoji] = useState(true); + const [isUsingMecab, setUsingMecab] = useState(true); const handleClick = useCallback(async () => { if (check.ok === 1) { await router.push('/video'); @@ -73,6 +74,7 @@ function Home() { // fetchData() }, []); + const ableToProceedToVideo = !isUsingMecab || (check.ok === 1) return ( @@ -81,11 +83,15 @@ function Home() {
- { - setUsingKuromoji(val); - }}/> - + className={"flex flex-col h-fit items-center bg-blue-50 gap-4 w-full p-5 border rounded-lg border-blue-800"}> +
+ { + setUsingMecab(val); + }}/> + {isUsingMecab &&
ι¬Όη•œ πŸ‘Ή
} + {!isUsingMecab &&
γƒŒγƒ« 🐣
} +
+
@@ -134,11 +140,12 @@ function Home() { {checkSymbol[check.ok]}{' '}{check.message}
- + - Video + className={ableToProceedToVideo ? '' : 'buttonDisabled'} + disabled={!ableToProceedToVideo}> + {!isUsingMecab &&
うん、けょっと見てるだけ 😏
} + {isUsingMecab &&
ζΊ–ε‚™OKγ€θˆΉι•·οΌπŸ«‘
}
From 3ed37f5bcd2eb8bd59461f6074aa2bcf3a639fc1 Mon Sep 17 00:00:00 2001 From: Hocky Yudhiono Date: Mon, 24 Jul 2023 22:16:43 +0700 Subject: [PATCH 05/15] [#32] Pack JMDict with miteiru --- main/background.ts | 6 +++++ renderer/components/KeyboardHelp.tsx | 2 +- renderer/components/Toggle.tsx | 16 ++++++------ renderer/pages/home.tsx | 38 +++++++++++++++++----------- 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/main/background.ts b/main/background.ts index 1098410..f840437 100644 --- a/main/background.ts +++ b/main/background.ts @@ -166,6 +166,12 @@ if (isProd) { }) + + ipcMain.handle('loadDefaultJmdict', async (event) => { + return await checkJMDict({ + jmdict: path.join(__dirname, 'dict/jmdict.json') + }); + }) ipcMain.handle('removeDictCache', (event) => { removeJMDictCache() return true; diff --git a/renderer/components/KeyboardHelp.tsx b/renderer/components/KeyboardHelp.tsx index 0c2867c..6ba6557 100644 --- a/renderer/components/KeyboardHelp.tsx +++ b/renderer/components/KeyboardHelp.tsx @@ -46,7 +46,7 @@ export const Key = ({value, extraClass = ""}) => { export const KeyboardHelp = () => { return
+ className={"grid grid-cols-2 gap-5 content-start w-full md:w-4/5 px-5 py-3 m-2 bg-blue-50 rounded-lg border-2 border-black"}> {shortcutInformation.map((val, idx) => { return
{ + const isChecked = e.target.checked; + setEnabled(isChecked); + onChange(isChecked); + }; + return (
@@ -12,19 +18,13 @@ export default function Toggle({onChange, defaultCheck}) { type="checkbox" className="sr-only peer" checked={enabled} - readOnly + onChange={handleChange} />
{ - setEnabled(old => { - onChange(!old); - return !old; - }); - }} className="w-11 h-6 bg-gray-500 rounded-full peer peer-focus:ring-green-300 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-600" >
); -} \ No newline at end of file +} diff --git a/renderer/pages/home.tsx b/renderer/pages/home.tsx index 2d923be..94ad03b 100644 --- a/renderer/pages/home.tsx +++ b/renderer/pages/home.tsx @@ -11,24 +11,34 @@ import {useRouter} from "next/router"; import Toggle from "../components/Toggle"; import SmoothCollapse from "react-smooth-collapse"; -const checkSymbol = ['❌', 'βœ…', 'πŸ™ƒ'] -const initialCheck = {ok: 0, message: 'Check is not run yet'} +const checkSymbol = ['❓', 'βœ…', 'πŸ™ƒ'] +const initialCheck = {ok: 0, message: '🐸 γ‚²γƒ­γ‚²γƒ­'} const mecabDefaultDirectory = { 'darwin': '/opt/homebrew/bin/mecab', 'linux': '/usr/bin/mecab', 'win32': 'C:\\Program Files (x86)\\MeCab\\bin\\mecab.exe' } +const checkingMessage = { + ok: 2, + message: "checking..." +} function Home() { const router = useRouter(); const [mecab, setMecab] = useState(mecabDefaultDirectory[process.platform] ?? mecabDefaultDirectory['linux']); const [jmdict, setJmdict] = useState(''); const [check, setCheck] = useState(initialCheck); - const [isUsingMecab, setUsingMecab] = useState(true); + const [isUsingMecab, setUsingMecab] = useState(false); const handleClick = useCallback(async () => { - if (check.ok === 1) { - await router.push('/video'); + if (!isUsingMecab) { + setCheck(checkingMessage); + const res = await ipcRenderer.invoke('loadDefaultJmdict'); + setCheck(res); + if (res.ok !== 1) { + return; + } } + await router.push('/video'); }, [check, router]); const handleSelectMecabPath = useCallback(() => { @@ -44,10 +54,7 @@ function Home() { }, []); const handleCheck = useCallback((cached = false) => { - setCheck({ - ok: 2, - message: "checking..." - }); + setCheck(checkingMessage); ipcRenderer.invoke('validateConfig', { mecab, jmdict, cached }).then(val => { @@ -83,9 +90,9 @@ function Home() {
+ className={"flex flex-col h-fit items-center bg-blue-50 gap-4 w-full md:w-4/5 p-5 border rounded-lg border-blue-800 border-2"}>
- { + { setUsingMecab(val); }}/> {isUsingMecab &&
ι¬Όη•œ πŸ‘Ή
} @@ -136,15 +143,16 @@ function Home() { onPress={handleRemoveJMDictCache}> Remove JMDict Cache -
- {checkSymbol[check.ok]}{' '}{check.message} -
+
+ {checkSymbol[check.ok]}{' '}{check.message} +
- {!isUsingMecab &&
うん、けょっと見てるだけ 😏
} + {!isUsingMecab &&
うん、けょっと見てるだけ 😏
} {isUsingMecab &&
ζΊ–ε‚™OKγ€θˆΉι•·οΌπŸ«‘
}
From 7f975eaac35fb25773d8c0896fb9cf16c5e74715 Mon Sep 17 00:00:00 2001 From: Hocky Yudhiono Date: Mon, 24 Jul 2023 22:31:23 +0700 Subject: [PATCH 06/15] [#32] Refactor mecab to general tokenizer --- main/background.ts | 7 ++++--- renderer/components/MeaningBox.tsx | 8 ++++---- renderer/hooks/useMecab.tsx | 16 ---------------- renderer/hooks/useMiteiruTokenizer.tsx | 23 ++++++++++++++++------- renderer/pages/home.tsx | 2 +- renderer/pages/learn.tsx | 2 +- renderer/pages/video.tsx | 10 +++++----- 7 files changed, 31 insertions(+), 37 deletions(-) delete mode 100644 renderer/hooks/useMecab.tsx diff --git a/main/background.ts b/main/background.ts index f840437..e14fbc8 100644 --- a/main/background.ts +++ b/main/background.ts @@ -167,7 +167,8 @@ if (isProd) { }) - ipcMain.handle('loadDefaultJmdict', async (event) => { + ipcMain.handle('loadDefaultMode', async (event) => { + mecabCommand = 'kuromoji'; return await checkJMDict({ jmdict: path.join(__dirname, 'dict/jmdict.json') }); @@ -242,8 +243,8 @@ if (isProd) { return okSetup; } - ipcMain.handle('getMecabCommand', async () => { - return mecabCommand + ipcMain.handle('getTokenizerMode', async () => { + return mecabCommand; }) ipcMain.handle('validateConfig', async (event, config) => { let jmdictRes; diff --git a/renderer/components/MeaningBox.tsx b/renderer/components/MeaningBox.tsx index 8ba3668..d2ecbbb 100644 --- a/renderer/components/MeaningBox.tsx +++ b/renderer/components/MeaningBox.tsx @@ -9,9 +9,9 @@ const initialContentState = {sense: [], kanji: []}; const MeaningBox = ({ meaning, setMeaning, - mecab, + tokenizeMiteiru, subtitleStyling = defaultMeaningBoxStyling - }: { meaning: string, setMeaning: any, mecab: string, subtitleStyling?: CJKStyling }) => { + }: { meaning: string, setMeaning: any, tokenizeMiteiru: (value: string) => Promise, subtitleStyling?: CJKStyling }) => { const [meaningContent, setMeaningContent] = useState(initialContentState) const [otherMeanings, setOtherMeanings] = useState([]); const [meaningIndex, setMeaningIndex] = useState(0); @@ -69,8 +69,8 @@ const MeaningBox = ({
{meaningContent.kanji.map((val, meanKey) => { - const furiganized = getFurigana(val.text, mecab); + }}>{meaningContent.kanji.map(async (val, meanKey) => { + const furiganized = await tokenizeMiteiru(val.text); return (
diff --git a/renderer/hooks/useMecab.tsx b/renderer/hooks/useMecab.tsx deleted file mode 100644 index 2a205a0..0000000 --- a/renderer/hooks/useMecab.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import {useState, useEffect} from 'react'; -import {ipcRenderer} from "electron"; - -const useMecab = () => { - const [mecab, setMecab] = useState(''); - - useEffect(() => { - ipcRenderer.invoke('getMecabCommand').then(val => { - setMecab(val); - }); - }, []); - - return mecab; -}; - -export default useMecab; diff --git a/renderer/hooks/useMiteiruTokenizer.tsx b/renderer/hooks/useMiteiruTokenizer.tsx index 5fde924..a29bf55 100644 --- a/renderer/hooks/useMiteiruTokenizer.tsx +++ b/renderer/hooks/useMiteiruTokenizer.tsx @@ -1,12 +1,21 @@ -import {useCallback, useEffect, useState} from "react"; -import {getTokenizer} from "kuromojin"; +import {useState, useEffect, useCallback} from 'react'; import {ipcRenderer} from "electron"; +import {getFurigana} from "shunou"; -const useMiteiruTokenizer = () => { +const useMiteiruTokenizer = (): { tokenizeMiteiru: (sentence: string) => Promise, tokenizerMode: string } => { + const [tokenizerMode, setMode] = useState(''); + + useEffect(() => { + ipcRenderer.invoke('getTokenizerMode').then(val => { + setMode(val); + }); + }, []); const tokenizeMiteiru = useCallback(async (sentence) => { - return await ipcRenderer.invoke('tokenizeUsingKuromoji', sentence); + if (tokenizerMode === '') return ''; + if (tokenizerMode === 'kuromoji') return await ipcRenderer.invoke('tokenizeUsingKuromoji', sentence); + return getFurigana(sentence, tokenizerMode); }, []) - return {tokenizeMiteiru}; -} + return {tokenizeMiteiru, tokenizerMode}; +}; -export default useMiteiruTokenizer; \ No newline at end of file +export default useMiteiruTokenizer; diff --git a/renderer/pages/home.tsx b/renderer/pages/home.tsx index 94ad03b..4f97fee 100644 --- a/renderer/pages/home.tsx +++ b/renderer/pages/home.tsx @@ -32,7 +32,7 @@ function Home() { const handleClick = useCallback(async () => { if (!isUsingMecab) { setCheck(checkingMessage); - const res = await ipcRenderer.invoke('loadDefaultJmdict'); + const res = await ipcRenderer.invoke('loadDefaultMode'); setCheck(res); if (res.ok !== 1) { return; diff --git a/renderer/pages/learn.tsx b/renderer/pages/learn.tsx index 4dc6a15..acc24bc 100644 --- a/renderer/pages/learn.tsx +++ b/renderer/pages/learn.tsx @@ -18,7 +18,7 @@ function Learn() { const [primaryStyling, setPrimaryStyling] = useState(defaultPrimarySubtitleStyling); const [directInput, setDirectInput] = useState(''); useEffect(() => { - ipcRenderer.invoke('getMecabCommand').then(val => { + ipcRenderer.invoke('getTokenizerMode').then(val => { setMecab(val) }) }, []); diff --git a/renderer/pages/video.tsx b/renderer/pages/video.tsx index 1a1113b..15f9d57 100644 --- a/renderer/pages/video.tsx +++ b/renderer/pages/video.tsx @@ -8,7 +8,6 @@ import Toast from "../components/Toast"; import {Sidebar} from "../components/Sidebar"; import useKeyBind from "../hooks/useKeyBind"; import useSubtitle from "../hooks/useSubtitle"; -import useMecab from "../hooks/useMecab"; import useLoadFiles from "../hooks/useLoadFiles"; import useMenuDisplay from "../hooks/useMenuDisplay"; import useReadyPlayerCallback from "../hooks/useReadyPlayerCallback"; @@ -22,9 +21,10 @@ import { useVideoTimeChanger } from "../hooks/useVideoController"; import {usePlayNextAfterEnd} from "../hooks/usePlayNextAfterEnd"; +import useMiteiruTokenizer from "../hooks/useMiteiruTokenizer"; function Video() { - const mecab = useMecab(); + const {tokenizerMode, tokenizeMiteiru} = useMiteiruTokenizer(); const {toastInfo, setToastInfo} = useMiteiruToast(); const { readyCallback, @@ -59,7 +59,7 @@ function Video() { useLoadFiles(setToastInfo, primarySub, setPrimarySub, secondarySub, setSecondarySub, - mecab, setEnableSeeker, changeTimeTo, player); + tokenizeMiteiru, setEnableSeeker, changeTimeTo, player); const {showController, setShowController, showSidebar, setShowSidebar} = useMenuDisplay(); useKeyBind(setMeaning, setShowController, setShowSidebar, setPrimarySub, setSecondarySub, primarySub); const {togglePlay, isPlaying} = useVideoPlayingToggle(player, metadata); @@ -72,7 +72,7 @@ function Video() {
- + }
- {mecab !== '' && } + {tokenizerMode !== '' && }
Date: Mon, 24 Jul 2023 22:35:15 +0700 Subject: [PATCH 07/15] [#32] Refactor DS and meaning box --- renderer/components/DataStructures.ts | 8 ++++---- renderer/components/MeaningBox.tsx | 1 - renderer/hooks/useLoadFiles.tsx | 8 ++++---- renderer/hooks/useMiteiruTokenizer.tsx | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/renderer/components/DataStructures.ts b/renderer/components/DataStructures.ts index 564ff9c..ed85c8b 100644 --- a/renderer/components/DataStructures.ts +++ b/renderer/components/DataStructures.ts @@ -33,8 +33,8 @@ export class Line { this.content = strContent; } - fillContentFurigana(mecab) { - this.content = getFurigana(this.content as string, mecab); + async fillContentFurigana(tokenizeMiteiru: (string) => Promise) { + this.content = await tokenizeMiteiru(this.content as string); } async fillContentWithLearningKotoba() { @@ -133,11 +133,11 @@ export class SubtitleContainer { return subtitleContainer } - async adjustJapanese(mecab) { + async adjustJapanese(tokenizeMiteiru: (string) => Promise) { for (let i = 0; i < this.lines.length; i++) { if (globalSubtitleId !== this.id) return; const line = this.lines[i]; - await line.fillContentFurigana(mecab) + await line.fillContentFurigana(tokenizeMiteiru) await line.fillContentWithLearningKotoba(); this.progress = `${((i + 1) * 100 / this.lines.length).toFixed(2)}%`; } diff --git a/renderer/components/MeaningBox.tsx b/renderer/components/MeaningBox.tsx index d2ecbbb..9356fb8 100644 --- a/renderer/components/MeaningBox.tsx +++ b/renderer/components/MeaningBox.tsx @@ -1,6 +1,5 @@ import React, {useEffect, useState} from "react"; import {ipcRenderer} from "electron"; -import {getFurigana} from "shunou"; import {Sentence} from "./Sentence"; import {CJKStyling, defaultMeaningBoxStyling} from "../utils/CJKStyling"; diff --git a/renderer/hooks/useLoadFiles.tsx b/renderer/hooks/useLoadFiles.tsx index 9d77a96..f316e11 100644 --- a/renderer/hooks/useLoadFiles.tsx +++ b/renderer/hooks/useLoadFiles.tsx @@ -6,12 +6,12 @@ import {isSubtitle, isVideo} from "../utils/utils"; import {findPositionDeltaInFolder} from "../utils/folderUtils"; import {useAsyncAwaitQueue} from "./useAsyncAwaitQueue"; -const useLoadFiles = (setToastInfo, primarySub, setPrimarySub, secondarySub, setSecondarySub, mecab, setEnableSeeker, changeTimeTo, player) => { +const useLoadFiles = (setToastInfo, primarySub, setPrimarySub, secondarySub, setSecondarySub, tokenizeMiteiru, setEnableSeeker, changeTimeTo, player) => { const [videoSrc, setVideoSrc] = useState({src: '', type: '', path: ''}); const queue = useAsyncAwaitQueue(); const resetSub = useCallback((subSetter) => { subSetter(new SubtitleContainer('')); - }, [mecab]); + }, [tokenizeMiteiru]); const onLoadFiles = useCallback(async acceptedFiles => { const currentHash = Symbol(); await queue.wait(currentHash); @@ -55,7 +55,7 @@ const useLoadFiles = (setToastInfo, primarySub, setPrimarySub, secondarySub, set update: randomUUID() }); }, TOAST_TIMEOUT / 10); - tmpSub.adjustJapanese(mecab).then(() => { + tmpSub.adjustJapanese(tokenizeMiteiru).then(() => { clearInterval(toastSetter); }) } @@ -71,7 +71,7 @@ const useLoadFiles = (setToastInfo, primarySub, setPrimarySub, secondarySub, set resetSub(setSecondarySub) } await queue.end(currentHash); - }, [mecab]); + }, [tokenizeMiteiru]); const onVideoChangeHandler = useCallback(async (delta: number = 1) => { if (videoSrc.path) { const nextVideo = findPositionDeltaInFolder(videoSrc.path, delta); diff --git a/renderer/hooks/useMiteiruTokenizer.tsx b/renderer/hooks/useMiteiruTokenizer.tsx index a29bf55..0a5cc1b 100644 --- a/renderer/hooks/useMiteiruTokenizer.tsx +++ b/renderer/hooks/useMiteiruTokenizer.tsx @@ -14,7 +14,7 @@ const useMiteiruTokenizer = (): { tokenizeMiteiru: (sentence: string) => Promise if (tokenizerMode === '') return ''; if (tokenizerMode === 'kuromoji') return await ipcRenderer.invoke('tokenizeUsingKuromoji', sentence); return getFurigana(sentence, tokenizerMode); - }, []) + }, [tokenizerMode]) return {tokenizeMiteiru, tokenizerMode}; }; From dce57239e00daed6745ca1d3894333df88440741 Mon Sep 17 00:00:00 2001 From: Hocky Yudhiono Date: Mon, 24 Jul 2023 23:23:42 +0700 Subject: [PATCH 08/15] [#32] Fix meaning box bug --- main/background.ts | 1 - package-lock.json | 8 ++-- package.json | 2 +- renderer/components/MeaningBox.tsx | 58 ++++++++++++++++---------- renderer/components/Sentence.tsx | 10 ++--- renderer/hooks/useMiteiruTokenizer.tsx | 13 ++++-- renderer/pages/learn.tsx | 5 ++- renderer/utils/utils.tsx | 9 ++++ 8 files changed, 67 insertions(+), 39 deletions(-) diff --git a/main/background.ts b/main/background.ts index e14fbc8..4b3799b 100644 --- a/main/background.ts +++ b/main/background.ts @@ -274,7 +274,6 @@ if (isProd) { console.log(e) }) ipcMain.handle('tokenizeUsingKuromoji', async (event, sentence) => { - console.log(sentence) return tokenizer.tokenizeForSentence(sentence); }); protocol.registerFileProtocol(scheme, requestHandler); /* eng-disable PROTOCOL_HANDLER_JS_CHECK */ diff --git a/package-lock.json b/package-lock.json index b276c0a..f0e00c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "react-dropzone": "^14.2.3", "react-smooth-collapse": "^2.1.2", "react-video-seek-slider": "^6.0.4", - "shunou": "^0.0.36", + "shunou": "^1.0.2", "styled-components": "^6.0.3", "video.js": "^8.3.0" }, @@ -10510,9 +10510,9 @@ } }, "node_modules/shunou": { - "version": "0.0.36", - "resolved": "https://registry.npmjs.org/shunou/-/shunou-0.0.36.tgz", - "integrity": "sha512-Z1xESqJ5ho8/ARihLQ1sTMz3YG+xtqpARVSSHTNX4mcHVoRfW1rqIwsF3oZoJeHDEUPl/RXQyuWyBAP93Kehkw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shunou/-/shunou-1.0.2.tgz", + "integrity": "sha512-Pdj1/8MGVIF/gm5U7E+4mvQUJIgMJQOoKKS1tRCgVyHgYxKhGNruqlnyU7UIdxerv/I9tbKntRfHCQOoOO0rkg==", "dependencies": { "wanakana": "^5.0.2" } diff --git a/package.json b/package.json index 49d4b02..b7d53df 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "react-dropzone": "^14.2.3", "react-smooth-collapse": "^2.1.2", "react-video-seek-slider": "^6.0.4", - "shunou": "^0.0.36", + "shunou": "^1.0.2", "styled-components": "^6.0.3", "video.js": "^8.3.0" }, diff --git a/renderer/components/MeaningBox.tsx b/renderer/components/MeaningBox.tsx index 9356fb8..c7f448c 100644 --- a/renderer/components/MeaningBox.tsx +++ b/renderer/components/MeaningBox.tsx @@ -2,6 +2,7 @@ import React, {useEffect, useState} from "react"; import {ipcRenderer} from "electron"; import {Sentence} from "./Sentence"; import {CJKStyling, defaultMeaningBoxStyling} from "../utils/CJKStyling"; +import {joinString} from "../utils/utils"; const initialContentState = {sense: [], kanji: []}; @@ -33,14 +34,25 @@ const MeaningBox = ({ setTags(val) }) }, [meaning]); - const joinString = (arr, separator = '; ') => { - let total = ""; - arr.forEach(val => { - if (total !== '') total += separator - total += val.toString(); - }) - return total; - } + + + const [furiganizedData, setFuriganizedData] = useState([]); + + useEffect(() => { + const fetchData = async () => { + const data = await Promise.all(meaningContent.kanji.map(async (val) => { + const furiganized = await tokenizeMiteiru(val.text); + return { + key: val.key, + furiganized + }; + })); + + setFuriganizedData(data); + }; + if (meaningContent.kanji.length) fetchData(); + }, [meaningContent.kanji]); // Add your dependencies here + if (meaningContent.kanji.length > 0) { return (
{ @@ -68,23 +80,23 @@ const MeaningBox = ({
{meaningContent.kanji.map(async (val, meanKey) => { - const furiganized = await tokenizeMiteiru(val.text); - return ( -
+ {furiganizedData.map(({key, furiganized}) => ( +
- {[...furiganized.map((val, idx) => { - return () - })]} -
); - })}
+ {[...furiganized.map((val, idx) => ( + + ))]} +
+ ))} +
{meaningIndex + 1 < otherMeanings.length && - < button className={"bg-blue-800 p-3 rounded-md m-4"} onClick={(e) => { +