[desktop] Fix desktop dev builds - Part 1 (#1063)
- Main change here is removing a submodule and moving to the upstream dependency - Also updated to Prettier 3 The original issue, about yarn dev not working because of context isolation, still remains. This PR prepares the ground, will have a go at it in a subsequent PR.
This commit is contained in:
commit
9c04a7102b
48 changed files with 324 additions and 340 deletions
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -9,10 +9,6 @@
|
|||
[submodule "auth/assets/simple-icons"]
|
||||
path = auth/assets/simple-icons
|
||||
url = https://github.com/simple-icons/simple-icons.git
|
||||
[submodule "desktop/thirdparty/next-electron-server"]
|
||||
path = desktop/thirdparty/next-electron-server
|
||||
url = https://github.com/ente-io/next-electron-server.git
|
||||
branch = desktop
|
||||
[submodule "mobile/thirdparty/flutter"]
|
||||
path = mobile/thirdparty/flutter
|
||||
url = https://github.com/flutter/flutter.git
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
thirdparty/
|
|
@ -4,128 +4,128 @@
|
|||
|
||||
### New
|
||||
|
||||
- Option to select file download location.
|
||||
- Add support for searching popular cities
|
||||
- Sorted duplicates in desecending order of size
|
||||
- Add Counter to upload section
|
||||
- Display full name and collection name on hover on dedupe screen photos
|
||||
- Option to select file download location.
|
||||
- Add support for searching popular cities
|
||||
- Sorted duplicates in desecending order of size
|
||||
- Add Counter to upload section
|
||||
- Display full name and collection name on hover on dedupe screen photos
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix add to album padding issue
|
||||
- Fix double uncategorized album issue
|
||||
- Hide Hidden collection files from all section
|
||||
- Fix add to album padding issue
|
||||
- Fix double uncategorized album issue
|
||||
- Hide Hidden collection files from all section
|
||||
|
||||
## v1.6.62
|
||||
|
||||
### New
|
||||
|
||||
- Integrated onnx clip runner
|
||||
- Integrated onnx clip runner
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixes login button requiring double click issue
|
||||
- Fixes Collection sort state not preserved issue
|
||||
- Fixes continuous export causing app crash
|
||||
- Improves ML related copies for better distinction from clip
|
||||
- Added Better favicon for light mode
|
||||
- Fixed face indexing issues
|
||||
- Fixed thumbnail load issue
|
||||
- Fixes login button requiring double click issue
|
||||
- Fixes Collection sort state not preserved issue
|
||||
- Fixes continuous export causing app crash
|
||||
- Improves ML related copies for better distinction from clip
|
||||
- Added Better favicon for light mode
|
||||
- Fixed face indexing issues
|
||||
- Fixed thumbnail load issue
|
||||
|
||||
## v1.6.60
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix Thumbnail Orientation issue
|
||||
- Fix ML logging issue
|
||||
- Fix Thumbnail Orientation issue
|
||||
- Fix ML logging issue
|
||||
|
||||
## v1.6.59
|
||||
|
||||
### New
|
||||
|
||||
- Added arm64 builds for linux
|
||||
- Added arm64 builds for linux
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix Editor file not loading issue
|
||||
- Fix ML results missing thumbnail issue
|
||||
- Fix Editor file not loading issue
|
||||
- Fix ML results missing thumbnail issue
|
||||
|
||||
## v1.6.58
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix File load issue
|
||||
- Fix File load issue
|
||||
|
||||
## v1.6.57
|
||||
|
||||
### New Features
|
||||
|
||||
- Added encrypted Disk caching for files
|
||||
- Added option to customize cache folder location
|
||||
- Added encrypted Disk caching for files
|
||||
- Added option to customize cache folder location
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed caching issue,causing multiple download of file during ml sync
|
||||
- Fixed caching issue,causing multiple download of file during ml sync
|
||||
|
||||
## v1.6.55
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Added manage family portal option if add-on is active
|
||||
- Fixed filename date parsing issue
|
||||
- Fixed storage limit ui glitch
|
||||
- Fixed dedupe page layout issue
|
||||
- Fixed ElectronAPI refactoring issue
|
||||
- Fixed Search related issues
|
||||
- Added manage family portal option if add-on is active
|
||||
- Fixed filename date parsing issue
|
||||
- Fixed storage limit ui glitch
|
||||
- Fixed dedupe page layout issue
|
||||
- Fixed ElectronAPI refactoring issue
|
||||
- Fixed Search related issues
|
||||
|
||||
## v1.6.54
|
||||
|
||||
### New Features
|
||||
|
||||
- Added support for HEIC and raw image in photo editor
|
||||
- Added support for HEIC and raw image in photo editor
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed 16bit HDR HEIC images support
|
||||
- Fixed blocked login due safe storage issue
|
||||
- Fixed Search related issues
|
||||
- Fixed issue of watch folder not cleared on logout
|
||||
- other under the hood ui/ux improvements
|
||||
- Fixed 16bit HDR HEIC images support
|
||||
- Fixed blocked login due safe storage issue
|
||||
- Fixed Search related issues
|
||||
- Fixed issue of watch folder not cleared on logout
|
||||
- other under the hood ui/ux improvements
|
||||
|
||||
## v1.6.53
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed watch folder disabled issue
|
||||
- Fixed BF Add on related issues
|
||||
- Fixed clip sync issue and added better logging
|
||||
- Fixed mov file upload
|
||||
- Fixed clip extraction related issue
|
||||
- Fixed watch folder disabled issue
|
||||
- Fixed BF Add on related issues
|
||||
- Fixed clip sync issue and added better logging
|
||||
- Fixed mov file upload
|
||||
- Fixed clip extraction related issue
|
||||
|
||||
## v1.6.52
|
||||
|
||||
### New Features
|
||||
|
||||
- Added Clip Desktop on windows
|
||||
- Added Clip Desktop on windows
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- fixed google json matching issue
|
||||
- other under-the-hood changes to improve performance and bug fixes
|
||||
- fixed google json matching issue
|
||||
- other under-the-hood changes to improve performance and bug fixes
|
||||
|
||||
## v1.6.50
|
||||
|
||||
### New Features
|
||||
|
||||
- Added Clip desktop
|
||||
- Added Clip desktop
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed desktop downloaded file had extra dot in the name
|
||||
- Cleanup error messages
|
||||
- fix the motion photo clustering issue
|
||||
- Add option to disable cf proxy locally
|
||||
- other under-the-hood changes to improve UX
|
||||
- Fixed desktop downloaded file had extra dot in the name
|
||||
- Cleanup error messages
|
||||
- fix the motion photo clustering issue
|
||||
- Add option to disable cf proxy locally
|
||||
- other under-the-hood changes to improve UX
|
||||
|
||||
## v1.6.49
|
||||
|
||||
|
@ -137,54 +137,54 @@ Check out our [blog](https://ente.io/blog/introducing-web-desktop-photo-editor/)
|
|||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed misaligned icons in photo-viewer
|
||||
- Fixed issue with Motion photo upload
|
||||
- Fixed issue with Live-photo upload
|
||||
- other minor ux improvement
|
||||
- Fixed misaligned icons in photo-viewer
|
||||
- Fixed issue with Motion photo upload
|
||||
- Fixed issue with Live-photo upload
|
||||
- other minor ux improvement
|
||||
|
||||
## v1.6.46
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixes OOM crashes during file upload [#1379](https://github.com/ente-io/photos-web/pull/1379)
|
||||
- Fixes OOM crashes during file upload [#1379](https://github.com/ente-io/photos-web/pull/1379)
|
||||
|
||||
## v1.6.45
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed app keeps reloading issue [#235](https://github.com/ente-io/photos-desktop/pull/235)
|
||||
- Fixed dng and arw preview issue [#1378](https://github.com/ente-io/photos-web/pull/1378)
|
||||
- Added view crash report option (help menu) for user to share electron crash report locally
|
||||
- Fixed app keeps reloading issue [#235](https://github.com/ente-io/photos-desktop/pull/235)
|
||||
- Fixed dng and arw preview issue [#1378](https://github.com/ente-io/photos-web/pull/1378)
|
||||
- Added view crash report option (help menu) for user to share electron crash report locally
|
||||
|
||||
## v1.6.44
|
||||
|
||||
- Upgraded electron to get latest security patches and other improvements.
|
||||
- Upgraded electron to get latest security patches and other improvements.
|
||||
|
||||
## v1.6.43
|
||||
|
||||
### Added
|
||||
|
||||
- #### Check for update and changelog option
|
||||
- #### Check for update and changelog option
|
||||
|
||||
Added options to check for update manually and a view changelog via the app menubar
|
||||
|
||||
- #### Opt out of crash reporting
|
||||
- #### Opt out of crash reporting
|
||||
|
||||
Added option to out of a crash reporting, it can accessed from the settings -> preferences -> disable crash reporting
|
||||
|
||||
- #### Type search
|
||||
- #### Type search
|
||||
|
||||
Added new search option to search files based on file type i.e, image, video, live-photo.
|
||||
|
||||
- #### Manual Convert Button
|
||||
- #### Manual Convert Button
|
||||
|
||||
In case the video is not playable, Now there is a convert button which can be used to trigger conversion of the video to supported format.
|
||||
|
||||
- #### File Download Progress
|
||||
- #### File Download Progress
|
||||
|
||||
The file loader now also shows the exact percentage download progress, instead of just a simple loader.
|
||||
|
||||
- #### Bug fixes & other enhancements
|
||||
- #### Bug fixes & other enhancements
|
||||
|
||||
We have squashed a few pesky bugs that were reported by our community
|
||||
|
||||
|
@ -192,21 +192,21 @@ Check out our [blog](https://ente.io/blog/introducing-web-desktop-photo-editor/)
|
|||
|
||||
### Added
|
||||
|
||||
- #### Hidden albums
|
||||
- #### Hidden albums
|
||||
|
||||
You can now hide albums, just like individual memories.
|
||||
|
||||
- #### Email verification
|
||||
- #### Email verification
|
||||
|
||||
We have now made email verification optional, so you can sign in with just your email address and password, without waiting for a verification code.
|
||||
|
||||
You can opt in / out of email verification from Settings > Security.
|
||||
|
||||
- #### Download Album
|
||||
- #### Download Album
|
||||
|
||||
You can now chose the download location for downloading albums. Along with that we have also added progress bar for album download.
|
||||
|
||||
- #### Bug fixes & other enhancements
|
||||
- #### Bug fixes & other enhancements
|
||||
|
||||
We have squashed a few pesky bugs that were reported by our community
|
||||
|
||||
|
|
|
@ -10,27 +10,13 @@ To know more about Ente, see [our main README](../README.md) or visit
|
|||
|
||||
## Building from source
|
||||
|
||||
Fetch submodules
|
||||
|
||||
```sh
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
Install dependencies
|
||||
|
||||
```sh
|
||||
yarn install
|
||||
```
|
||||
|
||||
Create a binary for your platform
|
||||
|
||||
```sh
|
||||
yarn build
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
Instead of building, you can run the app in development mode
|
||||
Run in development mode (with hot reload)
|
||||
|
||||
```sh
|
||||
yarn dev
|
||||
|
@ -39,15 +25,13 @@ yarn dev
|
|||
> [!CAUTION]
|
||||
>
|
||||
> `yarn dev` is currently not working (we'll fix soon). If you just want to
|
||||
> build from source and use the generated binary, use `yarn build` as described
|
||||
> above.
|
||||
> build from source and use the generated binary, use `yarn build`.
|
||||
|
||||
This'll launch a development server to serve the pages loaded by the renderer
|
||||
process, and will hot reload on changes.
|
||||
|
||||
If you also want hot reload for the main process, run this in a separate
|
||||
terminal:
|
||||
Or create a binary for your platform
|
||||
|
||||
```sh
|
||||
yarn watch
|
||||
yarn build
|
||||
```
|
||||
|
||||
That's the gist of it. For more development related documentation, see
|
||||
[docs](docs/README.md).
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Electron Updater Example</title>
|
||||
|
|
|
@ -4,3 +4,11 @@ See [web/docs/dependencies.md](../../web/docs/dependencies.md) for general web
|
|||
specific dependencies. See [electron.md](electron.md) for our main dependency,
|
||||
Electron. The rest of this document describes the remaining, desktop specific
|
||||
dependencies that are used by the Photos desktop app.
|
||||
|
||||
## Electron related
|
||||
|
||||
### next-electron-server
|
||||
|
||||
This spins up a server for serving files using a protocol handler inside our
|
||||
Electron process. This allows us to directly use the output produced by `next
|
||||
build` for loading into our renderer process.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Development tips
|
||||
|
||||
* `yarn build:quick` is a variant of `yarn build` that uses the
|
||||
`--config.compression=store` flag to (slightly) speed up electron-builder.
|
||||
- `yarn build:quick` is a variant of `yarn build` that uses the
|
||||
`--config.compression=store` flag to (slightly) speed up electron-builder.
|
||||
|
|
|
@ -7,15 +7,15 @@ Electron embeds Chromium and Node.js in the generated app's binary. The
|
|||
generated app thus consists of two separate processes - the _main_ process, and
|
||||
a _renderer_ process.
|
||||
|
||||
* The _main_ process is runs the embedded node. This process can deal with the
|
||||
host OS - it is conceptually like a `node` repl running on your machine. In our
|
||||
case, the TypeScript code (in the `src/` directory) gets transpiled by `tsc`
|
||||
into JavaScript in the `build/app/` directory, which gets bundled in the
|
||||
generated app's binary and is loaded by the node (main) process when the app
|
||||
starts.
|
||||
- The _main_ process is runs the embedded node. This process can deal with the
|
||||
host OS - it is conceptually like a `node` repl running on your machine. In our
|
||||
case, the TypeScript code (in the `src/` directory) gets transpiled by `tsc`
|
||||
into JavaScript in the `build/app/` directory, which gets bundled in the
|
||||
generated app's binary and is loaded by the node (main) process when the app
|
||||
starts.
|
||||
|
||||
* The _renderer_ process is a regular web app that gets loaded into the embedded
|
||||
Chromium. When the main process starts, it creates a new "window" that shows
|
||||
this embedded Chromium. In our case, we build and bundle a static export of
|
||||
the [Photos web app](../web/README.md) in the generated app. This gets loaded
|
||||
by the embedded Chromium at runtime, acting as the app's UI.
|
||||
- The _renderer_ process is a regular web app that gets loaded into the embedded
|
||||
Chromium. When the main process starts, it creates a new "window" that shows
|
||||
this embedded Chromium. In our case, we build and bundle a static export of
|
||||
the [Photos web app](../web/README.md) in the generated app. This gets loaded
|
||||
by the embedded Chromium at runtime, acting as the app's UI.
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
"build-main:quick": "tsc && electron-builder --config.compression=store",
|
||||
"build-renderer": "cd ../web && yarn install && yarn build:photos && cd ../desktop && rm -f out && ln -sf ../web/apps/photos/out",
|
||||
"build:quick": "yarn build-renderer && yarn build-main:quick",
|
||||
"dev": "concurrently \"yarn dev-main\" \"yarn dev-renderer\"",
|
||||
"dev-main": "tsc && electron build/app/main.js",
|
||||
"dev": "concurrently --names 'main,rndr,tscw' \"yarn dev-main\" \"yarn dev-renderer\" \"yarn dev-main-watch\"",
|
||||
"dev-main": "tsc && electron app/main.js",
|
||||
"dev-main-watch": "tsc --watch --preserveWatchOutput",
|
||||
"dev-renderer": "cd ../web && yarn install && yarn dev:photos",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"lint": "yarn prettier --check . && eslint \"src/**/*.ts\"",
|
||||
"lint-fix": "yarn prettier --write . && eslint --fix .",
|
||||
"watch": "tsc -w"
|
||||
"lint-fix": "yarn prettier --write . && eslint --fix src"
|
||||
},
|
||||
"dependencies": {
|
||||
"any-shell-escape": "^0.1.1",
|
||||
|
@ -32,7 +32,7 @@
|
|||
"get-folder-size": "^2.0.1",
|
||||
"html-entities": "^2.4.0",
|
||||
"jpeg-js": "^0.4.4",
|
||||
"next-electron-server": "file:./thirdparty/next-electron-server",
|
||||
"next-electron-server": "^1",
|
||||
"node-fetch": "^2.6.7",
|
||||
"node-stream-zip": "^1.15.0",
|
||||
"onnxruntime-node": "^1.16.3",
|
||||
|
@ -55,7 +55,7 @@
|
|||
"eslint": "^7.23.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"prettier": "2.5.1",
|
||||
"prettier": "^3",
|
||||
"prettier-plugin-organize-imports": "^3.2",
|
||||
"prettier-plugin-packagejson": "^2.4",
|
||||
"typescript": "^4.2.3"
|
||||
|
|
|
@ -22,7 +22,7 @@ const getCacheBucketDir = async (cacheName: string) => {
|
|||
|
||||
export async function openDiskCache(
|
||||
cacheName: string,
|
||||
cacheLimitInBytes?: number
|
||||
cacheLimitInBytes?: number,
|
||||
) {
|
||||
const cacheBucketDir = await getCacheBucketDir(cacheName);
|
||||
if (!existsSync(cacheBucketDir)) {
|
||||
|
@ -42,7 +42,7 @@ export async function deleteDiskCache(cacheName: string) {
|
|||
}
|
||||
|
||||
export async function setCustomCacheDirectory(
|
||||
directory: string
|
||||
directory: string,
|
||||
): Promise<void> {
|
||||
await ipcRenderer.invoke("set-custom-cache-directory", directory);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { isExecError, parseExecError } from "../utils/error";
|
|||
|
||||
export async function computeImageEmbedding(
|
||||
model: Model,
|
||||
imageData: Uint8Array
|
||||
imageData: Uint8Array,
|
||||
): Promise<Float32Array> {
|
||||
let tempInputFilePath = null;
|
||||
try {
|
||||
|
@ -15,7 +15,7 @@ export async function computeImageEmbedding(
|
|||
const embedding = await ipcRenderer.invoke(
|
||||
"compute-image-embedding",
|
||||
model,
|
||||
tempInputFilePath
|
||||
tempInputFilePath,
|
||||
);
|
||||
return embedding;
|
||||
} catch (err) {
|
||||
|
@ -34,13 +34,13 @@ export async function computeImageEmbedding(
|
|||
|
||||
export async function computeTextEmbedding(
|
||||
model: Model,
|
||||
text: string
|
||||
text: string,
|
||||
): Promise<Float32Array> {
|
||||
try {
|
||||
const embedding = await ipcRenderer.invoke(
|
||||
"compute-text-embedding",
|
||||
model,
|
||||
text
|
||||
text,
|
||||
);
|
||||
return embedding;
|
||||
} catch (err) {
|
||||
|
|
|
@ -13,7 +13,7 @@ export const checkExistsAndCreateDir = async (dirPath: string) => {
|
|||
|
||||
export const saveStreamToDisk = async (
|
||||
filePath: string,
|
||||
fileStream: ReadableStream<Uint8Array>
|
||||
fileStream: ReadableStream<Uint8Array>,
|
||||
) => {
|
||||
await writeStream(filePath, fileStream);
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ export async function runFFmpegCmd(
|
|||
cmd: string[],
|
||||
inputFile: File | ElectronFile,
|
||||
outputFileName: string,
|
||||
dontTimeout?: boolean
|
||||
dontTimeout?: boolean,
|
||||
) {
|
||||
let inputFilePath = null;
|
||||
let createdTempInputFile = null;
|
||||
|
@ -16,7 +16,7 @@ export async function runFFmpegCmd(
|
|||
if (!existsSync(inputFile.path)) {
|
||||
const tempFilePath = await ipcRenderer.invoke(
|
||||
"get-temp-file-path",
|
||||
inputFile.name
|
||||
inputFile.name,
|
||||
);
|
||||
await writeStream(tempFilePath, await inputFile.stream());
|
||||
inputFilePath = tempFilePath;
|
||||
|
@ -29,7 +29,7 @@ export async function runFFmpegCmd(
|
|||
cmd,
|
||||
inputFilePath,
|
||||
outputFileName,
|
||||
dontTimeout
|
||||
dontTimeout,
|
||||
);
|
||||
return new File([outputFileData], outputFileName);
|
||||
} finally {
|
||||
|
|
|
@ -8,7 +8,7 @@ import { isPlatform } from "../utils/common/platform";
|
|||
|
||||
export async function convertToJPEG(
|
||||
fileData: Uint8Array,
|
||||
filename: string
|
||||
filename: string,
|
||||
): Promise<Uint8Array> {
|
||||
if (isPlatform("windows")) {
|
||||
throw Error(CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED);
|
||||
|
@ -16,7 +16,7 @@ export async function convertToJPEG(
|
|||
const convertedFileData = await ipcRenderer.invoke(
|
||||
"convert-to-jpeg",
|
||||
fileData,
|
||||
filename
|
||||
filename,
|
||||
);
|
||||
return convertedFileData;
|
||||
}
|
||||
|
@ -24,20 +24,20 @@ export async function convertToJPEG(
|
|||
export async function generateImageThumbnail(
|
||||
inputFile: File | ElectronFile,
|
||||
maxDimension: number,
|
||||
maxSize: number
|
||||
maxSize: number,
|
||||
): Promise<Uint8Array> {
|
||||
let inputFilePath = null;
|
||||
let createdTempInputFile = null;
|
||||
try {
|
||||
if (isPlatform("windows")) {
|
||||
throw Error(
|
||||
CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED
|
||||
CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED,
|
||||
);
|
||||
}
|
||||
if (!existsSync(inputFile.path)) {
|
||||
const tempFilePath = await ipcRenderer.invoke(
|
||||
"get-temp-file-path",
|
||||
inputFile.name
|
||||
inputFile.name,
|
||||
);
|
||||
await writeStream(tempFilePath, await inputFile.stream());
|
||||
inputFilePath = tempFilePath;
|
||||
|
@ -49,7 +49,7 @@ export async function generateImageThumbnail(
|
|||
"generate-image-thumbnail",
|
||||
inputFilePath,
|
||||
maxDimension,
|
||||
maxSize
|
||||
maxSize,
|
||||
);
|
||||
return thumbnail;
|
||||
} finally {
|
||||
|
|
|
@ -6,7 +6,7 @@ export async function setEncryptionKey(encryptionKey: string) {
|
|||
try {
|
||||
const encryptedKey: Buffer = await ipcRenderer.invoke(
|
||||
"safeStorage-encrypt",
|
||||
encryptionKey
|
||||
encryptionKey,
|
||||
);
|
||||
const b64EncryptedKey = Buffer.from(encryptedKey).toString("base64");
|
||||
safeStorageStore.set("encryptionKey", b64EncryptedKey);
|
||||
|
@ -21,7 +21,7 @@ export async function getEncryptionKey(): Promise<string> {
|
|||
const b64EncryptedKey = safeStorageStore.get("encryptionKey");
|
||||
if (b64EncryptedKey) {
|
||||
const keyBuffer = new Uint8Array(
|
||||
Buffer.from(b64EncryptedKey, "base64")
|
||||
Buffer.from(b64EncryptedKey, "base64"),
|
||||
);
|
||||
return await ipcRenderer.invoke("safeStorage-decrypt", keyBuffer);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ export const reloadWindow = () => {
|
|||
};
|
||||
|
||||
export const registerUpdateEventListener = (
|
||||
showUpdateDialog: (updateInfo: AppUpdateInfo) => void
|
||||
showUpdateDialog: (updateInfo: AppUpdateInfo) => void,
|
||||
) => {
|
||||
ipcRenderer.removeAllListeners("show-update-dialog");
|
||||
ipcRenderer.on("show-update-dialog", (_, updateInfo: AppUpdateInfo) => {
|
||||
|
|
|
@ -39,7 +39,7 @@ export const getPendingUploads = async () => {
|
|||
export const showUploadDirsDialog = async () => {
|
||||
try {
|
||||
const filePaths: string[] = await ipcRenderer.invoke(
|
||||
"show-upload-dirs-dialog"
|
||||
"show-upload-dirs-dialog",
|
||||
);
|
||||
const files = await Promise.all(filePaths.map(getElectronFile));
|
||||
return files;
|
||||
|
@ -51,7 +51,7 @@ export const showUploadDirsDialog = async () => {
|
|||
export const showUploadFilesDialog = async () => {
|
||||
try {
|
||||
const filePaths: string[] = await ipcRenderer.invoke(
|
||||
"show-upload-files-dialog"
|
||||
"show-upload-files-dialog",
|
||||
);
|
||||
const files = await Promise.all(filePaths.map(getElectronFile));
|
||||
return files;
|
||||
|
@ -63,7 +63,7 @@ export const showUploadFilesDialog = async () => {
|
|||
export const showUploadZipDialog = async () => {
|
||||
try {
|
||||
const filePaths: string[] = await ipcRenderer.invoke(
|
||||
"show-upload-zip-dialog"
|
||||
"show-upload-zip-dialog",
|
||||
);
|
||||
let files: ElectronFile[] = [];
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { isMappingPresent } from "../utils/watch";
|
|||
export async function addWatchMapping(
|
||||
rootFolderName: string,
|
||||
folderPath: string,
|
||||
uploadStrategy: number
|
||||
uploadStrategy: number,
|
||||
) {
|
||||
ElectronLog.log(`Adding watch mapping: ${folderPath}`);
|
||||
const watchMappings = getWatchMappings();
|
||||
|
@ -35,7 +35,7 @@ export async function addWatchMapping(
|
|||
export async function removeWatchMapping(folderPath: string) {
|
||||
let watchMappings = getWatchMappings();
|
||||
const watchMapping = watchMappings.find(
|
||||
(mapping) => mapping.folderPath === folderPath
|
||||
(mapping) => mapping.folderPath === folderPath,
|
||||
);
|
||||
|
||||
if (!watchMapping) {
|
||||
|
@ -47,7 +47,7 @@ export async function removeWatchMapping(folderPath: string) {
|
|||
});
|
||||
|
||||
watchMappings = watchMappings.filter(
|
||||
(mapping) => mapping.folderPath !== watchMapping.folderPath
|
||||
(mapping) => mapping.folderPath !== watchMapping.folderPath,
|
||||
);
|
||||
|
||||
setWatchMappings(watchMappings);
|
||||
|
@ -55,11 +55,11 @@ export async function removeWatchMapping(folderPath: string) {
|
|||
|
||||
export function updateWatchMappingSyncedFiles(
|
||||
folderPath: string,
|
||||
files: WatchMapping["syncedFiles"]
|
||||
files: WatchMapping["syncedFiles"],
|
||||
): void {
|
||||
const watchMappings = getWatchMappings();
|
||||
const watchMapping = watchMappings.find(
|
||||
(mapping) => mapping.folderPath === folderPath
|
||||
(mapping) => mapping.folderPath === folderPath,
|
||||
);
|
||||
|
||||
if (!watchMapping) {
|
||||
|
@ -72,11 +72,11 @@ export function updateWatchMappingSyncedFiles(
|
|||
|
||||
export function updateWatchMappingIgnoredFiles(
|
||||
folderPath: string,
|
||||
files: WatchMapping["ignoredFiles"]
|
||||
files: WatchMapping["ignoredFiles"],
|
||||
): void {
|
||||
const watchMappings = getWatchMappings();
|
||||
const watchMapping = watchMappings.find(
|
||||
(mapping) => mapping.folderPath === folderPath
|
||||
(mapping) => mapping.folderPath === folderPath,
|
||||
);
|
||||
|
||||
if (!watchMapping) {
|
||||
|
@ -90,7 +90,7 @@ export function updateWatchMappingIgnoredFiles(
|
|||
export function registerWatcherFunctions(
|
||||
addFile: (file: ElectronFile) => Promise<void>,
|
||||
removeFile: (path: string) => Promise<void>,
|
||||
removeFolder: (folderPath: string) => Promise<void>
|
||||
removeFolder: (folderPath: string) => Promise<void>,
|
||||
) {
|
||||
ipcRenderer.removeAllListeners("watch-add");
|
||||
ipcRenderer.removeAllListeners("watch-change");
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { app, BrowserWindow } from "electron";
|
||||
import electronReload from "electron-reload";
|
||||
import serveNextAt from "next-electron-server";
|
||||
import { initWatcher } from "./services/chokidar";
|
||||
import { isDev } from "./utils/common";
|
||||
import { addAllowOriginHeader } from "./utils/cors";
|
||||
|
@ -14,9 +16,7 @@ import {
|
|||
handleUpdates,
|
||||
logSystemInfo,
|
||||
setupMacWindowOnDockIconClick,
|
||||
setupMainHotReload,
|
||||
setupMainMenu,
|
||||
setupNextElectronServe,
|
||||
setupTrayItem,
|
||||
} from "./utils/main";
|
||||
import { setupMainProcessStatsLogger } from "./utils/processStats";
|
||||
|
@ -38,14 +38,48 @@ export const setIsAppQuitting = (value: boolean): void => {
|
|||
export const isUpdateAvailable = (): boolean => {
|
||||
return updateIsAvailable;
|
||||
};
|
||||
|
||||
export const setIsUpdateAvailable = (value: boolean): void => {
|
||||
updateIsAvailable = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hot reload the main process if anything changes in the source directory that
|
||||
* we're running from.
|
||||
*
|
||||
* In particular, this gets triggered when the `tsc -w` rebuilds JS files in the
|
||||
* `app/` directory when we change the TS files in the `src/` directory.
|
||||
*/
|
||||
const setupMainHotReload = () => {
|
||||
if (isDev) {
|
||||
electronReload(__dirname, {});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The URL where the renderer HTML is being served from.
|
||||
*/
|
||||
export const rendererURL = "next://app";
|
||||
|
||||
/**
|
||||
* next-electron-server allows up to directly use the output of `next build` in
|
||||
* production mode and `next dev` in development mode, whilst keeping the rest
|
||||
* of our code the same.
|
||||
*
|
||||
* It uses protocol handlers to serve files from the "next://app" protocol
|
||||
*
|
||||
* - In development this is proxied to http://localhost:3000
|
||||
* - In production it serves files from the `/out` directory
|
||||
*
|
||||
* For more details, see this comparison:
|
||||
* https://github.com/HaNdTriX/next-electron-server/issues/5
|
||||
*/
|
||||
const setupRendererServer = () => {
|
||||
serveNextAt(rendererURL);
|
||||
};
|
||||
|
||||
setupMainHotReload();
|
||||
|
||||
setupNextElectronServe();
|
||||
|
||||
setupRendererServer();
|
||||
setupLogging(isDev);
|
||||
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
|
|
|
@ -59,13 +59,11 @@ import {
|
|||
updateWatchMappingSyncedFiles,
|
||||
} from "./api/watch";
|
||||
import { setupLogging } from "./utils/logging";
|
||||
import { fixHotReloadNext12 } from "./utils/preload";
|
||||
import {
|
||||
logRendererProcessMemoryUsage,
|
||||
setupRendererProcessStatsLogger,
|
||||
} from "./utils/processStats";
|
||||
|
||||
fixHotReloadNext12();
|
||||
setupLogging();
|
||||
setupRendererProcessStatsLogger();
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ export function setupAutoUpdater(mainWindow: BrowserWindow) {
|
|||
checkForUpdateAndNotify(mainWindow);
|
||||
setInterval(
|
||||
() => checkForUpdateAndNotify(mainWindow),
|
||||
ONE_DAY_IN_MICROSECOND
|
||||
ONE_DAY_IN_MICROSECOND,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
|
|||
if (
|
||||
compareVersions(
|
||||
updateCheckResult.updateInfo.version,
|
||||
app.getVersion()
|
||||
app.getVersion(),
|
||||
) <= 0
|
||||
) {
|
||||
log.debug("already at latest version");
|
||||
|
@ -60,7 +60,7 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
|
|||
) {
|
||||
log.info(
|
||||
"user chose to skip version ",
|
||||
updateCheckResult.updateInfo.version
|
||||
updateCheckResult.updateInfo.version,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
|
|||
isPlatform("mac") &&
|
||||
compareVersions(
|
||||
updateCheckResult.updateInfo.version,
|
||||
desktopCutoffVersion
|
||||
desktopCutoffVersion,
|
||||
) > 0
|
||||
) {
|
||||
log.debug("auto update not possible due to key change");
|
||||
|
@ -91,7 +91,7 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
|
|||
) {
|
||||
log.info(
|
||||
"user chose to mute update notification for version ",
|
||||
updateCheckResult.updateInfo.version
|
||||
updateCheckResult.updateInfo.version,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
|
|||
autoUpdatable: true,
|
||||
version: updateCheckResult.updateInfo.version,
|
||||
}),
|
||||
FIVE_MIN_IN_MICROSECOND
|
||||
FIVE_MIN_IN_MICROSECOND,
|
||||
);
|
||||
});
|
||||
autoUpdater.on("error", (error) => {
|
||||
|
@ -152,7 +152,7 @@ async function getDesktopCutoffVersion() {
|
|||
|
||||
function showUpdateDialog(
|
||||
mainWindow: BrowserWindow,
|
||||
updateInfo: AppUpdateInfo
|
||||
updateInfo: AppUpdateInfo,
|
||||
) {
|
||||
mainWindow.webContents.send("show-update-dialog", updateInfo);
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ export async function getClipImageModelPath(type: "ggml" | "onnx") {
|
|||
log.info("clip image model not found, downloading");
|
||||
imageModelDownloadInProgress = downloadModel(
|
||||
modelSavePath,
|
||||
IMAGE_MODEL_DOWNLOAD_URL[type]
|
||||
IMAGE_MODEL_DOWNLOAD_URL[type],
|
||||
);
|
||||
await imageModelDownloadInProgress;
|
||||
} else {
|
||||
|
@ -111,11 +111,11 @@ export async function getClipImageModelPath(type: "ggml" | "onnx") {
|
|||
if (localFileSize !== IMAGE_MODEL_SIZE_IN_BYTES[type]) {
|
||||
log.info(
|
||||
"clip image model size mismatch, downloading again got:",
|
||||
localFileSize
|
||||
localFileSize,
|
||||
);
|
||||
imageModelDownloadInProgress = downloadModel(
|
||||
modelSavePath,
|
||||
IMAGE_MODEL_DOWNLOAD_URL[type]
|
||||
IMAGE_MODEL_DOWNLOAD_URL[type],
|
||||
);
|
||||
await imageModelDownloadInProgress;
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ export async function getClipTextModelPath(type: "ggml" | "onnx") {
|
|||
if (localFileSize !== TEXT_MODEL_SIZE_IN_BYTES[type]) {
|
||||
log.info(
|
||||
"clip text model size mismatch, downloading again got:",
|
||||
localFileSize
|
||||
localFileSize,
|
||||
);
|
||||
textModelDownloadInProgress = true;
|
||||
downloadModel(modelSavePath, TEXT_MODEL_DOWNLOAD_URL[type])
|
||||
|
@ -212,7 +212,7 @@ function getTokenizer() {
|
|||
|
||||
export async function computeImageEmbedding(
|
||||
model: Model,
|
||||
inputFilePath: string
|
||||
inputFilePath: string,
|
||||
): Promise<Float32Array> {
|
||||
if (!existsSync(inputFilePath)) {
|
||||
throw Error(CustomErrors.INVALID_FILE_PATH);
|
||||
|
@ -227,7 +227,7 @@ export async function computeImageEmbedding(
|
|||
}
|
||||
|
||||
export async function computeGGMLImageEmbedding(
|
||||
inputFilePath: string
|
||||
inputFilePath: string,
|
||||
): Promise<Float32Array> {
|
||||
try {
|
||||
const clipModelPath = await getClipImageModelPath("ggml");
|
||||
|
@ -263,7 +263,7 @@ export async function computeGGMLImageEmbedding(
|
|||
}
|
||||
|
||||
export async function computeONNXImageEmbedding(
|
||||
inputFilePath: string
|
||||
inputFilePath: string,
|
||||
): Promise<Float32Array> {
|
||||
try {
|
||||
const imageSession = await getOnnxImageSession();
|
||||
|
@ -277,7 +277,7 @@ export async function computeONNXImageEmbedding(
|
|||
log.info(
|
||||
`onnx image embedding time: ${Date.now() - t1} ms (prep:${
|
||||
t2 - t1
|
||||
} ms, extraction: ${Date.now() - t2} ms)`
|
||||
} ms, extraction: ${Date.now() - t2} ms)`,
|
||||
);
|
||||
const imageEmbedding = results["output"].data; // Float32Array
|
||||
return normalizeEmbedding(imageEmbedding);
|
||||
|
@ -289,7 +289,7 @@ export async function computeONNXImageEmbedding(
|
|||
|
||||
export async function computeTextEmbedding(
|
||||
model: Model,
|
||||
text: string
|
||||
text: string,
|
||||
): Promise<Float32Array> {
|
||||
if (model === Model.GGML_CLIP) {
|
||||
return await computeGGMLTextEmbedding(text);
|
||||
|
@ -299,7 +299,7 @@ export async function computeTextEmbedding(
|
|||
}
|
||||
|
||||
export async function computeGGMLTextEmbedding(
|
||||
text: string
|
||||
text: string,
|
||||
): Promise<Float32Array> {
|
||||
try {
|
||||
const clipModelPath = await getClipTextModelPath("ggml");
|
||||
|
@ -339,7 +339,7 @@ export async function computeGGMLTextEmbedding(
|
|||
}
|
||||
|
||||
export async function computeONNXTextEmbedding(
|
||||
text: string
|
||||
text: string,
|
||||
): Promise<Float32Array> {
|
||||
try {
|
||||
const imageSession = await getOnnxTextSession();
|
||||
|
@ -354,7 +354,7 @@ export async function computeONNXTextEmbedding(
|
|||
log.info(
|
||||
`onnx text embedding time: ${Date.now() - t1} ms (prep:${
|
||||
t2 - t1
|
||||
} ms, extraction: ${Date.now() - t2} ms)`
|
||||
} ms, extraction: ${Date.now() - t2} ms)`,
|
||||
);
|
||||
const textEmbedding = results["output"].data; // Float32Array
|
||||
return normalizeEmbedding(textEmbedding);
|
||||
|
@ -444,7 +444,7 @@ async function getRGBData(inputFilePath: string) {
|
|||
|
||||
export const computeClipMatchScore = async (
|
||||
imageEmbedding: Float32Array,
|
||||
textEmbedding: Float32Array
|
||||
textEmbedding: Float32Array,
|
||||
) => {
|
||||
if (imageEmbedding.length !== textEmbedding.length) {
|
||||
throw Error("imageEmbedding and textEmbedding length mismatch");
|
||||
|
|
|
@ -11,7 +11,7 @@ const DEFAULT_CACHE_LIMIT = 1000 * 1000 * 1000; // 1GB
|
|||
export class DiskCache implements LimitedCache {
|
||||
constructor(
|
||||
private cacheBucketDir: string,
|
||||
private cacheLimit = DEFAULT_CACHE_LIMIT
|
||||
private cacheLimit = DEFAULT_CACHE_LIMIT,
|
||||
) {}
|
||||
|
||||
async put(cacheKey: string, response: Response): Promise<void> {
|
||||
|
@ -19,13 +19,13 @@ export class DiskCache implements LimitedCache {
|
|||
await writeStream(cachePath, response.body);
|
||||
DiskLRUService.enforceCacheSizeLimit(
|
||||
this.cacheBucketDir,
|
||||
this.cacheLimit
|
||||
this.cacheLimit,
|
||||
);
|
||||
}
|
||||
|
||||
async match(
|
||||
cacheKey: string,
|
||||
{ sizeInBytes }: { sizeInBytes?: number } = {}
|
||||
{ sizeInBytes }: { sizeInBytes?: number } = {},
|
||||
): Promise<Response> {
|
||||
const cachePath = path.join(this.cacheBucketDir, cacheKey);
|
||||
if (existsSync(cachePath)) {
|
||||
|
@ -33,7 +33,7 @@ export class DiskCache implements LimitedCache {
|
|||
if (sizeInBytes && fileStats.size !== sizeInBytes) {
|
||||
logError(
|
||||
Error(),
|
||||
"Cache key exists but size does not match. Deleting cache key."
|
||||
"Cache key exists but size does not match. Deleting cache key.",
|
||||
);
|
||||
unlink(cachePath).catch((e) => {
|
||||
if (e.code === "ENOENT") return;
|
||||
|
@ -47,14 +47,14 @@ export class DiskCache implements LimitedCache {
|
|||
// add fallback for old cache keys
|
||||
const oldCachePath = getOldAssetCachePath(
|
||||
this.cacheBucketDir,
|
||||
cacheKey
|
||||
cacheKey,
|
||||
);
|
||||
if (existsSync(oldCachePath)) {
|
||||
const fileStats = await stat(oldCachePath);
|
||||
if (sizeInBytes && fileStats.size !== sizeInBytes) {
|
||||
logError(
|
||||
Error(),
|
||||
"Old cache key exists but size does not match. Deleting cache key."
|
||||
"Old cache key exists but size does not match. Deleting cache key.",
|
||||
);
|
||||
unlink(oldCachePath).catch((e) => {
|
||||
if (e.code === "ENOENT") return;
|
||||
|
|
|
@ -60,7 +60,7 @@ class DiskLRUService {
|
|||
if (e.code !== "ENOENT") {
|
||||
logError(
|
||||
e,
|
||||
"Failed to evict least recently used"
|
||||
"Failed to evict least recently used",
|
||||
);
|
||||
}
|
||||
// ignoring the error, as it would get retried on the next run
|
||||
|
@ -77,7 +77,7 @@ class DiskLRUService {
|
|||
|
||||
private async findLeastRecentlyUsed(
|
||||
dir: string,
|
||||
result?: LeastRecentlyUsedResult
|
||||
result?: LeastRecentlyUsedResult,
|
||||
): Promise<LeastRecentlyUsedResult> {
|
||||
result = result || { atime: new Date(), path: "" };
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ export async function runFFmpegCmd(
|
|||
cmd: string[],
|
||||
inputFilePath: string,
|
||||
outputFileName: string,
|
||||
dontTimeout = false
|
||||
dontTimeout = false,
|
||||
) {
|
||||
let tempOutputFilePath: string;
|
||||
try {
|
||||
|
@ -49,7 +49,7 @@ export async function runFFmpegCmd(
|
|||
} else {
|
||||
await promiseWithTimeout(
|
||||
execAsync(escapedCmd),
|
||||
FFMPEG_EXECUTION_WAIT_TIME
|
||||
FFMPEG_EXECUTION_WAIT_TIME,
|
||||
);
|
||||
}
|
||||
if (!existsSync(tempOutputFilePath)) {
|
||||
|
@ -59,7 +59,7 @@ export async function runFFmpegCmd(
|
|||
"ffmpeg command execution time ",
|
||||
escapedCmd,
|
||||
Date.now() - startTime,
|
||||
"ms"
|
||||
"ms",
|
||||
);
|
||||
|
||||
const outputFile = await readFile(tempOutputFilePath);
|
||||
|
@ -87,7 +87,7 @@ export async function deleteTempFile(tempFilePath: string) {
|
|||
if (!tempFilePath.startsWith(tempDirPath)) {
|
||||
logErrorSentry(
|
||||
Error("not a temp file"),
|
||||
"tried to delete a non temp file"
|
||||
"tried to delete a non temp file",
|
||||
);
|
||||
}
|
||||
rmSync(tempFilePath, { force: true });
|
||||
|
|
|
@ -38,7 +38,7 @@ export const getFileStream = async (filePath: string) => {
|
|||
buff,
|
||||
0,
|
||||
FILE_STREAM_CHUNK_SIZE,
|
||||
offset
|
||||
offset,
|
||||
)) as unknown as number;
|
||||
offset += bytesRead;
|
||||
if (bytesRead === 0) {
|
||||
|
@ -103,7 +103,7 @@ export const getValidPaths = (paths: string[]) => {
|
|||
|
||||
export const getZipFileStream = async (
|
||||
zip: StreamZip.StreamZipAsync,
|
||||
filePath: string
|
||||
filePath: string,
|
||||
) => {
|
||||
const stream = await zip.stream(filePath);
|
||||
const done = {
|
||||
|
@ -204,7 +204,7 @@ export async function isFolder(dirPath: string) {
|
|||
}
|
||||
|
||||
export const convertBrowserStreamToNode = (
|
||||
fileStream: ReadableStream<Uint8Array>
|
||||
fileStream: ReadableStream<Uint8Array>,
|
||||
) => {
|
||||
const reader = fileStream.getReader();
|
||||
const rs = new Readable();
|
||||
|
@ -229,7 +229,7 @@ export const convertBrowserStreamToNode = (
|
|||
|
||||
export async function writeNodeStream(
|
||||
filePath: string,
|
||||
fileStream: NodeJS.ReadableStream
|
||||
fileStream: NodeJS.ReadableStream,
|
||||
) {
|
||||
const writeable = fs.createWriteStream(filePath);
|
||||
|
||||
|
@ -252,7 +252,7 @@ export async function writeNodeStream(
|
|||
|
||||
export async function writeStream(
|
||||
filePath: string,
|
||||
fileStream: ReadableStream<Uint8Array>
|
||||
fileStream: ReadableStream<Uint8Array>,
|
||||
) {
|
||||
const readable = convertBrowserStreamToNode(fileStream);
|
||||
await writeNodeStream(filePath, readable);
|
||||
|
@ -267,7 +267,7 @@ export async function readTextFile(filePath: string) {
|
|||
|
||||
export async function moveFile(
|
||||
sourcePath: string,
|
||||
destinationPath: string
|
||||
destinationPath: string,
|
||||
): Promise<void> {
|
||||
if (!existsSync(sourcePath)) {
|
||||
throw new Error("File does not exist");
|
||||
|
|
|
@ -80,7 +80,7 @@ function getImageMagickStaticPath() {
|
|||
|
||||
export async function convertToJPEG(
|
||||
fileData: Uint8Array,
|
||||
filename: string
|
||||
filename: string,
|
||||
): Promise<Uint8Array> {
|
||||
let tempInputFilePath: string;
|
||||
let tempOutputFilePath: string;
|
||||
|
@ -96,7 +96,7 @@ export async function convertToJPEG(
|
|||
throw new Error("heic convert output file not found");
|
||||
}
|
||||
const convertedFileData = new Uint8Array(
|
||||
await readFile(tempOutputFilePath)
|
||||
await readFile(tempOutputFilePath),
|
||||
);
|
||||
return convertedFileData;
|
||||
} catch (e) {
|
||||
|
@ -118,11 +118,11 @@ export async function convertToJPEG(
|
|||
|
||||
async function runConvertCommand(
|
||||
tempInputFilePath: string,
|
||||
tempOutputFilePath: string
|
||||
tempOutputFilePath: string,
|
||||
) {
|
||||
const convertCmd = constructConvertCommand(
|
||||
tempInputFilePath,
|
||||
tempOutputFilePath
|
||||
tempOutputFilePath,
|
||||
);
|
||||
const escapedCmd = shellescape(convertCmd);
|
||||
log.info("running convert command: " + escapedCmd);
|
||||
|
@ -131,7 +131,7 @@ async function runConvertCommand(
|
|||
|
||||
function constructConvertCommand(
|
||||
tempInputFilePath: string,
|
||||
tempOutputFilePath: string
|
||||
tempOutputFilePath: string,
|
||||
) {
|
||||
let convertCmd: string[];
|
||||
if (isPlatform("mac")) {
|
||||
|
@ -157,7 +157,7 @@ function constructConvertCommand(
|
|||
return tempOutputFilePath;
|
||||
}
|
||||
return cmdPart;
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
throw Error(CustomErrors.INVALID_OS(process.platform));
|
||||
|
@ -168,7 +168,7 @@ function constructConvertCommand(
|
|||
export async function generateImageThumbnail(
|
||||
inputFilePath: string,
|
||||
width: number,
|
||||
maxSize: number
|
||||
maxSize: number,
|
||||
): Promise<Uint8Array> {
|
||||
let tempOutputFilePath: string;
|
||||
let quality = MAX_QUALITY;
|
||||
|
@ -180,7 +180,7 @@ export async function generateImageThumbnail(
|
|||
inputFilePath,
|
||||
tempOutputFilePath,
|
||||
width,
|
||||
quality
|
||||
quality,
|
||||
);
|
||||
|
||||
if (!existsSync(tempOutputFilePath)) {
|
||||
|
@ -206,14 +206,14 @@ async function runThumbnailGenerationCommand(
|
|||
inputFilePath: string,
|
||||
tempOutputFilePath: string,
|
||||
maxDimension: number,
|
||||
quality: number
|
||||
quality: number,
|
||||
) {
|
||||
const thumbnailGenerationCmd: string[] =
|
||||
constructThumbnailGenerationCommand(
|
||||
inputFilePath,
|
||||
tempOutputFilePath,
|
||||
maxDimension,
|
||||
quality
|
||||
quality,
|
||||
);
|
||||
const escapedCmd = shellescape(thumbnailGenerationCmd);
|
||||
log.info("running thumbnail generation command: " + escapedCmd);
|
||||
|
@ -223,7 +223,7 @@ function constructThumbnailGenerationCommand(
|
|||
inputFilePath: string,
|
||||
tempOutputFilePath: string,
|
||||
maxDimension: number,
|
||||
quality: number
|
||||
quality: number,
|
||||
) {
|
||||
let thumbnailGenerationCmd: string[];
|
||||
if (isPlatform("mac")) {
|
||||
|
@ -242,7 +242,7 @@ function constructThumbnailGenerationCommand(
|
|||
return quality.toString();
|
||||
}
|
||||
return cmdPart;
|
||||
}
|
||||
},
|
||||
);
|
||||
} else if (isPlatform("linux")) {
|
||||
thumbnailGenerationCmd =
|
||||
|
@ -259,13 +259,13 @@ function constructThumbnailGenerationCommand(
|
|||
if (cmdPart.includes(SAMPLE_SIZE_PLACEHOLDER)) {
|
||||
return cmdPart.replaceAll(
|
||||
SAMPLE_SIZE_PLACEHOLDER,
|
||||
(2 * maxDimension).toString()
|
||||
(2 * maxDimension).toString(),
|
||||
);
|
||||
}
|
||||
if (cmdPart.includes(MAX_DIMENSION_PLACEHOLDER)) {
|
||||
return cmdPart.replaceAll(
|
||||
MAX_DIMENSION_PLACEHOLDER,
|
||||
maxDimension.toString()
|
||||
maxDimension.toString(),
|
||||
);
|
||||
}
|
||||
if (cmdPart === QUALITY_PLACEHOLDER) {
|
||||
|
|
|
@ -5,12 +5,12 @@ import { logToDisk } from "./logging";
|
|||
export function logErrorSentry(
|
||||
error: any,
|
||||
msg: string,
|
||||
info?: Record<string, unknown>
|
||||
info?: Record<string, unknown>,
|
||||
) {
|
||||
logToDisk(
|
||||
`error: ${error?.name} ${error?.message} ${
|
||||
error?.stack
|
||||
} msg: ${msg} info: ${JSON.stringify(info)}`
|
||||
} msg: ${msg} info: ${JSON.stringify(info)}`,
|
||||
);
|
||||
if (isDev) {
|
||||
console.log(error, { msg, info });
|
||||
|
|
|
@ -7,7 +7,7 @@ import { getValidPaths, getZipFileStream } from "./fs";
|
|||
export const getSavedFilePaths = (type: FILE_PATH_TYPE) => {
|
||||
const paths =
|
||||
getValidPaths(
|
||||
uploadStatusStore.get(FILE_PATH_KEYS[type]) as string[]
|
||||
uploadStatusStore.get(FILE_PATH_KEYS[type]) as string[],
|
||||
) ?? [];
|
||||
|
||||
setToUploadFiles(type, paths);
|
||||
|
@ -17,7 +17,7 @@ export const getSavedFilePaths = (type: FILE_PATH_TYPE) => {
|
|||
export async function getZipEntryAsElectronFile(
|
||||
zipName: string,
|
||||
zip: StreamZip.StreamZipAsync,
|
||||
entry: StreamZip.ZipEntry
|
||||
entry: StreamZip.ZipEntry,
|
||||
): Promise<ElectronFile> {
|
||||
return {
|
||||
path: path
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export interface LimitedCache {
|
||||
match: (
|
||||
key: string,
|
||||
options?: { sizeInBytes?: number }
|
||||
options?: { sizeInBytes?: number },
|
||||
) => Promise<Response>;
|
||||
put: (key: string, data: Response) => Promise<void>;
|
||||
delete: (key: string) => Promise<boolean>;
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
# CLIP Byte Pair Encoding JavaScript Port
|
||||
|
||||
A JavaScript port of [OpenAI's CLIP byte-pair-encoding tokenizer](https://github.com/openai/CLIP/blob/3bee28119e6b28e75b82b811b87b56935314e6a5/clip/simple_tokenizer.py).
|
||||
|
||||
```js
|
||||
import Tokenizer from "https://deno.land/x/clip_bpe@v0.0.6/mod.js";
|
||||
let t = new Tokenizer();
|
||||
|
||||
t.encode("hello") // [3306]
|
||||
t.encode("magnificent") // [10724]
|
||||
t.encode("magnificently") // [9725, 2922]
|
||||
t.decode(t.encode("HELLO")) // "hello "
|
||||
t.decode(t.encode("abc123")) // "abc 1 2 3 "
|
||||
t.decode(st.encode("let's see here")) // "let 's see here "
|
||||
t.encode("hello world!") // [3306, 1002, 256]
|
||||
t.encode("hello"); // [3306]
|
||||
t.encode("magnificent"); // [10724]
|
||||
t.encode("magnificently"); // [9725, 2922]
|
||||
t.decode(t.encode("HELLO")); // "hello "
|
||||
t.decode(t.encode("abc123")); // "abc 1 2 3 "
|
||||
t.decode(st.encode("let's see here")); // "let 's see here "
|
||||
t.encode("hello world!"); // [3306, 1002, 256]
|
||||
|
||||
// to encode for CLIP (trims to maximum of 77 tokens and adds start and end token, and pads with zeros if less than 77 tokens):
|
||||
t.encodeForCLIP("hello world!") // [49406,3306,1002,256,49407,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
|
||||
t.encodeForCLIP("hello world!"); // [49406,3306,1002,256,49407,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
|
||||
```
|
||||
|
||||
This encoder/decoder behaves differently to the the GPT-2/3 tokenizer (JavaScript version of that [here](https://github.com/latitudegames/GPT-3-Encoder)). For example, it doesn't preserve capital letters, as shown above.
|
||||
|
|
|
@ -75,7 +75,7 @@ export default class {
|
|||
constructor() {
|
||||
this.byteEncoder = bytesToUnicode();
|
||||
this.byteDecoder = Object.fromEntries(
|
||||
Object.entries(this.byteEncoder).map(([k, v]) => [v, Number(k)])
|
||||
Object.entries(this.byteEncoder).map(([k, v]) => [v, Number(k)]),
|
||||
);
|
||||
let merges = bpeVocabData.text.split("\n");
|
||||
merges = merges.slice(1, 49152 - 256 - 2 + 1);
|
||||
|
@ -346,10 +346,10 @@ export default class {
|
|||
vocab.push("<|startoftext|>", "<|endoftext|>");
|
||||
this.encoder = Object.fromEntries(vocab.map((v, i) => [v, i]));
|
||||
this.decoder = Object.fromEntries(
|
||||
Object.entries(this.encoder).map(([k, v]) => [v, k])
|
||||
Object.entries(this.encoder).map(([k, v]) => [v, k]),
|
||||
);
|
||||
this.bpeRanks = Object.fromEntries(
|
||||
mergedMerges.map((v, i) => [v.join("·😎·"), i])
|
||||
mergedMerges.map((v, i) => [v.join("·😎·"), i]),
|
||||
); // ·😎· because js doesn't yet have tuples
|
||||
this.cache = {
|
||||
"<|startoftext|>": "<|startoftext|>",
|
||||
|
@ -436,7 +436,7 @@ export default class {
|
|||
bpeTokens.push(
|
||||
...this.bpe(token)
|
||||
.split(" ")
|
||||
.map((bpeToken: string) => this.encoder[bpeToken])
|
||||
.map((bpeToken: string) => this.encoder[bpeToken]),
|
||||
);
|
||||
}
|
||||
return bpeTokens;
|
||||
|
|
|
@ -4,7 +4,7 @@ export const isDev = !app.isPackaged;
|
|||
|
||||
export const promiseWithTimeout = async <T>(
|
||||
request: Promise<T>,
|
||||
timeout: number
|
||||
timeout: number,
|
||||
): Promise<T> => {
|
||||
const timeoutRef: {
|
||||
current: NodeJS.Timeout;
|
||||
|
@ -12,7 +12,7 @@ export const promiseWithTimeout = async <T>(
|
|||
const rejectOnTimeout = new Promise<null>((_, reject) => {
|
||||
timeoutRef.current = setTimeout(
|
||||
() => reject(Error(CustomErrors.WAIT_TIME_EXCEEDED)),
|
||||
timeout
|
||||
timeout,
|
||||
);
|
||||
});
|
||||
const requestWithTimeOutCancellation = async () => {
|
||||
|
|
|
@ -16,6 +16,6 @@ export function addAllowOriginHeader(mainWindow: BrowserWindow) {
|
|||
callback({
|
||||
responseHeaders: details.responseHeaders,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import { app, BrowserWindow, nativeImage } from "electron";
|
||||
import ElectronLog from "electron-log";
|
||||
import * as path from "path";
|
||||
import { isAppQuitting } from "../main";
|
||||
import { isAppQuitting, rendererURL } from "../main";
|
||||
import autoLauncher from "../services/autoLauncher";
|
||||
import { logErrorSentry } from "../services/sentry";
|
||||
import { getHideDockIconPreference } from "../services/userPreference";
|
||||
import { isDev } from "./common";
|
||||
import { isPlatform } from "./common/platform";
|
||||
import { PROD_HOST_URL } from "./main";
|
||||
|
||||
export async function createWindow(): Promise<BrowserWindow> {
|
||||
const appImgPath = isDev
|
||||
|
@ -41,21 +40,21 @@ export async function createWindow(): Promise<BrowserWindow> {
|
|||
|
||||
if (isDev) {
|
||||
splash.loadFile(`../resources/splash.html`);
|
||||
mainWindow.loadURL(PROD_HOST_URL);
|
||||
mainWindow.loadURL(rendererURL);
|
||||
// Open the DevTools.
|
||||
mainWindow.webContents.openDevTools();
|
||||
} else {
|
||||
splash.loadURL(
|
||||
`file://${path.join(process.resourcesPath, "splash.html")}`
|
||||
`file://${path.join(process.resourcesPath, "splash.html")}`,
|
||||
);
|
||||
mainWindow.loadURL(PROD_HOST_URL);
|
||||
mainWindow.loadURL(rendererURL);
|
||||
}
|
||||
mainWindow.webContents.on("did-fail-load", () => {
|
||||
splash.close();
|
||||
isDev
|
||||
? mainWindow.loadFile(`../resources/error.html`)
|
||||
: splash.loadURL(
|
||||
`file://${path.join(process.resourcesPath, "error.html")}`
|
||||
`file://${path.join(process.resourcesPath, "error.html")}`,
|
||||
);
|
||||
mainWindow.maximize();
|
||||
mainWindow.show();
|
||||
|
@ -76,7 +75,7 @@ export async function createWindow(): Promise<BrowserWindow> {
|
|||
logErrorSentry(
|
||||
Error("render-process-gone"),
|
||||
"webContents event render-process-gone",
|
||||
{ details }
|
||||
{ details },
|
||||
);
|
||||
ElectronLog.log("webContents event render-process-gone", details);
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ export const parseExecError = (err: any) => {
|
|||
if (errMessage.includes("Bad CPU type in executable")) {
|
||||
return CustomErrors.UNSUPPORTED_PLATFORM(
|
||||
process.platform,
|
||||
process.arch
|
||||
process.arch,
|
||||
);
|
||||
} else {
|
||||
return errMessage;
|
||||
|
|
|
@ -38,7 +38,7 @@ import { generateTempFilePath } from "./temp";
|
|||
export default function setupIpcComs(
|
||||
tray: Tray,
|
||||
mainWindow: BrowserWindow,
|
||||
watcher: chokidar.FSWatcher
|
||||
watcher: chokidar.FSWatcher,
|
||||
): void {
|
||||
ipcMain.handle("select-dir", async () => {
|
||||
const result = await dialog.showOpenDialog({
|
||||
|
@ -156,9 +156,9 @@ export default function setupIpcComs(
|
|||
cmd,
|
||||
inputFilePath,
|
||||
outputFileName,
|
||||
dontTimeout
|
||||
dontTimeout,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
ipcMain.handle("get-temp-file-path", (_, formatSuffix) => {
|
||||
return generateTempFilePath(formatSuffix);
|
||||
|
@ -171,7 +171,7 @@ export default function setupIpcComs(
|
|||
"generate-image-thumbnail",
|
||||
(_, fileData, maxDimension, maxSize) => {
|
||||
return generateImageThumbnail(fileData, maxDimension, maxSize);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle("compute-image-embedding", (_, model, inputFilePath) => {
|
||||
|
|
|
@ -12,7 +12,7 @@ export function setupLogging(isDev?: boolean) {
|
|||
|
||||
export function convertBytesToHumanReadable(
|
||||
bytes: number,
|
||||
precision = 2
|
||||
precision = 2,
|
||||
): string {
|
||||
if (bytes === 0 || isNaN(bytes)) {
|
||||
return "0 MB";
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { app, BrowserWindow, Menu, nativeImage, Tray } from "electron";
|
||||
import ElectronLog from "electron-log";
|
||||
import electronReload from "electron-reload";
|
||||
import serveNextAt from "next-electron-server";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import { existsSync } from "promise-fs";
|
||||
import util from "util";
|
||||
import { rendererURL } from "../main";
|
||||
import { setupAutoUpdater } from "../services/appUpdater";
|
||||
import autoLauncher from "../services/autoLauncher";
|
||||
import { getHideDockIconPreference } from "../services/userPreference";
|
||||
|
@ -14,9 +13,6 @@ import { isPlatform } from "./common/platform";
|
|||
import { buildContextMenu, buildMenuBar } from "./menu";
|
||||
const execAsync = util.promisify(require("child_process").exec);
|
||||
|
||||
export const PROD_HOST_URL: string = "ente://app";
|
||||
const RENDERER_OUTPUT_DIR: string = "./out";
|
||||
|
||||
export async function handleUpdates(mainWindow: BrowserWindow) {
|
||||
const isInstalledViaBrew = await checkIfInstalledViaBrew();
|
||||
if (!isDev && !isInstalledViaBrew) {
|
||||
|
@ -29,7 +25,7 @@ export function setupTrayItem(mainWindow: BrowserWindow) {
|
|||
: "taskbar-icon.png";
|
||||
const trayImgPath = path.join(
|
||||
isDev ? "build" : process.resourcesPath,
|
||||
iconName
|
||||
iconName,
|
||||
);
|
||||
const trayIcon = nativeImage.createFromPath(trayImgPath);
|
||||
const tray = new Tray(trayIcon);
|
||||
|
@ -41,11 +37,22 @@ export function setupTrayItem(mainWindow: BrowserWindow) {
|
|||
export function handleDownloads(mainWindow: BrowserWindow) {
|
||||
mainWindow.webContents.session.on("will-download", (_, item) => {
|
||||
item.setSavePath(
|
||||
getUniqueSavePath(item.getFilename(), app.getPath("downloads"))
|
||||
getUniqueSavePath(item.getFilename(), app.getPath("downloads")),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function handleExternalLinks(mainWindow: BrowserWindow) {
|
||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (!url.startsWith(rendererURL)) {
|
||||
require("electron").shell.openExternal(url);
|
||||
return { action: "deny" };
|
||||
} else {
|
||||
return { action: "allow" };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getUniqueSavePath(filename: string, directory: string): string {
|
||||
let uniqueFileSavePath = path.join(directory, filename);
|
||||
const { name: filenameWithoutExtension, ext: extension } =
|
||||
|
@ -78,22 +85,6 @@ export async function setupMainMenu(mainWindow: BrowserWindow) {
|
|||
Menu.setApplicationMenu(await buildMenuBar(mainWindow));
|
||||
}
|
||||
|
||||
export function setupMainHotReload() {
|
||||
// Hot reload the main process if anything changes in the source directory
|
||||
// that we're running from. In particular, this gets triggered when `yarn
|
||||
// watch` rebuilds JS files in the `app/` directory when we change the TS
|
||||
// files in the `src/` directory.
|
||||
if (isDev) {
|
||||
electronReload(__dirname, {});
|
||||
}
|
||||
}
|
||||
|
||||
export function setupNextElectronServe() {
|
||||
serveNextAt(PROD_HOST_URL, {
|
||||
outputDir: RENDERER_OUTPUT_DIR,
|
||||
});
|
||||
}
|
||||
|
||||
export async function handleDockIconHideOnAutoLaunch() {
|
||||
const shouldHideDockIcon = getHideDockIconPreference();
|
||||
const wasAutoLaunched = await autoLauncher.wasAutoLaunched();
|
||||
|
@ -116,17 +107,6 @@ export function logSystemInfo() {
|
|||
ElectronLog.info({ appVersion });
|
||||
}
|
||||
|
||||
export function handleExternalLinks(mainWindow: BrowserWindow) {
|
||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (!url.startsWith(PROD_HOST_URL)) {
|
||||
require("electron").shell.openExternal(url);
|
||||
return { action: "deny" };
|
||||
} else {
|
||||
return { action: "allow" };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function checkIfInstalledViaBrew() {
|
||||
if (!isPlatform("mac")) {
|
||||
return false;
|
||||
|
|
|
@ -64,7 +64,7 @@ export async function buildMenuBar(mainWindow: BrowserWindow): Promise<Menu> {
|
|||
label: "View Changelog",
|
||||
click: () => {
|
||||
shell.openExternal(
|
||||
"https://github.com/ente-io/ente/blob/main/desktop/CHANGELOG.md"
|
||||
"https://github.com/ente-io/ente/blob/main/desktop/CHANGELOG.md",
|
||||
);
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import { webFrame } from "electron";
|
||||
|
||||
export const fixHotReloadNext12 = () => {
|
||||
webFrame.executeJavaScript(`Object.defineProperty(globalThis, 'WebSocket', {
|
||||
value: new Proxy(WebSocket, {
|
||||
construct: (Target, [url, protocols]) => {
|
||||
if (url.endsWith('/_next/webpack-hmr')) {
|
||||
// Fix the Next.js hmr client url
|
||||
return new Target("ws://localhost:3000/_next/webpack-hmr", protocols)
|
||||
} else {
|
||||
return new Target(url, protocols)
|
||||
}
|
||||
}
|
||||
})
|
||||
});`);
|
||||
};
|
|
@ -16,11 +16,11 @@ const HIGH_RENDERER_MEMORY_USAGE_THRESHOLD_IN_KILOBYTES = 1024 * 1024; // 1 GB
|
|||
|
||||
async function logMainProcessStats() {
|
||||
const processMemoryInfo = await getNormalizedProcessMemoryInfo(
|
||||
await process.getProcessMemoryInfo()
|
||||
await process.getProcessMemoryInfo(),
|
||||
);
|
||||
const cpuUsage = process.getCPUUsage();
|
||||
const heapStatistics = getNormalizedHeapStatistics(
|
||||
process.getHeapStatistics()
|
||||
process.getHeapStatistics(),
|
||||
);
|
||||
|
||||
ElectronLog.log("main process stats", {
|
||||
|
@ -42,11 +42,11 @@ async function logSpikeMainMemoryUsage() {
|
|||
const processMemoryInfo = await process.getProcessMemoryInfo();
|
||||
const currentMemoryUsage = Math.max(
|
||||
processMemoryInfo.residentSet ?? 0,
|
||||
processMemoryInfo.private
|
||||
processMemoryInfo.private,
|
||||
);
|
||||
const previousMemoryUsage = Math.max(
|
||||
previousMainProcessMemoryInfo.residentSet ?? 0,
|
||||
previousMainProcessMemoryInfo.private
|
||||
previousMainProcessMemoryInfo.private,
|
||||
);
|
||||
const isSpiking =
|
||||
currentMemoryUsage - previousMemoryUsage >=
|
||||
|
@ -66,7 +66,7 @@ async function logSpikeMainMemoryUsage() {
|
|||
await getNormalizedProcessMemoryInfo(previousMainProcessMemoryInfo);
|
||||
const cpuUsage = process.getCPUUsage();
|
||||
const heapStatistics = getNormalizedHeapStatistics(
|
||||
process.getHeapStatistics()
|
||||
process.getHeapStatistics(),
|
||||
);
|
||||
|
||||
ElectronLog.log("reporting main memory usage spike", {
|
||||
|
@ -94,12 +94,12 @@ async function logSpikeRendererMemoryUsage() {
|
|||
const processMemoryInfo = await process.getProcessMemoryInfo();
|
||||
const currentMemoryUsage = Math.max(
|
||||
processMemoryInfo.residentSet ?? 0,
|
||||
processMemoryInfo.private
|
||||
processMemoryInfo.private,
|
||||
);
|
||||
|
||||
const previousMemoryUsage = Math.max(
|
||||
previousRendererProcessMemoryInfo.private,
|
||||
previousRendererProcessMemoryInfo.residentSet ?? 0
|
||||
previousRendererProcessMemoryInfo.residentSet ?? 0,
|
||||
);
|
||||
const isSpiking =
|
||||
currentMemoryUsage - previousMemoryUsage >=
|
||||
|
@ -117,11 +117,11 @@ async function logSpikeRendererMemoryUsage() {
|
|||
await getNormalizedProcessMemoryInfo(processMemoryInfo);
|
||||
const normalizedPreviousProcessMemoryInfo =
|
||||
await getNormalizedProcessMemoryInfo(
|
||||
previousRendererProcessMemoryInfo
|
||||
previousRendererProcessMemoryInfo,
|
||||
);
|
||||
const cpuUsage = process.getCPUUsage();
|
||||
const heapStatistics = getNormalizedHeapStatistics(
|
||||
process.getHeapStatistics()
|
||||
process.getHeapStatistics(),
|
||||
);
|
||||
|
||||
ElectronLog.log("reporting renderer memory usage spike", {
|
||||
|
@ -140,11 +140,11 @@ async function logSpikeRendererMemoryUsage() {
|
|||
async function logRendererProcessStats() {
|
||||
const blinkMemoryInfo = getNormalizedBlinkMemoryInfo();
|
||||
const heapStatistics = getNormalizedHeapStatistics(
|
||||
process.getHeapStatistics()
|
||||
process.getHeapStatistics(),
|
||||
);
|
||||
const webFrameResourceUsage = getNormalizedWebFrameResourceUsage();
|
||||
const processMemoryInfo = await getNormalizedProcessMemoryInfo(
|
||||
await process.getProcessMemoryInfo()
|
||||
await process.getProcessMemoryInfo(),
|
||||
);
|
||||
ElectronLog.log("renderer process stats", {
|
||||
blinkMemoryInfo,
|
||||
|
@ -157,7 +157,7 @@ async function logRendererProcessStats() {
|
|||
export function setupMainProcessStatsLogger() {
|
||||
setInterval(
|
||||
logSpikeMainMemoryUsage,
|
||||
SPIKE_DETECTION_INTERVAL_IN_MICROSECONDS
|
||||
SPIKE_DETECTION_INTERVAL_IN_MICROSECONDS,
|
||||
);
|
||||
setInterval(logMainProcessStats, LOGGING_INTERVAL_IN_MICROSECONDS);
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ export function setupMainProcessStatsLogger() {
|
|||
export function setupRendererProcessStatsLogger() {
|
||||
setInterval(
|
||||
logSpikeRendererMemoryUsage,
|
||||
SPIKE_DETECTION_INTERVAL_IN_MICROSECONDS
|
||||
SPIKE_DETECTION_INTERVAL_IN_MICROSECONDS,
|
||||
);
|
||||
setInterval(logRendererProcessStats, LOGGING_INTERVAL_IN_MICROSECONDS);
|
||||
}
|
||||
|
@ -174,21 +174,21 @@ export async function logRendererProcessMemoryUsage(message: string) {
|
|||
const processMemoryInfo = await process.getProcessMemoryInfo();
|
||||
const processMemory = Math.max(
|
||||
processMemoryInfo.private,
|
||||
processMemoryInfo.residentSet ?? 0
|
||||
processMemoryInfo.residentSet ?? 0,
|
||||
);
|
||||
ElectronLog.log(
|
||||
"renderer ProcessMemory",
|
||||
message,
|
||||
convertBytesToHumanReadable(processMemory * 1024)
|
||||
convertBytesToHumanReadable(processMemory * 1024),
|
||||
);
|
||||
}
|
||||
|
||||
const getNormalizedProcessMemoryInfo = async (
|
||||
processMemoryInfo: Electron.ProcessMemoryInfo
|
||||
processMemoryInfo: Electron.ProcessMemoryInfo,
|
||||
) => {
|
||||
return {
|
||||
residentSet: convertBytesToHumanReadable(
|
||||
processMemoryInfo.residentSet * 1024
|
||||
processMemoryInfo.residentSet * 1024,
|
||||
),
|
||||
private: convertBytesToHumanReadable(processMemoryInfo.private * 1024),
|
||||
shared: convertBytesToHumanReadable(processMemoryInfo.shared * 1024),
|
||||
|
@ -199,40 +199,40 @@ const getNormalizedBlinkMemoryInfo = () => {
|
|||
const blinkMemoryInfo = process.getBlinkMemoryInfo();
|
||||
return {
|
||||
allocated: convertBytesToHumanReadable(
|
||||
blinkMemoryInfo.allocated * 1024
|
||||
blinkMemoryInfo.allocated * 1024,
|
||||
),
|
||||
total: convertBytesToHumanReadable(blinkMemoryInfo.total * 1024),
|
||||
};
|
||||
};
|
||||
|
||||
const getNormalizedHeapStatistics = (
|
||||
heapStatistics: Electron.HeapStatistics
|
||||
heapStatistics: Electron.HeapStatistics,
|
||||
) => {
|
||||
return {
|
||||
totalHeapSize: convertBytesToHumanReadable(
|
||||
heapStatistics.totalHeapSize * 1024
|
||||
heapStatistics.totalHeapSize * 1024,
|
||||
),
|
||||
totalHeapSizeExecutable: convertBytesToHumanReadable(
|
||||
heapStatistics.totalHeapSizeExecutable * 1024
|
||||
heapStatistics.totalHeapSizeExecutable * 1024,
|
||||
),
|
||||
totalPhysicalSize: convertBytesToHumanReadable(
|
||||
heapStatistics.totalPhysicalSize * 1024
|
||||
heapStatistics.totalPhysicalSize * 1024,
|
||||
),
|
||||
totalAvailableSize: convertBytesToHumanReadable(
|
||||
heapStatistics.totalAvailableSize * 1024
|
||||
heapStatistics.totalAvailableSize * 1024,
|
||||
),
|
||||
usedHeapSize: convertBytesToHumanReadable(
|
||||
heapStatistics.usedHeapSize * 1024
|
||||
heapStatistics.usedHeapSize * 1024,
|
||||
),
|
||||
|
||||
heapSizeLimit: convertBytesToHumanReadable(
|
||||
heapStatistics.heapSizeLimit * 1024
|
||||
heapStatistics.heapSizeLimit * 1024,
|
||||
),
|
||||
mallocedMemory: convertBytesToHumanReadable(
|
||||
heapStatistics.mallocedMemory * 1024
|
||||
heapStatistics.mallocedMemory * 1024,
|
||||
),
|
||||
peakMallocedMemory: convertBytesToHumanReadable(
|
||||
heapStatistics.peakMallocedMemory * 1024
|
||||
heapStatistics.peakMallocedMemory * 1024,
|
||||
),
|
||||
doesZapGarbage: heapStatistics.doesZapGarbage,
|
||||
};
|
||||
|
@ -244,51 +244,51 @@ const getNormalizedWebFrameResourceUsage = () => {
|
|||
images: {
|
||||
count: webFrameResourceUsage.images.count,
|
||||
size: convertBytesToHumanReadable(
|
||||
webFrameResourceUsage.images.size
|
||||
webFrameResourceUsage.images.size,
|
||||
),
|
||||
liveSize: convertBytesToHumanReadable(
|
||||
webFrameResourceUsage.images.liveSize
|
||||
webFrameResourceUsage.images.liveSize,
|
||||
),
|
||||
},
|
||||
scripts: {
|
||||
count: webFrameResourceUsage.scripts.count,
|
||||
size: convertBytesToHumanReadable(
|
||||
webFrameResourceUsage.scripts.size
|
||||
webFrameResourceUsage.scripts.size,
|
||||
),
|
||||
liveSize: convertBytesToHumanReadable(
|
||||
webFrameResourceUsage.scripts.liveSize
|
||||
webFrameResourceUsage.scripts.liveSize,
|
||||
),
|
||||
},
|
||||
cssStyleSheets: {
|
||||
count: webFrameResourceUsage.cssStyleSheets.count,
|
||||
size: convertBytesToHumanReadable(
|
||||
webFrameResourceUsage.cssStyleSheets.size
|
||||
webFrameResourceUsage.cssStyleSheets.size,
|
||||
),
|
||||
liveSize: convertBytesToHumanReadable(
|
||||
webFrameResourceUsage.cssStyleSheets.liveSize
|
||||
webFrameResourceUsage.cssStyleSheets.liveSize,
|
||||
),
|
||||
},
|
||||
xslStyleSheets: {
|
||||
count: webFrameResourceUsage.xslStyleSheets.count,
|
||||
size: convertBytesToHumanReadable(
|
||||
webFrameResourceUsage.xslStyleSheets.size
|
||||
webFrameResourceUsage.xslStyleSheets.size,
|
||||
),
|
||||
liveSize: convertBytesToHumanReadable(
|
||||
webFrameResourceUsage.xslStyleSheets.liveSize
|
||||
webFrameResourceUsage.xslStyleSheets.liveSize,
|
||||
),
|
||||
},
|
||||
fonts: {
|
||||
count: webFrameResourceUsage.fonts.count,
|
||||
size: convertBytesToHumanReadable(webFrameResourceUsage.fonts.size),
|
||||
liveSize: convertBytesToHumanReadable(
|
||||
webFrameResourceUsage.fonts.liveSize
|
||||
webFrameResourceUsage.fonts.liveSize,
|
||||
),
|
||||
},
|
||||
other: {
|
||||
count: webFrameResourceUsage.other.count,
|
||||
size: convertBytesToHumanReadable(webFrameResourceUsage.other.size),
|
||||
liveSize: convertBytesToHumanReadable(
|
||||
webFrameResourceUsage.other.liveSize
|
||||
webFrameResourceUsage.other.liveSize,
|
||||
),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -21,7 +21,7 @@ function generateTempName(length: number) {
|
|||
const charactersLength = CHARACTERS.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += CHARACTERS.charAt(
|
||||
Math.floor(Math.random() * charactersLength)
|
||||
Math.floor(Math.random() * charactersLength),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -2,10 +2,10 @@ import { WatchMapping } from "../types";
|
|||
|
||||
export function isMappingPresent(
|
||||
watchMappings: WatchMapping[],
|
||||
folderPath: string
|
||||
folderPath: string,
|
||||
) {
|
||||
const watchMapping = watchMappings?.find(
|
||||
(mapping) => mapping.folderPath === folderPath
|
||||
(mapping) => mapping.folderPath === folderPath,
|
||||
);
|
||||
return !!watchMapping;
|
||||
}
|
||||
|
|
1
desktop/thirdparty/next-electron-server
vendored
1
desktop/thirdparty/next-electron-server
vendored
|
@ -1 +0,0 @@
|
|||
Subproject commit a88030295c89dd8f43d9e3a45025678d95c78a45
|
|
@ -2441,8 +2441,10 @@ natural-compare@^1.4.0:
|
|||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||
|
||||
"next-electron-server@file:./thirdparty/next-electron-server":
|
||||
version "0.0.8"
|
||||
next-electron-server@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/next-electron-server/-/next-electron-server-1.0.0.tgz#03e133ed64a5ef671b6c6409f908c4901b1828cb"
|
||||
integrity sha512-fTUaHwT0Jry2fbdUSIkAiIqgDAInI5BJFF4/j90/okvZCYlyx6yxpXB30KpzmOG6TN/ESwyvsFJVvS2WHT8PAA==
|
||||
|
||||
node-addon-api@^1.6.3:
|
||||
version "1.7.2"
|
||||
|
@ -2697,10 +2699,10 @@ prettier-plugin-packagejson@^2.4:
|
|||
sort-package-json "2.8.0"
|
||||
synckit "0.9.0"
|
||||
|
||||
prettier@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a"
|
||||
integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==
|
||||
prettier@^3:
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368"
|
||||
integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==
|
||||
|
||||
pretty-bytes@^4.0.2:
|
||||
version "4.0.2"
|
||||
|
|
Loading…
Add table
Reference in a new issue