diff --git a/electron/main/buffer.js b/electron/main/buffer.js
index b8799dd3..0ecc5a75 100644
--- a/electron/main/buffer.js
+++ b/electron/main/buffer.js
@@ -16,15 +16,15 @@ const untildify = (pathWithTilde) => {
: pathWithTilde;
}
-export function constructBufferFilePath(directoryPath) {
- return join(untildify(directoryPath), isDev ? "buffer-dev.txt" : "buffer.txt")
+export function constructBufferFilePath(directoryPath, path) {
+ return join(untildify(directoryPath), path)
}
-export function getBufferFilePath() {
+export function getFullBufferFilePath(path) {
let defaultPath = app.getPath("userData")
let configPath = CONFIG.get("settings.bufferPath")
let bufferPath = configPath.length ? configPath : defaultPath
- let bufferFilePath = constructBufferFilePath(bufferPath)
+ let bufferFilePath = constructBufferFilePath(bufferPath, path)
try {
// use realpathSync to resolve a potential symlink
return fs.realpathSync(bufferFilePath)
@@ -103,39 +103,45 @@ export class Buffer {
// Buffer
-let buffer
-export function loadBuffer() {
- if (buffer) {
- buffer.close()
+let buffers = {}
+export function loadBuffer(path) {
+ if (buffers[path]) {
+ buffers[path].close()
}
- buffer = new Buffer({
- filePath: getBufferFilePath(),
+ buffers[path] = new Buffer({
+ filePath: getFullBufferFilePath(path),
onChange: (content) => {
- win?.webContents.send("buffer-content:change", content)
+ console.log("Old buffer.js onChange")
+ win?.webContents.send("buffer-content:change", path, content)
},
})
- return buffer
+ return buffers[path]
}
-ipcMain.handle('buffer-content:load', async () => {
- if (buffer.exists() && !(eraseInitialContent && isDev)) {
- return await buffer.load()
+ipcMain.handle('buffer-content:load', async (event, path) => {
+ if (!buffers[path]) {
+ loadBuffer(path)
+ }
+ if (buffers[path].exists() && !(eraseInitialContent && isDev)) {
+ return await buffers[path].load()
} else {
return isDev ? initialDevContent : initialContent
}
});
-async function save(content) {
- return await buffer.save(content)
+async function save(path, content) {
+ return await buffers[path].save(content)
}
-ipcMain.handle('buffer-content:save', async (event, content) => {
- return await save(content)
+ipcMain.handle('buffer-content:save', async (event, path, content) => {
+ return await save(path, content)
});
export let contentSaved = false
-ipcMain.handle('buffer-content:saveAndQuit', async (event, content) => {
- await save(content)
+ipcMain.handle('buffer-content:saveAndQuit', async (event, contents) => {
+ for (const [path, content] of contents) {
+ await save(path, content)
+ }
contentSaved = true
app.quit()
})
diff --git a/electron/main/file-library.js b/electron/main/file-library.js
new file mode 100644
index 00000000..d7c365b0
--- /dev/null
+++ b/electron/main/file-library.js
@@ -0,0 +1,166 @@
+import fs from "fs"
+import os from "node:os"
+import { join, dirname, basename } from "path"
+
+import * as jetpack from "fs-jetpack";
+import { app, ipcMain, dialog } from "electron"
+
+
+const untildify = (pathWithTilde) => {
+ const homeDir = os.homedir()
+ return homeDir ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDir) : pathWithTilde
+}
+
+
+export class FileLibrary {
+ constructor(basePath) {
+ this.basePath = fs.realpathSync(untildify(basePath))
+ this.jetpack = jetpack.cwd(this.basePath)
+ this.files = {};
+ this.watcher = null;
+ this.contentSaved = false
+ this.onChangeCallback = null
+
+ if (jetpack.exists(this.basePath) !== "dir") {
+ throw new Error(`Invalid base path: ${this.basePath}`)
+ }
+
+ this.setupWatcher()
+ }
+
+ async exists(path) {
+ return this.jetpack.exists(path) === "file"
+ }
+
+ async load(path) {
+ if (this.files[path]) {
+ return this.files[path].read()
+ }
+ const fullPath = fs.realpathSync(join(this.basePath, path))
+ this.files[path] = new Buffer({fullPath, library:this})
+ return await this.files[path].read()
+ }
+
+ async save(path, content) {
+ if (!this.files[path]) {
+ throw new Error(`File not loaded: ${path}`)
+ }
+ return await this.files[path].save(content)
+ }
+
+ async listFiles() {
+ return await this.jetpack.findAsync(this.basePath, {
+ matching: "*.txt",
+ recursive: true,
+ })
+ }
+
+ setupWatcher() {
+ if (!this.watcher) {
+ this.watcher = fs.watch(
+ this.basePath,
+ {
+ persistent: true,
+ recursive: true,
+ encoding: "utf8",
+ },
+ async (eventType, changedPath) => {
+ console.log("File changed", eventType, changedPath)
+ for (const [path, buffer] of Object.entries(this.files)) {
+ if (changedPath === basename(path)) {
+ const content = await buffer.read()
+ if (buffer._lastSavedContent !== content) {
+ this.onChangeCallback(path, content)
+ }
+ }
+ }
+ }
+ )
+ }
+ }
+
+ closeFile(path) {
+ if (this.files[path]) {
+ delete this.files[path]
+ }
+ }
+
+ close() {
+ for (const buffer of Object.values(this.files)) {
+ this.closeFile(buffer.filePath)
+ }
+ this.stopWatcher()
+ }
+
+ stopWatcher() {
+ if (this.watcher) {
+ this.watcher.close()
+ this.watcher = null
+ }
+ }
+}
+
+
+
+export class Buffer {
+ constructor({fullPath, library}) {
+ this.fullPath = fullPath
+ this._lastSavedContent = null
+ this.library = library
+ }
+
+ async read() {
+ return await this.library.jetpack.read(this.fullPath, 'utf8')
+ }
+
+ async save(content) {
+ this._lastSavedContent = content
+ const saveResult = await this.library.jetpack.write(this.fullPath, content, {
+ atomic: true,
+ mode: '600',
+ })
+ return saveResult
+ }
+
+ exists() {
+ return jetpack.exists(this.fullPath) === "file"
+ }
+}
+
+
+export function setupFileLibraryEventHandlers(library, win) {
+ ipcMain.handle('buffer:load', async (event, path) => {
+ console.log("buffer:load", path)
+ return await library.load(path)
+ });
+
+
+ ipcMain.handle('buffer:save', async (event, path, content) => {
+ return await library.save(path, content)
+ });
+
+ ipcMain.handle('buffer:listFiles', async (event) => {
+ return await library.listFiles()
+ });
+
+ ipcMain.handle('buffer:exists', async (event, path) => {
+ return await library.exists(path)
+ });
+
+ ipcMain.handle('buffer:close', async (event, path) => {
+ return await library.closeFile(path)
+ });
+
+ ipcMain.handle('buffer:saveAndQuit', async (event, contents) => {
+ library.stopWatcher()
+ for (const [path, content] of contents) {
+ await library.save(path, content)
+ }
+ library.contentSaved = true
+ app.quit()
+ })
+
+ library.onChangeCallback = (path, content) => {
+ win.webContents.send("buffer:change", path, content)
+ }
+}
diff --git a/electron/main/index.ts b/electron/main/index.ts
index dd7a26f4..efd5fd28 100644
--- a/electron/main/index.ts
+++ b/electron/main/index.ts
@@ -10,6 +10,7 @@ import { isDev, isLinux, isMac, isWindows } from '../detect-platform';
import { initializeAutoUpdate, checkForUpdates } from './auto-update';
import { fixElectronCors } from './cors';
import { loadBuffer, contentSaved } from './buffer';
+import { FileLibrary, setupFileLibraryEventHandlers } from './file-library';
// The built directory structure
@@ -49,7 +50,9 @@ Menu.setApplicationMenu(menu)
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
export let win: BrowserWindow | null = null
+let fileLibrary: FileLibrary | null = null
let tray: Tray | null = null;
+let initErrors: string[] = []
// Here, you can also use other preload
const preload = join(__dirname, '../preload/index.js')
const url = process.env.VITE_DEV_SERVER_URL
@@ -138,7 +141,7 @@ async function createWindow() {
}
// Prevent the window from closing, and send a message to the renderer which will in turn
// send a message to the main process to save the current buffer and close the window.
- if (!contentSaved) {
+ if (!fileLibrary.contentSaved) {
event.preventDefault()
win?.webContents.send(WINDOW_CLOSE_EVENT)
} else {
@@ -302,6 +305,7 @@ function registerAlwaysOnTop() {
}
app.whenReady().then(createWindow).then(async () => {
+ setupFileLibraryEventHandlers(fileLibrary, win)
initializeAutoUpdate(win)
registerGlobalHotkey()
registerShowInDock()
@@ -343,8 +347,19 @@ ipcMain.handle('dark-mode:set', (event, mode) => {
ipcMain.handle('dark-mode:get', () => nativeTheme.themeSource)
-// load buffer on app start
-loadBuffer()
+// Initialize note/file library
+const customLibraryPath = CONFIG.get("settings.bufferPath")
+const libraryPath = join(app.getPath("userData"), "notes")
+console.log("libraryPath", libraryPath)
+fileLibrary = new FileLibrary(libraryPath)
+fileLibrary.listFiles().then((files) => {
+ console.log("files", files)
+})
+initErrors.push("Could not load file library")
+
+ipcMain.handle("getInitErrors", () => {
+ return initErrors
+})
ipcMain.handle('settings:set', async (event, settings) => {
diff --git a/electron/preload/index.ts b/electron/preload/index.ts
index 64e82629..07c0f832 100644
--- a/electron/preload/index.ts
+++ b/electron/preload/index.ts
@@ -1,6 +1,6 @@
const { contextBridge } = require('electron')
import themeMode from "./theme-mode"
-import { isMac, isWindows, isLinux } from "../detect-platform"
+import { isMac, isWindows, isLinux, isDev } from "../detect-platform"
import { ipcRenderer } from "electron"
import {
WINDOW_CLOSE_EVENT,
@@ -29,9 +29,20 @@ contextBridge.exposeInMainWorld("heynote", {
isLinux,
isWebApp: false,
},
-
+
+ isDev: isDev,
themeMode: themeMode,
+ init() {
+ ipcRenderer.on("buffer:change", (event, path, content) => {
+ // called on all changes to open buffer files
+ // go through all registered callbacks for this path and call them
+ if (this.buffer._onChangeCallbacks[path]) {
+ this.buffer._onChangeCallbacks[path].forEach(callback => callback(content))
+ }
+ })
+ },
+
quit() {
console.log("quitting")
//ipcRenderer.invoke("app_quit")
@@ -46,20 +57,42 @@ contextBridge.exposeInMainWorld("heynote", {
},
buffer: {
- async load() {
- return await ipcRenderer.invoke("buffer-content:load")
+ async exists(path) {
+ return await ipcRenderer.invoke("buffer:exists", path)
+ },
+
+ async listFiles(path) {
+ return await ipcRenderer.invoke("buffer:listFiles")
+ },
+
+ async load(path) {
+ return await ipcRenderer.invoke("buffer:load", path)
},
- async save(content) {
- return await ipcRenderer.invoke("buffer-content:save", content)
+ async save(path, content) {
+ return await ipcRenderer.invoke("buffer:save", path, content)
},
- async saveAndQuit(content) {
- return await ipcRenderer.invoke("buffer-content:saveAndQuit", content)
+ async saveAndQuit(contents) {
+ return await ipcRenderer.invoke("buffer:saveAndQuit", contents)
},
- onChangeCallback(callback) {
- ipcRenderer.on("buffer-content:change", callback)
+ async close(path) {
+ return await ipcRenderer.invoke("buffer:close", path)
+ },
+
+ _onChangeCallbacks: {},
+ addOnChangeCallback(path, callback) {
+ // register a callback to be called when the buffer content changes for a specific file
+ if (!this._onChangeCallbacks[path]) {
+ this._onChangeCallbacks[path] = []
+ }
+ this._onChangeCallbacks[path].push(callback)
+ },
+ removeOnChangeCallback(path, callback) {
+ if (this._onChangeCallbacks[path]) {
+ this._onChangeCallbacks[path] = this._onChangeCallbacks[path].filter(cb => cb !== callback)
+ }
},
async selectLocation() {
@@ -103,7 +136,11 @@ contextBridge.exposeInMainWorld("heynote", {
async getVersion() {
return await ipcRenderer.invoke("getVersion")
- }
+ },
+
+ async getInitErrors() {
+ return await ipcRenderer.invoke("getInitErrors")
+ },
})
diff --git a/package-lock.json b/package-lock.json
index 810c8fbf..34812c29 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
"license": "Commons Clause MIT",
"dependencies": {
"electron-log": "^5.0.1",
+ "pinia": "^2.1.7",
"semver": "^7.6.3"
},
"devDependencies": {
@@ -242,7 +243,6 @@
"version": "7.23.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz",
"integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==",
- "dev": true,
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -1013,8 +1013,7 @@
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
- "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
- "dev": true
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"node_modules/@lezer/common": {
"version": "1.2.1",
@@ -1732,7 +1731,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.11.tgz",
"integrity": "sha512-h97/TGWBilnLuRaj58sxNrsUU66fwdRKLOLQ9N/5iNDfp+DZhYH9Obhe0bXxhedl8fjAgpRANpiZfbgWyruQ0w==",
- "dev": true,
"dependencies": {
"@babel/parser": "^7.23.5",
"@vue/shared": "3.3.11",
@@ -1744,7 +1742,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.11.tgz",
"integrity": "sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw==",
- "dev": true,
"dependencies": {
"@vue/compiler-core": "3.3.11",
"@vue/shared": "3.3.11"
@@ -1754,7 +1751,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.11.tgz",
"integrity": "sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==",
- "dev": true,
"dependencies": {
"@babel/parser": "^7.23.5",
"@vue/compiler-core": "3.3.11",
@@ -1772,12 +1768,16 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.11.tgz",
"integrity": "sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==",
- "dev": true,
"dependencies": {
"@vue/compiler-dom": "3.3.11",
"@vue/shared": "3.3.11"
}
},
+ "node_modules/@vue/devtools-api": {
+ "version": "6.6.3",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.3.tgz",
+ "integrity": "sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw=="
+ },
"node_modules/@vue/language-core": {
"version": "1.8.25",
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.25.tgz",
@@ -1831,7 +1831,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.11.tgz",
"integrity": "sha512-D5tcw091f0nuu+hXq5XANofD0OXnBmaRqMYl5B3fCR+mX+cXJIGNw/VNawBqkjLNWETrFW0i+xH9NvDbTPVh7g==",
- "dev": true,
"dependencies": {
"@vue/shared": "3.3.11"
}
@@ -1840,7 +1839,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.11.tgz",
"integrity": "sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg==",
- "dev": true,
"dependencies": {
"@babel/parser": "^7.23.5",
"@vue/compiler-core": "3.3.11",
@@ -1853,7 +1851,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.11.tgz",
"integrity": "sha512-g9ztHGwEbS5RyWaOpXuyIVFTschclnwhqEbdy5AwGhYOgc7m/q3NFwr50MirZwTTzX55JY8pSkeib9BX04NIpw==",
- "dev": true,
"dependencies": {
"@vue/reactivity": "3.3.11",
"@vue/shared": "3.3.11"
@@ -1863,7 +1860,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.11.tgz",
"integrity": "sha512-OlhtV1PVpbgk+I2zl+Y5rQtDNcCDs12rsRg71XwaA2/Rbllw6mBLMi57VOn8G0AjOJ4Mdb4k56V37+g8ukShpQ==",
- "dev": true,
"dependencies": {
"@vue/runtime-core": "3.3.11",
"@vue/shared": "3.3.11",
@@ -1874,7 +1870,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.11.tgz",
"integrity": "sha512-AIWk0VwwxCAm4wqtJyxBylRTXSy1wCLOKbWxHaHiu14wjsNYtiRCSgVuqEPVuDpErOlRdNnuRgipQfXRLjLN5A==",
- "dev": true,
"dependencies": {
"@vue/compiler-ssr": "3.3.11",
"@vue/shared": "3.3.11"
@@ -1886,8 +1881,7 @@
"node_modules/@vue/shared": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.11.tgz",
- "integrity": "sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw==",
- "dev": true
+ "integrity": "sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw=="
},
"node_modules/@xmldom/xmldom": {
"version": "0.8.10",
@@ -2756,8 +2750,7 @@
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/de-indent": {
"version": "1.0.2",
@@ -3570,8 +3563,7 @@
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
- "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "dev": true
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"node_modules/extract-zip": {
"version": "2.0.1",
@@ -4444,7 +4436,6 @@
"version": "0.30.5",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
- "dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
},
@@ -4611,7 +4602,6 @@
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -4846,8 +4836,7 @@
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
- "dev": true
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -4861,6 +4850,56 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/pinia": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz",
+ "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
+ "dependencies": {
+ "@vue/devtools-api": "^6.5.0",
+ "vue-demi": ">=0.14.5"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.4.0",
+ "typescript": ">=4.4.4",
+ "vue": "^2.6.14 || ^3.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pinia/node_modules/vue-demi": {
+ "version": "0.14.8",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
+ "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
+ "hasInstallScript": true,
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
"node_modules/pkg-up": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
@@ -4935,7 +4974,6 @@
"version": "8.4.32",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -5443,7 +5481,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -5749,7 +5786,7 @@
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
- "dev": true,
+ "devOptional": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -6295,7 +6332,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.11.tgz",
"integrity": "sha512-d4oBctG92CRO1cQfVBZp6WJAs0n8AK4Xf5fNjQCBeKCvMI1efGQ5E3Alt1slFJS9fZuPcFoiAiqFvQlv1X7t/w==",
- "dev": true,
"dependencies": {
"@vue/compiler-dom": "3.3.11",
"@vue/compiler-sfc": "3.3.11",
diff --git a/package.json b/package.json
index d3186170..1688cc12 100644
--- a/package.json
+++ b/package.json
@@ -79,6 +79,7 @@
},
"dependencies": {
"electron-log": "^5.0.1",
+ "pinia": "^2.1.7",
"semver": "^7.6.3"
}
}
diff --git a/playwright.config.ts b/playwright.config.ts
index f4ea00db..173cc961 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -42,15 +42,15 @@ export default defineConfig({
},
},
- {
- name: 'firefox',
- use: { ...devices['Desktop Firefox'] },
- },
-
- {
- name: 'webkit',
- use: { ...devices['Desktop Safari'] },
- },
+ //{
+ // name: 'firefox',
+ // use: { ...devices['Desktop Firefox'] },
+ //},
+ //
+ //{
+ // name: 'webkit',
+ // use: { ...devices['Desktop Safari'] },
+ //},
/* Test against mobile viewports. */
// {
diff --git a/public/langdetect-worker.js b/public/langdetect-worker.js
index a519b4af..15d736f9 100644
--- a/public/langdetect-worker.js
+++ b/public/langdetect-worker.js
@@ -28,6 +28,7 @@ onmessage = (event) => {
},
content: content,
idx: event.data.idx,
+ path: event.data.path,
})
return
}
@@ -53,6 +54,7 @@ onmessage = (event) => {
},
content: content,
idx: event.data.idx,
+ path: event.data.path,
})
return
}
@@ -66,6 +68,7 @@ onmessage = (event) => {
},
content: content,
idx: event.data.idx,
+ path: event.data.path,
})
return
}
diff --git a/src/components/App.vue b/src/components/App.vue
index 08234d50..c6817bc3 100644
--- a/src/components/App.vue
+++ b/src/components/App.vue
@@ -1,8 +1,14 @@
+
+
+
+