diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..57680f9dc43011e0e57e3c57dca5d4a9deaf1376
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+# Set line endings of shell scripts to LF, even on Windows, otherwise execution
+# within Docker fails.
+*.sh text eol=lf
diff --git a/.github/workflows/auth-lint.yml b/.github/workflows/auth-lint.yml
index fff7fb75f5243505b100af1cbb6f0ad5b3612c62..1b45a2d324ffaaca05277d6265a7d5d725be0dd9 100644
--- a/.github/workflows/auth-lint.yml
+++ b/.github/workflows/auth-lint.yml
@@ -1,11 +1,9 @@
name: "Lint (auth)"
on:
- # Run on every push to branches (this also covers pull requests)
+ # Run on every push to a branch other than main that changes auth/
push:
- # See: [Note: Specify branch when specifying a path filter]
- branches: ["**"]
- # Only run if something changes in these paths
+ branches-ignore: [main]
paths:
- "auth/**"
- ".github/workflows/auth-lint.yml"
diff --git a/.github/workflows/mobile-lint.yml b/.github/workflows/mobile-lint.yml
index c2e54d38ae757bebf81ea645817f1ed5296e1b29..7b5e5fbedbcb5ab7a2f75510ee65cf2d0a67bb2a 100644
--- a/.github/workflows/mobile-lint.yml
+++ b/.github/workflows/mobile-lint.yml
@@ -1,11 +1,9 @@
name: "Lint (mobile)"
on:
- # Run on every push (this also covers pull requests)
+ # Run on every push to a branch other than main that changes mobile/
push:
- # See: [Note: Specify branch when specifying a path filter]
- branches: ["**"]
- # Only run if something changes in these paths
+ branches-ignore: [main]
paths:
- "mobile/**"
- ".github/workflows/mobile-lint.yml"
diff --git a/.github/workflows/server-lint.yml b/.github/workflows/server-lint.yml
index e23036a6ed48c94b10f95245dd41f568df233201..c051d029011697405346b18aef71cd21332c6e7c 100644
--- a/.github/workflows/server-lint.yml
+++ b/.github/workflows/server-lint.yml
@@ -1,11 +1,9 @@
name: "Lint (server)"
on:
- # Run on every push (this also covers pull requests)
+ # Run on every push to a branch other than main that changes server/
push:
- # See: [Note: Specify branch when specifying a path filter]
- branches: ["**"]
- # Only run if something changes in these paths
+ branches-ignore: [main]
paths:
- "server/**"
- ".github/workflows/server-lint.yml"
diff --git a/.github/workflows/web-lint.yml b/.github/workflows/web-lint.yml
index a905069f67adc138ab2542d6711cdbdacc4ca5e9..7f5d270029ee6a79054ab11e37fb5cce9a0de412 100644
--- a/.github/workflows/web-lint.yml
+++ b/.github/workflows/web-lint.yml
@@ -1,20 +1,9 @@
name: "Lint (web)"
on:
- # Run on every push (this also covers pull requests)
+ # Run on every push to a branch other than main that changes web/
push:
- # [Note: Specify branch when specifying a path filter]
- #
- # Path filters are ignored for tag pushes, which causes this workflow to
- # always run when we push a tag. Defining an explicit branch solves the
- # issue. From GitHub's docs:
- #
- # > if you define both branches/branches-ignore and paths/paths-ignore,
- # > the workflow will only run when both filters are satisfied.
- #
- # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
- branches: ["**"]
- # Only run if something changes in these paths
+ branches-ignore: [main]
paths:
- "web/**"
- ".github/workflows/web-lint.yml"
diff --git a/.github/workflows/web-preview.yml b/.github/workflows/web-preview.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4e86d9a816f54bbb130df2ef2572ca7f85f21f6c
--- /dev/null
+++ b/.github/workflows/web-preview.yml
@@ -0,0 +1,52 @@
+name: "Preview (web)"
+
+on:
+ workflow_dispatch:
+ inputs:
+ app:
+ description: "App to build and deploy"
+ type: choice
+ required: true
+ default: "photos"
+ options:
+ - "accounts"
+ - "auth"
+ - "cast"
+ - "photos"
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+
+ defaults:
+ run:
+ working-directory: web
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+
+ - name: Setup node and enable yarn caching
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: "yarn"
+ cache-dependency-path: "docs/yarn.lock"
+
+ - name: Install dependencies
+ run: yarn install
+
+ - name: Build ${{ inputs.app }}
+ run: yarn build:${{ inputs.app }}
+
+ - name: Publish ${{ inputs.app }} to preview
+ uses: cloudflare/pages-action@1
+ with:
+ accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
+ apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+ projectName: ente
+ branch: preview
+ directory: web/apps/${{ inputs.app }}/out
+ wranglerVersion: "3"
diff --git a/.gitmodules b/.gitmodules
index a3a52e7be57a7057042ff5a6241ee609e8f17717..36834f2cd2865564b709f696a1ba23cc1366f8b3 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -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
diff --git a/desktop/.github/workflows/build.yml b/desktop/.github/workflows/build.yml
index c43dfcfdfed0f637f0b328f9117ab3a0afa9e615..acd744c05675ab0042fbf7035b2ec21a1bd04af3 100644
--- a/desktop/.github/workflows/build.yml
+++ b/desktop/.github/workflows/build.yml
@@ -52,7 +52,4 @@ jobs:
# macOS notarization API key
API_KEY_ID: ${{ secrets.api_key_id }}
API_KEY_ISSUER_ID: ${{ secrets.api_key_issuer_id}}
- # setry crash reporting token
- SENTRY_AUTH_TOKEN: ${{secrets.sentry_auth_token}}
- NEXT_PUBLIC_DISABLE_SENTRY: ${{secrets.next_public_disable_sentry}}
USE_HARD_LINKS: false
diff --git a/desktop/.gitignore b/desktop/.gitignore
index b01288f0a9bfcb3a0db1dafe4d32c7ec0cb81e30..9b7e0cc60875ffc1689b9589e4c7fa625fe3f1d5 100644
--- a/desktop/.gitignore
+++ b/desktop/.gitignore
@@ -11,10 +11,11 @@ node_modules/
.env
.env.*.local
-# Generated code during build
-# - tsc transpiles src/**/*.ts and emits the generated JS into build/app
-# - The out dir from the photos web app is symlinked to build/out
-build/
+# tsc transpiles src/**/*.ts and emits the generated JS into app
+app/
+
+# out is a symlink to the photos web app's dir
+out
# electron-builder
dist/
diff --git a/desktop/.prettierignore b/desktop/.prettierignore
deleted file mode 100644
index c2ed1358c4a25e8c406f7a54a0d045b8192ec8dd..0000000000000000000000000000000000000000
--- a/desktop/.prettierignore
+++ /dev/null
@@ -1 +0,0 @@
-thirdparty/
diff --git a/desktop/CHANGELOG.md b/desktop/CHANGELOG.md
index 0a1602b94f70731cce534a0214bfef6444a02a95..73aae2397d4ed847c298cc10913e062a7d6f8971 100644
--- a/desktop/CHANGELOG.md
+++ b/desktop/CHANGELOG.md
@@ -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
diff --git a/desktop/README.md b/desktop/README.md
index 9d637de04ff64472c2ffbfe113c3e39176831c74..da74b133ffa4236500b5ef35d1965fb32ac8885f 100644
--- a/desktop/README.md
+++ b/desktop/README.md
@@ -10,40 +10,28 @@ To know more about Ente, see [our main README](../README.md) or visit
## Building from source
-> [!CAUTION]
->
-> We moved a few things around when switching to a monorepo recently, and the
-> desktop app is not currently building with these instructions below. Hang
-> tight, we're on it, and will fix soon.
-
-Fetch submodules
-
-```sh
-git submodule update --init --recursive
-```
-
Install dependencies
```sh
yarn install
```
-Run the app
+Run in development mode (with hot reload)
```sh
yarn dev
```
-To recompile automatically using electron-reload, run this in a separate
-terminal:
-
-```bash
-yarn watch
-```
+> [!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`.
-`yarn dev` is handy during development, but if you wish, you can also create a
-binary for your platform by using
+Or create a binary for your platform
```sh
yarn build
```
+
+That's the gist of it. For more development related documentation, see
+[docs](docs/README.md).
diff --git a/desktop/resources/entitlements.mac.plist b/desktop/build/entitlements.mac.plist
similarity index 100%
rename from desktop/resources/entitlements.mac.plist
rename to desktop/build/entitlements.mac.plist
diff --git a/desktop/resources/error.html b/desktop/build/error.html
similarity index 98%
rename from desktop/resources/error.html
rename to desktop/build/error.html
index 3291f7789b9096fb52d5f2cbeb7c35fe6de41596..cf1ea149d5ab67c3d3e3b49da26ce9c5aa30fcb5 100644
--- a/desktop/resources/error.html
+++ b/desktop/build/error.html
@@ -1,4 +1,4 @@
-
+
diff --git a/desktop/resources/ggmlclip-linux b/desktop/build/ggmlclip-linux
similarity index 100%
rename from desktop/resources/ggmlclip-linux
rename to desktop/build/ggmlclip-linux
diff --git a/desktop/resources/ggmlclip-mac b/desktop/build/ggmlclip-mac
similarity index 100%
rename from desktop/resources/ggmlclip-mac
rename to desktop/build/ggmlclip-mac
diff --git a/desktop/resources/ggmlclip-windows.exe b/desktop/build/ggmlclip-windows.exe
similarity index 100%
rename from desktop/resources/ggmlclip-windows.exe
rename to desktop/build/ggmlclip-windows.exe
diff --git a/desktop/resources/icon.icns b/desktop/build/icon.icns
similarity index 100%
rename from desktop/resources/icon.icns
rename to desktop/build/icon.icns
diff --git a/desktop/resources/icon.png b/desktop/build/icon.png
similarity index 100%
rename from desktop/resources/icon.png
rename to desktop/build/icon.png
diff --git a/desktop/resources/image-magick b/desktop/build/image-magick
similarity index 100%
rename from desktop/resources/image-magick
rename to desktop/build/image-magick
diff --git a/desktop/resources/msvcp140d.dll b/desktop/build/msvcp140d.dll
similarity index 100%
rename from desktop/resources/msvcp140d.dll
rename to desktop/build/msvcp140d.dll
diff --git a/desktop/resources/splash.html b/desktop/build/splash.html
similarity index 99%
rename from desktop/resources/splash.html
rename to desktop/build/splash.html
index 7d4a195e579cdf0bb8433e9e5da2c0c64150b8da..199c316010f9608ceee0e0c113d13c86e7213a5c 100644
--- a/desktop/resources/splash.html
+++ b/desktop/build/splash.html
@@ -1,4 +1,4 @@
-
+
diff --git a/desktop/resources/taskbar-icon-Template.png b/desktop/build/taskbar-icon-Template.png
similarity index 100%
rename from desktop/resources/taskbar-icon-Template.png
rename to desktop/build/taskbar-icon-Template.png
diff --git a/desktop/resources/taskbar-icon-Template@2x.png b/desktop/build/taskbar-icon-Template@2x.png
similarity index 100%
rename from desktop/resources/taskbar-icon-Template@2x.png
rename to desktop/build/taskbar-icon-Template@2x.png
diff --git a/desktop/resources/taskbar-icon-Template@3x.png b/desktop/build/taskbar-icon-Template@3x.png
similarity index 100%
rename from desktop/resources/taskbar-icon-Template@3x.png
rename to desktop/build/taskbar-icon-Template@3x.png
diff --git a/desktop/resources/taskbar-icon.png b/desktop/build/taskbar-icon.png
similarity index 100%
rename from desktop/resources/taskbar-icon.png
rename to desktop/build/taskbar-icon.png
diff --git a/desktop/resources/taskbar-icon@2x.png b/desktop/build/taskbar-icon@2x.png
similarity index 100%
rename from desktop/resources/taskbar-icon@2x.png
rename to desktop/build/taskbar-icon@2x.png
diff --git a/desktop/resources/taskbar-icon@3x.png b/desktop/build/taskbar-icon@3x.png
similarity index 100%
rename from desktop/resources/taskbar-icon@3x.png
rename to desktop/build/taskbar-icon@3x.png
diff --git a/desktop/resources/ucrtbased.dll b/desktop/build/ucrtbased.dll
similarity index 100%
rename from desktop/resources/ucrtbased.dll
rename to desktop/build/ucrtbased.dll
diff --git a/desktop/resources/vcruntime140_1d.dll b/desktop/build/vcruntime140_1d.dll
similarity index 100%
rename from desktop/resources/vcruntime140_1d.dll
rename to desktop/build/vcruntime140_1d.dll
diff --git a/desktop/resources/vcruntime140d.dll b/desktop/build/vcruntime140d.dll
similarity index 100%
rename from desktop/resources/vcruntime140d.dll
rename to desktop/build/vcruntime140d.dll
diff --git a/desktop/resources/version.html b/desktop/build/version.html
similarity index 98%
rename from desktop/resources/version.html
rename to desktop/build/version.html
index 03bf806a6b07c2fe082d10e0de258f74cff5a10c..b2038edd7ae735a4e4751c74c54660ef5e6722ef 100644
--- a/desktop/resources/version.html
+++ b/desktop/build/version.html
@@ -1,4 +1,4 @@
-
+
Electron Updater Example
diff --git a/desktop/resources/window-icon.png b/desktop/build/window-icon.png
similarity index 100%
rename from desktop/resources/window-icon.png
rename to desktop/build/window-icon.png
diff --git a/desktop/docs/dependencies.md b/desktop/docs/dependencies.md
index 8703331a223fab71740a766219b7d2689d615208..6b019672341bf1d97d2136656a1c95c090185010 100644
--- a/desktop/docs/dependencies.md
+++ b/desktop/docs/dependencies.md
@@ -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.
diff --git a/desktop/docs/dev.md b/desktop/docs/dev.md
new file mode 100644
index 0000000000000000000000000000000000000000..bfa80df6907623c47cc4c5c7a3559de1d0c94479
--- /dev/null
+++ b/desktop/docs/dev.md
@@ -0,0 +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.
diff --git a/desktop/docs/electron.md b/desktop/docs/electron.md
index e6c56b55bbd1e8dcda312eb5d70012062a50bd8e..84c47e329b77b1bc290ab429a7e5b5edf23c5d76 100644
--- a/desktop/docs/electron.md
+++ b/desktop/docs/electron.md
@@ -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.
diff --git a/desktop/package.json b/desktop/package.json
index b0ea95b093ec9fcf0bf12cccc21faa4c9408d7aa..8d90fca6e6da7243df21978efd967770a4078af2 100644
--- a/desktop/package.json
+++ b/desktop/package.json
@@ -4,21 +4,22 @@
"private": true,
"description": "Desktop client for Ente Photos",
"author": "Ente ",
- "main": "build/app/main.js",
+ "main": "app/main.js",
"scripts": {
- "build": "mkdir -p build && yarn build-renderer && yarn build-main",
- "build-main": "tsc && electron-builder --config.compression=store",
- "build-renderer": "cd ../web && yarn install && yarn build:photos && cd ../desktop/build && rm -f out && ln -sf ../../web/apps/photos/out",
- "dev": "concurrently \"yarn dev-main\" \"yarn dev-renderer\"",
- "dev-main": "tsc && electron build/app/main.js",
- "dev-renderer": "cd ../web && yarn install && yarn dev:photos && cd ../desktop/build && rm -f out && ln -sf ../../web/apps/photos/out",
+ "build": "yarn build-renderer && yarn build-main",
+ "build-main": "tsc && electron-builder",
+ "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 --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": {
- "@sentry/electron": "^2.5.1",
"any-shell-escape": "^0.1.1",
"auto-launch": "^5.0.5",
"chokidar": "^3.5.3",
@@ -31,14 +32,13 @@
"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",
"promise-fs": "^2.1.1"
},
"devDependencies": {
- "@sentry/cli": "^1.68.0",
"@types/auto-launch": "^5.0.2",
"@types/ffmpeg-static": "^3.0.1",
"@types/get-folder-size": "^2.0.0",
@@ -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"
@@ -112,26 +112,20 @@
"x64ArchFiles": "Contents/Resources/ggmlclip-mac"
},
"afterSign": "electron-builder-notarize",
- "extraFiles": [
- {
- "from": "resources",
- "to": "resources",
- "filter": [
- "**/*"
- ]
- }
- ],
"asarUnpack": [
"node_modules/ffmpeg-static/bin/${os}/${arch}/ffmpeg",
"node_modules/ffmpeg-static/index.js",
"node_modules/ffmpeg-static/package.json"
],
- "files": [
- "build/app/**/*",
+ "extraFiles": [
{
- "from": "build/out",
- "to": "out"
+ "from": "build",
+ "to": "resources"
}
+ ],
+ "files": [
+ "app/**/*",
+ "out"
]
},
"productName": "ente",
diff --git a/desktop/sentry.properties b/desktop/sentry.properties
deleted file mode 100644
index d9303317cff6dc5f7b5b88b879e1caebac596156..0000000000000000000000000000000000000000
--- a/desktop/sentry.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-defaults.url=https://sentry.ente.io/
-defaults.org=ente
-defaults.project=desktop-photos
diff --git a/desktop/src/api/cache.ts b/desktop/src/api/cache.ts
index eabcef9808fefb8ed43f02df1080444128e3a202..86ba4378c3e3137f1ddb990b08c19e8d060f8e33 100644
--- a/desktop/src/api/cache.ts
+++ b/desktop/src/api/cache.ts
@@ -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 {
await ipcRenderer.invoke("set-custom-cache-directory", directory);
}
diff --git a/desktop/src/api/clip.ts b/desktop/src/api/clip.ts
index 56772474d859d2a10bb4f4d6522b6ec8ade2db23..d2469e7b984e858151a6344e45ca58933a72347b 100644
--- a/desktop/src/api/clip.ts
+++ b/desktop/src/api/clip.ts
@@ -5,7 +5,7 @@ import { isExecError, parseExecError } from "../utils/error";
export async function computeImageEmbedding(
model: Model,
- imageData: Uint8Array
+ imageData: Uint8Array,
): Promise {
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 {
try {
const embedding = await ipcRenderer.invoke(
"compute-text-embedding",
model,
- text
+ text,
);
return embedding;
} catch (err) {
diff --git a/desktop/src/api/common.ts b/desktop/src/api/common.ts
index 1c6281d489f064936b7ee3b6c1c21f94ce7ba49f..f185069817eeb44404937757e1095ed06cf45095 100644
--- a/desktop/src/api/common.ts
+++ b/desktop/src/api/common.ts
@@ -36,9 +36,4 @@ export const getPlatform = async (): Promise<"mac" | "windows" | "linux"> => {
}
};
-export {
- getSentryUserID,
- logToDisk,
- openLogDirectory,
- updateOptOutOfCrashReports,
-} from "../services/logging";
+export { logToDisk, openLogDirectory } from "../services/logging";
diff --git a/desktop/src/api/electronStore.ts b/desktop/src/api/electronStore.ts
index 9475134c69c752600123b6e7479f5b4f24c74a45..5f84776e1ad07aa11bb1018e7f2b86ab7d586abe 100644
--- a/desktop/src/api/electronStore.ts
+++ b/desktop/src/api/electronStore.ts
@@ -2,7 +2,6 @@ import { logError } from "../services/logging";
import { keysStore } from "../stores/keys.store";
import { safeStorageStore } from "../stores/safeStorage.store";
import { uploadStatusStore } from "../stores/upload.store";
-import { userPreferencesStore } from "../stores/userPreferences.store";
import { watchStore } from "../stores/watch.store";
export const clearElectronStore = () => {
@@ -11,7 +10,6 @@ export const clearElectronStore = () => {
keysStore.clear();
safeStorageStore.clear();
watchStore.clear();
- userPreferencesStore.delete("optOutOfCrashReports");
} catch (e) {
logError(e, "error while clearing electron store");
throw e;
diff --git a/desktop/src/api/export.ts b/desktop/src/api/export.ts
index f5eef4f59a555fbe724c9553f3a6865d791876eb..8adaa236fbbd7197403c1c04351871d511cc9da4 100644
--- a/desktop/src/api/export.ts
+++ b/desktop/src/api/export.ts
@@ -13,7 +13,7 @@ export const checkExistsAndCreateDir = async (dirPath: string) => {
export const saveStreamToDisk = async (
filePath: string,
- fileStream: ReadableStream
+ fileStream: ReadableStream,
) => {
await writeStream(filePath, fileStream);
};
diff --git a/desktop/src/api/ffmpeg.ts b/desktop/src/api/ffmpeg.ts
index ef2e2291e0ce1ef0e80a138e4d0ddec757a269e0..9d11183a8c075bcce6ab0e068de693519077553a 100644
--- a/desktop/src/api/ffmpeg.ts
+++ b/desktop/src/api/ffmpeg.ts
@@ -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 {
diff --git a/desktop/src/api/imageProcessor.ts b/desktop/src/api/imageProcessor.ts
index 84d447a54ae12d83977cf77e0a7536ad711c1acf..9d93aecd1be47ff30613796afb51603937fcf070 100644
--- a/desktop/src/api/imageProcessor.ts
+++ b/desktop/src/api/imageProcessor.ts
@@ -8,7 +8,7 @@ import { isPlatform } from "../utils/common/platform";
export async function convertToJPEG(
fileData: Uint8Array,
- filename: string
+ filename: string,
): Promise {
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 {
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 {
diff --git a/desktop/src/api/safeStorage.ts b/desktop/src/api/safeStorage.ts
index 89529013ad4fb428f968a00ba86bacda4bc4bd08..64c48919502599ea537d616691a3410b5b7eed46 100644
--- a/desktop/src/api/safeStorage.ts
+++ b/desktop/src/api/safeStorage.ts
@@ -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 {
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);
}
diff --git a/desktop/src/api/system.ts b/desktop/src/api/system.ts
index 7a8635a9f1fc14afe324275f0cbeb66cd908ef44..a4dc91e054de1d0c02a9ff3e5a3cfb03416621b0 100644
--- a/desktop/src/api/system.ts
+++ b/desktop/src/api/system.ts
@@ -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) => {
diff --git a/desktop/src/api/upload.ts b/desktop/src/api/upload.ts
index fc137452c1c4d98f522c6cbf1da5164dfb172212..280ff084ff50cb1a49572e39647d141b26f93ff5 100644
--- a/desktop/src/api/upload.ts
+++ b/desktop/src/api/upload.ts
@@ -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[] = [];
diff --git a/desktop/src/api/watch.ts b/desktop/src/api/watch.ts
index 9ebb5b4616f5f9d0fb16d14f925307f0ca33fe94..1b7a4ac3cfccbafbf5e42c1c74f35f88b94380a1 100644
--- a/desktop/src/api/watch.ts
+++ b/desktop/src/api/watch.ts
@@ -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,
removeFile: (path: string) => Promise,
- removeFolder: (folderPath: string) => Promise
+ removeFolder: (folderPath: string) => Promise,
) {
ipcRenderer.removeAllListeners("watch-add");
ipcRenderer.removeAllListeners("watch-change");
diff --git a/desktop/src/config/index.ts b/desktop/src/config/index.ts
deleted file mode 100644
index a27de5aa24e51930e245896d5401bbd33367cb61..0000000000000000000000000000000000000000
--- a/desktop/src/config/index.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-const PROD_HOST_URL: string = "ente://app";
-const RENDERER_OUTPUT_DIR: string = "./out";
-const LOG_FILENAME = "ente.log";
-const MAX_LOG_SIZE = 50 * 1024 * 1024; // 50MB
-
-const FILE_STREAM_CHUNK_SIZE: number = 4 * 1024 * 1024;
-
-const SENTRY_DSN = "https://759d8498487a81ac33a0c2efa2a42c4f@sentry.ente.io/9";
-
-const RELEASE_VERSION = require("../../package.json").version;
-
-export {
- PROD_HOST_URL,
- RENDERER_OUTPUT_DIR,
- FILE_STREAM_CHUNK_SIZE,
- LOG_FILENAME,
- MAX_LOG_SIZE,
- SENTRY_DSN,
- RELEASE_VERSION,
-};
diff --git a/desktop/src/main.ts b/desktop/src/main.ts
index 3663b7b8aac93d7d742223f96ab004c4689a4f12..a280a9b59e93eff2da14c74354a84be4e3b7fb3f 100644
--- a/desktop/src/main.ts
+++ b/desktop/src/main.ts
@@ -1,7 +1,7 @@
import { app, BrowserWindow } from "electron";
+import electronReload from "electron-reload";
+import serveNextAt from "next-electron-server";
import { initWatcher } from "./services/chokidar";
-import { initSentry } from "./services/sentry";
-import { getOptOutOfCrashReports } from "./services/userPreference";
import { isDev } from "./utils/common";
import { addAllowOriginHeader } from "./utils/cors";
import { createWindow } from "./utils/createWindow";
@@ -16,9 +16,7 @@ import {
handleUpdates,
logSystemInfo,
setupMacWindowOnDockIconClick,
- setupMainHotReload,
setupMainMenu,
- setupNextElectronServe,
setupTrayItem,
} from "./utils/main";
import { setupMainProcessStatsLogger } from "./utils/processStats";
@@ -29,8 +27,6 @@ let appIsQuitting = false;
let updateIsAvailable = false;
-let optedOutOfCrashReports = false;
-
export const isAppQuitting = (): boolean => {
return appIsQuitting;
};
@@ -42,22 +38,48 @@ export const setIsAppQuitting = (value: boolean): void => {
export const isUpdateAvailable = (): boolean => {
return updateIsAvailable;
};
+
export const setIsUpdateAvailable = (value: boolean): void => {
updateIsAvailable = value;
};
-export const hasOptedOutOfCrashReports = (): boolean => {
- return optedOutOfCrashReports;
+/**
+ * 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, {});
+ }
};
-export const updateOptOutOfCrashReports = (value: boolean): void => {
- optedOutOfCrashReports = value;
+/**
+ * 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();
@@ -83,11 +105,6 @@ if (!gotTheLock) {
app.on("ready", async () => {
logSystemInfo();
setupMainProcessStatsLogger();
- const hasOptedOutOfCrashReports = getOptOutOfCrashReports();
- updateOptOutOfCrashReports(hasOptedOutOfCrashReports);
- if (!hasOptedOutOfCrashReports) {
- initSentry();
- }
mainWindow = await createWindow();
const tray = setupTrayItem(mainWindow);
const watcher = initWatcher(mainWindow);
diff --git a/desktop/src/preload.ts b/desktop/src/preload.ts
index af4ef32227a91bc8be6f17190887239188b01c69..a602e76bbd1e78b23c06b909abfb484dda631bec 100644
--- a/desktop/src/preload.ts
+++ b/desktop/src/preload.ts
@@ -8,12 +8,10 @@ import { computeImageEmbedding, computeTextEmbedding } from "./api/clip";
import {
getAppVersion,
getPlatform,
- getSentryUserID,
logToDisk,
openDirectory,
openLogDirectory,
selectDirectory,
- updateOptOutOfCrashReports,
} from "./api/common";
import { clearElectronStore } from "./api/electronStore";
import {
@@ -61,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();
@@ -108,7 +104,6 @@ windowObject["ElectronAPIs"] = {
registerUpdateEventListener,
updateAndRestart,
skipAppUpdate,
- getSentryUserID,
getAppVersion,
runFFmpegCmd,
muteUpdateNotification,
@@ -120,7 +115,6 @@ windowObject["ElectronAPIs"] = {
deleteFolder,
rename,
deleteFile,
- updateOptOutOfCrashReports,
computeImageEmbedding,
computeTextEmbedding,
getPlatform,
diff --git a/desktop/src/services/appUpdater.ts b/desktop/src/services/appUpdater.ts
index c6af2541ae37e547723037128af8ca76899e7f2d..2ddcef7047cb140df98ad56c9487ff580c83bf62 100644
--- a/desktop/src/services/appUpdater.ts
+++ b/desktop/src/services/appUpdater.ts
@@ -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);
}
diff --git a/desktop/src/services/clipService.ts b/desktop/src/services/clipService.ts
index 9e3a6a5b069d1fea66874cece013e9635f355dfa..4a808d7a4e42907d9d14568f8c30195c03545c92 100644
--- a/desktop/src/services/clipService.ts
+++ b/desktop/src/services/clipService.ts
@@ -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 {
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 {
try {
const clipModelPath = await getClipImageModelPath("ggml");
@@ -263,7 +263,7 @@ export async function computeGGMLImageEmbedding(
}
export async function computeONNXImageEmbedding(
- inputFilePath: string
+ inputFilePath: string,
): Promise {
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 {
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 {
try {
const clipModelPath = await getClipTextModelPath("ggml");
@@ -339,7 +339,7 @@ export async function computeGGMLTextEmbedding(
}
export async function computeONNXTextEmbedding(
- text: string
+ text: string,
): Promise {
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");
diff --git a/desktop/src/services/diskCache.ts b/desktop/src/services/diskCache.ts
index be9199cbf9494a02d260db4f3f4aa05d955910e7..6861bba9f808fef1e78296b2f94dec47e5229083 100644
--- a/desktop/src/services/diskCache.ts
+++ b/desktop/src/services/diskCache.ts
@@ -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 {
@@ -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 {
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;
diff --git a/desktop/src/services/diskLRU.ts b/desktop/src/services/diskLRU.ts
index cf10b73510221cd3d94780927997e113e9a05998..44b05c099410c1c6093917d9ac1b8f1822db2cba 100644
--- a/desktop/src/services/diskLRU.ts
+++ b/desktop/src/services/diskLRU.ts
@@ -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 {
result = result || { atime: new Date(), path: "" };
diff --git a/desktop/src/services/ffmpeg.ts b/desktop/src/services/ffmpeg.ts
index 6a76169b78a60e75d4fc9dc9140337682c47b9c7..e0a9157909bfa3d7040f1c3c7d1dee5f498b4778 100644
--- a/desktop/src/services/ffmpeg.ts
+++ b/desktop/src/services/ffmpeg.ts
@@ -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 });
diff --git a/desktop/src/services/fs.ts b/desktop/src/services/fs.ts
index cbbe06146702e6adbb3c6b36242d99a9eafa9260..06d413c1f08e353160cc816760217931ffc93cf1 100644
--- a/desktop/src/services/fs.ts
+++ b/desktop/src/services/fs.ts
@@ -3,10 +3,11 @@ import StreamZip from "node-stream-zip";
import path from "path";
import * as fs from "promise-fs";
import { Readable } from "stream";
-import { FILE_STREAM_CHUNK_SIZE } from "../config";
import { ElectronFile } from "../types";
import { logError } from "./logging";
+const FILE_STREAM_CHUNK_SIZE: number = 4 * 1024 * 1024;
+
// https://stackoverflow.com/a/63111390
export const getDirFilePaths = async (dirPath: string) => {
if (!(await fs.stat(dirPath)).isDirectory()) {
@@ -37,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) {
@@ -102,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 = {
@@ -203,7 +204,7 @@ export async function isFolder(dirPath: string) {
}
export const convertBrowserStreamToNode = (
- fileStream: ReadableStream
+ fileStream: ReadableStream,
) => {
const reader = fileStream.getReader();
const rs = new Readable();
@@ -228,7 +229,7 @@ export const convertBrowserStreamToNode = (
export async function writeNodeStream(
filePath: string,
- fileStream: NodeJS.ReadableStream
+ fileStream: NodeJS.ReadableStream,
) {
const writeable = fs.createWriteStream(filePath);
@@ -251,7 +252,7 @@ export async function writeNodeStream(
export async function writeStream(
filePath: string,
- fileStream: ReadableStream
+ fileStream: ReadableStream,
) {
const readable = convertBrowserStreamToNode(fileStream);
await writeNodeStream(filePath, readable);
@@ -266,7 +267,7 @@ export async function readTextFile(filePath: string) {
export async function moveFile(
sourcePath: string,
- destinationPath: string
+ destinationPath: string,
): Promise {
if (!existsSync(sourcePath)) {
throw new Error("File does not exist");
diff --git a/desktop/src/services/imageProcessor.ts b/desktop/src/services/imageProcessor.ts
index d9b3e45f03d6bc94a07eccaa6aa66208d36573ae..cb6c7416d66966430c1c6eacdd8ced9b12e707fe 100644
--- a/desktop/src/services/imageProcessor.ts
+++ b/desktop/src/services/imageProcessor.ts
@@ -80,7 +80,7 @@ function getImageMagickStaticPath() {
export async function convertToJPEG(
fileData: Uint8Array,
- filename: string
+ filename: string,
): Promise {
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 {
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) {
diff --git a/desktop/src/services/logging.ts b/desktop/src/services/logging.ts
index fa82492bcd6153d05497ffe32f457661b70d967d..bcbacd9f569f52cb32a3072111a889e796470d33 100644
--- a/desktop/src/services/logging.ts
+++ b/desktop/src/services/logging.ts
@@ -12,11 +12,3 @@ export function openLogDirectory() {
export function logError(error: Error, message: string, info?: string): void {
ipcRenderer.invoke("log-error", error, message, info);
}
-
-export function getSentryUserID(): Promise {
- return ipcRenderer.invoke("get-sentry-id");
-}
-
-export function updateOptOutOfCrashReports(optOut: boolean) {
- return ipcRenderer.invoke("update-opt-out-crash-reports", optOut);
-}
diff --git a/desktop/src/services/sentry.ts b/desktop/src/services/sentry.ts
index 7adfcc255e38dda5094b1cc160bedf406c8bad65..4c5573152f7a22e1cc3d8233f447822a46b2c86d 100644
--- a/desktop/src/services/sentry.ts
+++ b/desktop/src/services/sentry.ts
@@ -1,68 +1,18 @@
-import * as Sentry from "@sentry/electron/dist/main";
-import { RELEASE_VERSION, SENTRY_DSN } from "../config";
-import { hasOptedOutOfCrashReports } from "../main";
-import { keysStore } from "../stores/keys.store";
import { isDev } from "../utils/common";
-import { makeID } from "../utils/logging";
import { logToDisk } from "./logging";
-const ENV_DEVELOPMENT = "development";
-
-const isDEVSentryENV = () =>
- process.env.NEXT_PUBLIC_SENTRY_ENV === ENV_DEVELOPMENT;
-
-export function initSentry(): void {
- Sentry.init({
- dsn: SENTRY_DSN,
- release: RELEASE_VERSION,
- environment: isDev ? "development" : "production",
- });
- Sentry.setUser({ id: getSentryUserID() });
-}
-
+/** Deprecated, but no alternative yet */
export function logErrorSentry(
error: any,
msg: string,
- info?: Record
+ info?: Record,
) {
- const err = errorWithContext(error, msg);
logToDisk(
`error: ${error?.name} ${error?.message} ${
error?.stack
- } msg: ${msg} info: ${JSON.stringify(info)}`
+ } msg: ${msg} info: ${JSON.stringify(info)}`,
);
- if (isDEVSentryENV()) {
+ if (isDev) {
console.log(error, { msg, info });
}
- if (hasOptedOutOfCrashReports()) {
- return;
- }
- Sentry.captureException(err, {
- level: Sentry.Severity.Info,
- user: { id: getSentryUserID() },
- contexts: {
- ...(info && {
- info: info,
- }),
- rootCause: { message: error?.message },
- },
- });
-}
-
-function errorWithContext(originalError: Error, context: string) {
- const errorWithContext = new Error(context);
- errorWithContext.stack =
- errorWithContext.stack.split("\n").slice(2, 4).join("\n") +
- "\n" +
- originalError.stack;
- return errorWithContext;
-}
-
-export function getSentryUserID() {
- let anonymizeUserID = keysStore.get("AnonymizeUserID")?.id;
- if (!anonymizeUserID) {
- anonymizeUserID = makeID(6);
- keysStore.set("AnonymizeUserID", { id: anonymizeUserID });
- }
- return anonymizeUserID;
}
diff --git a/desktop/src/services/upload.ts b/desktop/src/services/upload.ts
index e9db576a18103e64c423559173903f1643276a92..38a628c2552efc4c4d7bf94e7132d62a7a42e6ea 100644
--- a/desktop/src/services/upload.ts
+++ b/desktop/src/services/upload.ts
@@ -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 {
return {
path: path
diff --git a/desktop/src/services/userPreference.ts b/desktop/src/services/userPreference.ts
index 1c20e64da7a42a2b63c3f3821620a1a3d2b6dec1..e3c6db29047165106a5a911ff7c95346a5380224 100644
--- a/desktop/src/services/userPreference.ts
+++ b/desktop/src/services/userPreference.ts
@@ -24,14 +24,6 @@ export function setMuteUpdateNotificationVersion(version: string) {
userPreferencesStore.set("muteUpdateNotificationVersion", version);
}
-export function getOptOutOfCrashReports() {
- return userPreferencesStore.get("optOutOfCrashReports") ?? false;
-}
-
-export function setOptOutOfCrashReports(optOut: boolean) {
- userPreferencesStore.set("optOutOfCrashReports", optOut);
-}
-
export function clearSkipAppVersion() {
userPreferencesStore.delete("skipAppVersion");
}
diff --git a/desktop/src/stores/userPreferences.store.ts b/desktop/src/stores/userPreferences.store.ts
index 546ca743e120a76c7de3215d9260664651fb8e66..e6fec425ae5a9f0cecd5f8d79980d6b22e4877a2 100644
--- a/desktop/src/stores/userPreferences.store.ts
+++ b/desktop/src/stores/userPreferences.store.ts
@@ -11,9 +11,6 @@ const userPreferencesSchema: Schema = {
muteUpdateNotificationVersion: {
type: "string",
},
- optOutOfCrashReports: {
- type: "boolean",
- },
customCacheDirectory: {
type: "string",
},
diff --git a/desktop/src/types/cache.ts b/desktop/src/types/cache.ts
index fde7dc038435253ed5f82b8b017580723aeb2d55..112716eea001a2cce73fbd4f2afc682e4a410390 100644
--- a/desktop/src/types/cache.ts
+++ b/desktop/src/types/cache.ts
@@ -1,7 +1,7 @@
export interface LimitedCache {
match: (
key: string,
- options?: { sizeInBytes?: number }
+ options?: { sizeInBytes?: number },
) => Promise;
put: (key: string, data: Response) => Promise;
delete: (key: string) => Promise;
diff --git a/desktop/src/types/index.ts b/desktop/src/types/index.ts
index cd3ea8546b655152318a72cd96b0b646aacf1001..208983826b1705d5915728dbc498a08e979b5c65 100644
--- a/desktop/src/types/index.ts
+++ b/desktop/src/types/index.ts
@@ -58,7 +58,6 @@ export interface UserPreferencesType {
hideDockIcon: boolean;
skipAppVersion: string;
muteUpdateNotificationVersion: string;
- optOutOfCrashReports: boolean;
customCacheDirectory: string;
}
diff --git a/desktop/src/utils/clip-bpe-ts/README.md b/desktop/src/utils/clip-bpe-ts/README.md
index dd171eb42c53730eac3725651eb8b14eff260a0c..ee052eb41427880c2e6dd1173a5197e7818620c8 100644
--- a/desktop/src/utils/clip-bpe-ts/README.md
+++ b/desktop/src/utils/clip-bpe-ts/README.md
@@ -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.
diff --git a/desktop/src/utils/clip-bpe-ts/mod.ts b/desktop/src/utils/clip-bpe-ts/mod.ts
index 880d8b438fc18493f15819ce35154647673977bd..6cdf246f75c4bd7dc5bc176718f5956c575d4069 100644
--- a/desktop/src/utils/clip-bpe-ts/mod.ts
+++ b/desktop/src/utils/clip-bpe-ts/mod.ts
@@ -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;
diff --git a/desktop/src/utils/common/index.ts b/desktop/src/utils/common/index.ts
index 61880bcda2e7f179598b113857676eb211cbe8f7..e970dfec41146524cda2aa96fc680cc2167e43ec 100644
--- a/desktop/src/utils/common/index.ts
+++ b/desktop/src/utils/common/index.ts
@@ -4,7 +4,7 @@ export const isDev = !app.isPackaged;
export const promiseWithTimeout = async (
request: Promise,
- timeout: number
+ timeout: number,
): Promise => {
const timeoutRef: {
current: NodeJS.Timeout;
@@ -12,7 +12,7 @@ export const promiseWithTimeout = async (
const rejectOnTimeout = new Promise((_, reject) => {
timeoutRef.current = setTimeout(
() => reject(Error(CustomErrors.WAIT_TIME_EXCEEDED)),
- timeout
+ timeout,
);
});
const requestWithTimeOutCancellation = async () => {
diff --git a/desktop/src/utils/cors.ts b/desktop/src/utils/cors.ts
index bec656061247f407065a41388d8d2a2b400e2d2c..25f76211a30dfd9118be1807365d2e0b1c6a8b18 100644
--- a/desktop/src/utils/cors.ts
+++ b/desktop/src/utils/cors.ts
@@ -16,6 +16,6 @@ export function addAllowOriginHeader(mainWindow: BrowserWindow) {
callback({
responseHeaders: details.responseHeaders,
});
- }
+ },
);
}
diff --git a/desktop/src/utils/createWindow.ts b/desktop/src/utils/createWindow.ts
index b9e6a5b4715aa0157ee6d12986a941dd83990c45..c7d44e6c9a7df7540f0d6fa899f4c39003c056b4 100644
--- a/desktop/src/utils/createWindow.ts
+++ b/desktop/src/utils/createWindow.ts
@@ -1,8 +1,7 @@
import { app, BrowserWindow, nativeImage } from "electron";
import ElectronLog from "electron-log";
import * as path from "path";
-import { PROD_HOST_URL } from "../config";
-import { isAppQuitting } from "../main";
+import { isAppQuitting, rendererURL } from "../main";
import autoLauncher from "../services/autoLauncher";
import { logErrorSentry } from "../services/sentry";
import { getHideDockIconPreference } from "../services/userPreference";
@@ -41,21 +40,21 @@ export async function createWindow(): Promise {
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 {
logErrorSentry(
Error("render-process-gone"),
"webContents event render-process-gone",
- { details }
+ { details },
);
ElectronLog.log("webContents event render-process-gone", details);
});
diff --git a/desktop/src/utils/error.ts b/desktop/src/utils/error.ts
index aff17edb63ae27ba2a8caa130679c811317434c8..1922045a2c5e7dfa264c928053fcfc6284058eb3 100644
--- a/desktop/src/utils/error.ts
+++ b/desktop/src/utils/error.ts
@@ -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;
diff --git a/desktop/src/utils/ipcComms.ts b/desktop/src/utils/ipcComms.ts
index 84b80fae1aacfbf53e6f7831718d22ade8a3ec8b..eec644aebbf4e26dc17d012ad3cdc80b53a72d4e 100644
--- a/desktop/src/utils/ipcComms.ts
+++ b/desktop/src/utils/ipcComms.ts
@@ -10,7 +10,6 @@ import {
Tray,
} from "electron";
import path from "path";
-import { updateOptOutOfCrashReports } from "../main";
import {
getAppVersion,
muteUpdateNotification,
@@ -27,11 +26,10 @@ import {
convertToJPEG,
generateImageThumbnail,
} from "../services/imageProcessor";
-import { getSentryUserID, logErrorSentry } from "../services/sentry";
+import { logErrorSentry } from "../services/sentry";
import {
getCustomCacheDirectory,
setCustomCacheDirectory,
- setOptOutOfCrashReports,
} from "../services/userPreference";
import { getPlatform } from "./common/platform";
import { createWindow } from "./createWindow";
@@ -40,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({
@@ -146,9 +144,6 @@ export default function setupIpcComs(
ipcMain.on("mute-update-notification", (_, version) => {
muteUpdateNotification(version);
});
- ipcMain.handle("get-sentry-id", () => {
- return getSentryUserID();
- });
ipcMain.handle("get-app-version", () => {
return getAppVersion();
@@ -161,9 +156,9 @@ export default function setupIpcComs(
cmd,
inputFilePath,
outputFileName,
- dontTimeout
+ dontTimeout,
);
- }
+ },
);
ipcMain.handle("get-temp-file-path", (_, formatSuffix) => {
return generateTempFilePath(formatSuffix);
@@ -176,13 +171,9 @@ export default function setupIpcComs(
"generate-image-thumbnail",
(_, fileData, maxDimension, maxSize) => {
return generateImageThumbnail(fileData, maxDimension, maxSize);
- }
+ },
);
- ipcMain.handle("update-opt-out-crash-reports", (_, optOut) => {
- setOptOutOfCrashReports(optOut);
- updateOptOutOfCrashReports(optOut);
- });
ipcMain.handle("compute-image-embedding", (_, model, inputFilePath) => {
return computeImageEmbedding(model, inputFilePath);
});
diff --git a/desktop/src/utils/logging.ts b/desktop/src/utils/logging.ts
index c843642f81e115977e8e15927c8e4bb9e9aae801..351a1aef860cea34775eeb2ddeee6a9d6020a421 100644
--- a/desktop/src/utils/logging.ts
+++ b/desktop/src/utils/logging.ts
@@ -1,9 +1,8 @@
import log from "electron-log";
-import { LOG_FILENAME, MAX_LOG_SIZE } from "../config";
export function setupLogging(isDev?: boolean) {
- log.transports.file.fileName = LOG_FILENAME;
- log.transports.file.maxSize = MAX_LOG_SIZE;
+ log.transports.file.fileName = "ente.log";
+ log.transports.file.maxSize = 50 * 1024 * 1024; // 50MB;
if (!isDev) {
log.transports.console.level = false;
}
@@ -11,22 +10,9 @@ export function setupLogging(isDev?: boolean) {
"[{y}-{m}-{d}T{h}:{i}:{s}{z}] [{level}]{scope} {text}";
}
-export function makeID(length: number) {
- let result = "";
- const characters =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- const charactersLength = characters.length;
- for (let i = 0; i < length; i++) {
- result += characters.charAt(
- Math.floor(Math.random() * charactersLength)
- );
- }
- return result;
-}
-
export function convertBytesToHumanReadable(
bytes: number,
- precision = 2
+ precision = 2,
): string {
if (bytes === 0 || isNaN(bytes)) {
return "0 MB";
diff --git a/desktop/src/utils/main.ts b/desktop/src/utils/main.ts
index ebf466c038c8b43a6535f8318b1c96577c11fa04..569752326e6ec6ef5fd15590d9b5ab0e33966bf9 100644
--- a/desktop/src/utils/main.ts
+++ b/desktop/src/utils/main.ts
@@ -1,12 +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 { PROD_HOST_URL, RENDERER_OUTPUT_DIR } from "../config";
+import { rendererURL } from "../main";
import { setupAutoUpdater } from "../services/appUpdater";
import autoLauncher from "../services/autoLauncher";
import { getHideDockIconPreference } from "../services/userPreference";
@@ -27,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);
@@ -39,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 } =
@@ -76,18 +85,6 @@ export async function setupMainMenu(mainWindow: BrowserWindow) {
Menu.setApplicationMenu(await buildMenuBar(mainWindow));
}
-export function setupMainHotReload() {
- 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();
@@ -110,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;
diff --git a/desktop/src/utils/menu.ts b/desktop/src/utils/menu.ts
index 3c6a1b4e4fb6dc9d6cd26190b516b14b68279ad1..c86786ff6fee9a7b2f79085341fc57871c1d3e67 100644
--- a/desktop/src/utils/menu.ts
+++ b/desktop/src/utils/menu.ts
@@ -64,7 +64,7 @@ export async function buildMenuBar(mainWindow: BrowserWindow): Promise