[desktop] Various improvements (#1407)

- Restore macOS icon
- Remove unnecessary require
- Spruce dependencies docs
- Use standard import for jpeg-js
- Rearrange files
- Import onnxruntime with TypeScript types
This commit is contained in:
Manav Rathi 2024-04-10 21:13:01 +05:30 committed by GitHub
commit 549ad77ac6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 107 additions and 92 deletions

BIN
desktop/build/icon.icns Normal file

Binary file not shown.

View file

@ -1,5 +1,9 @@
# Dependencies
* [Electron](#electron)
* [Dev dependencies](#dev)
* [Functionality](#functionality)
## Electron
[Electron](https://www.electronjs.org) is a cross-platform (Linux, Windows,
@ -73,7 +77,7 @@ Electron process. This allows us to directly use the output produced by
## Dev
See [web/docs/dependencies#DX](../../web/docs/dependencies.md#dev) for the
See [web/docs/dependencies#dev](../../web/docs/dependencies.md#dev) for the
general development experience related dependencies like TypeScript etc, which
are similar to that in the web code.
@ -88,7 +92,7 @@ Some extra ones specific to the code here are:
## Functionality
### Conversion
### Format conversion
The main tool we use is for arbitrary conversions is FFMPEG. To bundle a
(platform specific) static binary of ffmpeg with our app, we use
@ -104,20 +108,23 @@ resources (`build`) folder. This is used for thumbnail generation on Linux.
On macOS, we use the `sips` CLI tool for conversion, but that is already
available on the host machine, and is not bundled with our app.
### AI/ML
[onnxruntime-node](https://github.com/Microsoft/onnxruntime) is used as the
AI/ML runtime. It powers both natural language searches (using CLIP) and face
detection (using YOLO).
[jpeg-js](https://github.com/jpeg-js/jpeg-js#readme) is used for decoding
JPEG data into raw RGB bytes before passing it to ONNX.
html-entities is used by the bundled clip-bpe-ts tokenizer for CLIP.
### Watch Folders
[chokidar](https://github.com/paulmillr/chokidar) is used as a file system
watcher for the watch folders functionality.
### AI/ML
- [onnxruntime-node](https://github.com/Microsoft/onnxruntime) is used for
natural language searches based on CLIP.
- html-entities is used by the bundled clip-bpe-ts tokenizer.
- [jpeg-js](https://github.com/jpeg-js/jpeg-js#readme) is used for decoding
JPEG data into raw RGB bytes before passing it to ONNX.
## ZIP
### ZIP
[node-stream-zip](https://github.com/antelle/node-stream-zip) is used for
reading of large ZIP files (e.g. during imports of Google Takeout ZIPs).

View file

@ -26,9 +26,9 @@ import {
import { attachFSWatchIPCHandlers, attachIPCHandlers } from "./main/ipc";
import log, { initLogging } from "./main/log";
import { createApplicationMenu } from "./main/menu";
import { setupAutoUpdater } from "./main/services/app-update";
import { initWatcher } from "./main/services/chokidar";
import { isDev } from "./main/util";
import { setupAutoUpdater } from "./services/app-update";
import { initWatcher } from "./services/chokidar";
let appIsQuitting = false;

View file

@ -1,8 +1,8 @@
import { dialog } from "electron/main";
import path from "node:path";
import { getDirFilePaths, getElectronFile } from "../services/fs";
import { getElectronFilesFromGoogleZip } from "../services/upload";
import type { ElectronFile } from "../types/ipc";
import { getDirFilePaths, getElectronFile } from "./services/fs";
import { getElectronFilesFromGoogleZip } from "./services/upload";
export const selectDirectory = async () => {
const result = await dialog.showOpenDialog({

View file

@ -1,12 +1,12 @@
import { app, BrowserWindow, nativeImage, Tray } from "electron";
import { BrowserWindow, Tray, app, nativeImage, shell } from "electron";
import { existsSync } from "node:fs";
import path from "node:path";
import { isAppQuitting, rendererURL } from "../main";
import autoLauncher from "../services/autoLauncher";
import { getHideDockIconPreference } from "../services/userPreference";
import { isPlatform } from "../utils/common/platform";
import log from "./log";
import { createTrayContextMenu } from "./menu";
import { isPlatform } from "./platform";
import autoLauncher from "./services/autoLauncher";
import { getHideDockIconPreference } from "./services/userPreference";
import { isDev } from "./util";
/**
@ -109,7 +109,7 @@ export function handleDownloads(mainWindow: BrowserWindow) {
export function handleExternalLinks(mainWindow: BrowserWindow) {
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (!url.startsWith(rendererURL)) {
require("electron").shell.openExternal(url);
shell.openExternal(url);
return { action: "deny" };
} else {
return { action: "allow" };

View file

@ -10,37 +10,6 @@
import type { FSWatcher } from "chokidar";
import { ipcMain } from "electron/main";
import {
appVersion,
skipAppUpdate,
updateAndRestart,
updateOnNextRestart,
} from "../services/app-update";
import { clipImageEmbedding, clipTextEmbedding } from "../services/clip";
import { runFFmpegCmd } from "../services/ffmpeg";
import { getDirFiles } from "../services/fs";
import {
convertToJPEG,
generateImageThumbnail,
} from "../services/imageProcessor";
import {
clearStores,
encryptionKey,
saveEncryptionKey,
} from "../services/store";
import {
getElectronFilesFromGoogleZip,
getPendingUploads,
setToUploadCollection,
setToUploadFiles,
} from "../services/upload";
import {
addWatchMapping,
getWatchMappings,
removeWatchMapping,
updateWatchMappingIgnoredFiles,
updateWatchMappingSyncedFiles,
} from "../services/watch";
import type { ElectronFile, FILE_PATH_TYPE, WatchMapping } from "../types/ipc";
import {
selectDirectory,
@ -61,6 +30,37 @@ import {
saveStreamToDisk,
} from "./fs";
import { logToDisk } from "./log";
import {
appVersion,
skipAppUpdate,
updateAndRestart,
updateOnNextRestart,
} from "./services/app-update";
import { clipImageEmbedding, clipTextEmbedding } from "./services/clip";
import { runFFmpegCmd } from "./services/ffmpeg";
import { getDirFiles } from "./services/fs";
import {
convertToJPEG,
generateImageThumbnail,
} from "./services/imageProcessor";
import {
clearStores,
encryptionKey,
saveEncryptionKey,
} from "./services/store";
import {
getElectronFilesFromGoogleZip,
getPendingUploads,
setToUploadCollection,
setToUploadFiles,
} from "./services/upload";
import {
addWatchMapping,
getWatchMappings,
removeWatchMapping,
updateWatchMappingIgnoredFiles,
updateWatchMappingSyncedFiles,
} from "./services/watch";
import { openDirectory, openLogDirectory } from "./util";
/**

View file

@ -6,12 +6,12 @@ import {
shell,
} from "electron";
import { setIsAppQuitting } from "../main";
import { forceCheckForAppUpdates } from "../services/app-update";
import autoLauncher from "../services/autoLauncher";
import { forceCheckForAppUpdates } from "./services/app-update";
import autoLauncher from "./services/autoLauncher";
import {
getHideDockIconPreference,
setHideDockIconPreference,
} from "../services/userPreference";
} from "./services/userPreference";
import { openLogDirectory } from "./util";
/** Create and return the entries in the app's main menu bar */

View file

@ -2,10 +2,10 @@ import { compareVersions } from "compare-versions";
import { app, BrowserWindow } from "electron";
import { default as electronLog } from "electron-log";
import { autoUpdater } from "electron-updater";
import { setIsAppQuitting, setIsUpdateAvailable } from "../main";
import log from "../main/log";
import { setIsAppQuitting, setIsUpdateAvailable } from "../../main";
import { AppUpdateInfo } from "../../types/ipc";
import log from "../log";
import { userPreferencesStore } from "../stores/user-preferences";
import { AppUpdateInfo } from "../types/ipc";
export const setupAutoUpdater = (mainWindow: BrowserWindow) => {
autoUpdater.logger = electronLog;

View file

@ -1,5 +1,5 @@
import { AutoLauncherClient } from "../types/main";
import { isPlatform } from "../utils/common/platform";
import { AutoLauncherClient } from "../../types/main";
import { isPlatform } from "../platform";
import linuxAndWinAutoLauncher from "./autoLauncherClients/linuxAndWinAutoLauncher";
import macAutoLauncher from "./autoLauncherClients/macAutoLauncher";

View file

@ -1,6 +1,6 @@
import AutoLaunch from "auto-launch";
import { app } from "electron";
import { AutoLauncherClient } from "../../types/main";
import { AutoLauncherClient } from "../../../types/main";
const LAUNCHED_AS_HIDDEN_FLAG = "hidden";

View file

@ -1,5 +1,5 @@
import { app } from "electron";
import { AutoLauncherClient } from "../../types/main";
import { AutoLauncherClient } from "../../../types/main";
class MacAutoLauncher implements AutoLauncherClient {
async isEnabled() {

View file

@ -1,9 +1,9 @@
import chokidar from "chokidar";
import { BrowserWindow } from "electron";
import path from "path";
import log from "../main/log";
import { getWatchMappings } from "../services/watch";
import log from "../log";
import { getElectronFile } from "./fs";
import { getWatchMappings } from "./watch";
/**
* Convert a file system {@link filePath} that uses the local system specific

View file

@ -11,16 +11,16 @@
*/
import { app, net } from "electron/main";
import { existsSync } from "fs";
import jpeg from "jpeg-js";
import fs from "node:fs/promises";
import path from "node:path";
import { writeStream } from "../main/fs";
import log from "../main/log";
import { CustomErrors } from "../types/ipc";
import Tokenizer from "../utils/clip-bpe-ts/mod";
import { generateTempFilePath } from "../utils/temp";
import * as ort from "onnxruntime-node";
import Tokenizer from "../../thirdparty/clip-bpe-ts/mod";
import { CustomErrors } from "../../types/ipc";
import { writeStream } from "../fs";
import log from "../log";
import { generateTempFilePath } from "../temp";
import { deleteTempFile } from "./ffmpeg";
const jpeg = require("jpeg-js");
const ort = require("onnxruntime-node");
const textModelName = "clip-text-vit-32-uint8.onnx";
const textModelByteSize = 64173509; // 61.2 MB

View file

@ -1,11 +1,11 @@
import pathToFfmpeg from "ffmpeg-static";
import { existsSync } from "node:fs";
import fs from "node:fs/promises";
import { writeStream } from "../main/fs";
import log from "../main/log";
import { execAsync } from "../main/util";
import { ElectronFile } from "../types/ipc";
import { generateTempFilePath, getTempDirPath } from "../utils/temp";
import { ElectronFile } from "../../types/ipc";
import { writeStream } from "../fs";
import log from "../log";
import { generateTempFilePath, getTempDirPath } from "../temp";
import { execAsync } from "../util";
const INPUT_PATH_PLACEHOLDER = "INPUT";
const FFMPEG_PLACEHOLDER = "FFMPEG";

View file

@ -2,8 +2,8 @@ import StreamZip from "node-stream-zip";
import { existsSync } from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";
import log from "../main/log";
import { ElectronFile } from "../types/ipc";
import { ElectronFile } from "../../types/ipc";
import log from "../log";
const FILE_STREAM_CHUNK_SIZE: number = 4 * 1024 * 1024;

View file

@ -1,12 +1,12 @@
import { existsSync } from "fs";
import fs from "node:fs/promises";
import path from "path";
import { writeStream } from "../main/fs";
import log from "../main/log";
import { execAsync, isDev } from "../main/util";
import { CustomErrors, ElectronFile } from "../types/ipc";
import { isPlatform } from "../utils/common/platform";
import { generateTempFilePath } from "../utils/temp";
import { CustomErrors, ElectronFile } from "../../types/ipc";
import { writeStream } from "../fs";
import log from "../log";
import { isPlatform } from "../platform";
import { generateTempFilePath } from "../temp";
import { execAsync, isDev } from "../util";
import { deleteTempFile } from "./ffmpeg";
const IMAGE_MAGICK_PLACEHOLDER = "IMAGE_MAGICK";

View file

@ -1,10 +1,9 @@
import StreamZip from "node-stream-zip";
import path from "path";
import { getElectronFile } from "../services/fs";
import { ElectronFile, FILE_PATH_TYPE } from "../../types/ipc";
import { FILE_PATH_KEYS } from "../../types/main";
import { uploadStatusStore } from "../stores/upload.store";
import { ElectronFile, FILE_PATH_TYPE } from "../types/ipc";
import { FILE_PATH_KEYS } from "../types/main";
import { getValidPaths, getZipFileStream } from "./fs";
import { getElectronFile, getValidPaths, getZipFileStream } from "./fs";
export const getPendingUploads = async () => {
const filePaths = getSavedFilePaths(FILE_PATH_TYPE.FILES);

View file

@ -1,7 +1,7 @@
import type { FSWatcher } from "chokidar";
import ElectronLog from "electron-log";
import { WatchMapping, WatchStoreType } from "../../types/ipc";
import { watchStore } from "../stores/watch.store";
import { WatchMapping, WatchStoreType } from "../types/ipc";
export const addWatchMapping = async (
watcher: FSWatcher,

View file

@ -1,5 +1,5 @@
import Store, { Schema } from "electron-store";
import type { KeysStoreType } from "../types/main";
import type { KeysStoreType } from "../../types/main";
const keysStoreSchema: Schema<KeysStoreType> = {
AnonymizeUserID: {

View file

@ -1,5 +1,5 @@
import Store, { Schema } from "electron-store";
import type { SafeStorageStoreType } from "../types/main";
import type { SafeStorageStoreType } from "../../types/main";
const safeStorageSchema: Schema<SafeStorageStoreType> = {
encryptionKey: {

View file

@ -1,5 +1,5 @@
import Store, { Schema } from "electron-store";
import type { UploadStoreType } from "../types/main";
import type { UploadStoreType } from "../../types/main";
const uploadStoreSchema: Schema<UploadStoreType> = {
filePaths: {

View file

@ -1,5 +1,5 @@
import Store, { Schema } from "electron-store";
import { WatchStoreType } from "../types/ipc";
import { WatchStoreType } from "../../types/ipc";
const watchStoreSchema: Schema<WatchStoreType> = {
mappings: {

View file

@ -0,0 +1,9 @@
/**
* Types for [onnxruntime-node](https://onnxruntime.ai/docs/api/js/index.html).
*
* Note: these are not the official types but are based on a temporary
* [workaround](https://github.com/microsoft/onnxruntime/issues/17979).
*/
declare module "onnxruntime-node" {
export * from "onnxruntime-common";
}