Prettier 3 + reformat (same as web)

This commit is contained in:
Manav Rathi 2024-03-11 16:42:02 +05:30
parent 829406fa62
commit 88741083fe
60 changed files with 1353 additions and 1215 deletions

3
desktop/.prettierignore Normal file
View file

@ -0,0 +1,3 @@
thirdparty/
public/
*.md

View file

@ -1,6 +1,7 @@
{
"tabWidth": 4,
"trailingComma": "es5",
"singleQuote": true,
"bracketSameLine": true
}
"plugins": [
"prettier-plugin-organize-imports",
"prettier-plugin-packagejson"
]
}

View file

@ -1,20 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ente Photos</title>
</head>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ente Photos</title>
</head>
<body style="background-color: black;">
<div style=" height: 95vh;width: 96vw; display: grid; place-items: center; color: white;">
<div>
<div style="margin-bottom: 10px;">Site unreachable, please try again later</div>
<button onClick="window[`ElectronAPIs`].reloadWindow()">Reload</button>
<body style="background-color: black">
<div
style="
height: 95vh;
width: 96vw;
display: grid;
place-items: center;
color: white;
"
>
<div>
<div style="margin-bottom: 10px">
Site unreachable, please try again later
</div>
<button onClick="window[`ElectronAPIs`].reloadWindow()">
Reload
</button>
</div>
</div>
</div>
</body>
</body>
</html>

View file

@ -1,30 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ente Photos</title>
</head>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ente Photos</title>
</head>
<body style="background-color: black;">
<div style="display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 90vh;">
<div style="width:64px;"><svg version="1.1" id="L9" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 100 100"
enable-background="new 0 0 0 0" xml:space="preserve">
<path fill="#2dc262"
d="M73,50c0-12.7-10.3-23-23-23S27,37.3,27,50 M30.9,50c0-10.5,8.5-19.1,19.1-19.1S69.1,39.5,69.1,50">
<animateTransform attributeName="transform" attributeType="XML" type="rotate" dur="1s"
from="0 50 50" to="360 50 50" repeatCount="indefinite" />
</path>
</svg>
<body style="background-color: black">
<div
style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 90vh;
"
>
<div style="width: 64px">
<svg
version="1.1"
id="L9"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 100 100"
enable-background="new 0 0 0 0"
xml:space="preserve"
>
<path
fill="#2dc262"
d="M73,50c0-12.7-10.3-23-23-23S27,37.3,27,50 M30.9,50c0-10.5,8.5-19.1,19.1-19.1S69.1,39.5,69.1,50"
>
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
dur="1s"
from="0 50 50"
to="360 50 50"
repeatCount="indefinite"
/>
</path>
</svg>
</div>
</div>
</div>
</body>
</body>
</html>

View file

@ -1,24 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>Electron Updater Example</title>
</head>
<body>
Current version: <span id="version">vX.Y.Z</span>
<div id="messages"></div>
<script>
// Display the current version
let version = window.location.hash.substring(1);
document.getElementById('version').innerText = version;
<head>
<title>Electron Updater Example</title>
</head>
<body>
Current version: <span id="version">vX.Y.Z</span>
<div id="messages"></div>
<script>
// Display the current version
let version = window.location.hash.substring(1);
document.getElementById("version").innerText = version;
// Listen for messages
const {ipcRenderer} = require('electron');
ipcRenderer.on('message', function(event, text) {
var container = document.getElementById('messages');
var message = document.createElement('div');
message.innerHTML = text;
container.appendChild(message);
})
</script>
</body>
</html>
// Listen for messages
const { ipcRenderer } = require("electron");
ipcRenderer.on("message", function (event, text) {
var container = document.getElementById("messages");
var message = document.createElement("div");
message.innerHTML = text;
container.appendChild(message);
});
</script>
</body>
</html>

View file

@ -1,10 +1,67 @@
{
"name": "ente",
"productName": "ente",
"version": "1.6.63",
"private": true,
"description": "Desktop client for ente.io",
"author": "ente <code@ente.io>",
"main": "app/main.js",
"scripts": {
"build": "yarn build-renderer && yarn build-main",
"build-main": "yarn install && tsc",
"build-renderer": "cd ui && yarn install && yarn export:photos",
"postinstall": "electron-builder install-app-deps",
"lint": "yarn prettier --check . && eslint \"src/**/*.{js,jsx,ts,tsx}\"",
"lint-fix": "yarn prettier --write . && eslint --fix .",
"start": "concurrently \"yarn start-main\" \"yarn start-renderer\"",
"start-main": "yarn build-main && electron app/main.js",
"start-renderer": "cd ui && yarn install && yarn dev:photos",
"test-release": "cross-env IS_TEST_RELEASE=true yarn build && electron-builder --config.compression=store",
"watch": "tsc -w"
},
"dependencies": {
"@sentry/electron": "^2.5.1",
"any-shell-escape": "^0.1.1",
"auto-launch": "^5.0.5",
"chokidar": "^3.5.3",
"compare-versions": "^6.1.0",
"electron-log": "^4.3.5",
"electron-reload": "^2.0.0-alpha.1",
"electron-store": "^8.0.1",
"electron-updater": "^4.3.8",
"ffmpeg-static": "^5.1.0",
"get-folder-size": "^2.0.1",
"html-entities": "^2.4.0",
"jpeg-js": "^0.4.4",
"next-electron-server": "file:./thirdparty/next-electron-server",
"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",
"@types/node": "18.15.0",
"@types/node-fetch": "^2.6.2",
"@types/promise-fs": "^2.1.1",
"@typescript-eslint/eslint-plugin": "^5.28.0",
"@typescript-eslint/parser": "^5.28.0",
"concurrently": "^7.0.0",
"cross-env": "^7.0.3",
"electron": "^25.8.4",
"electron-builder": "^24.6.4",
"electron-builder-notarize": "^1.2.0",
"electron-download": "^4.1.1",
"eslint": "^7.23.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^8.5.0",
"prettier": "2.5.1",
"prettier-plugin-organize-imports": "^3.2",
"prettier-plugin-packagejson": "^2.4",
"typescript": "^4.2.3"
},
"build": {
"appId": "io.ente.bhari-frame",
"artifactName": "${productName}-${version}-${arch}.${ext}",
@ -83,63 +140,7 @@
}
]
},
"scripts": {
"postinstall": "electron-builder install-app-deps",
"prebuild": "eslint \"src/**/*.{js,jsx,ts,tsx}\"",
"prepare": "husky install",
"lint": "eslint -c .eslintrc --ext .ts src",
"watch": "tsc -w",
"build-main": "yarn install && tsc",
"start-main": "yarn build-main && electron app/main.js",
"start-renderer": "cd ui && yarn install && yarn dev:photos",
"start": "concurrently \"yarn start-main\" \"yarn start-renderer\"",
"build-renderer": "cd ui && yarn install && yarn export:photos",
"build": "yarn build-renderer && yarn build-main",
"test-release": "cross-env IS_TEST_RELEASE=true yarn build && electron-builder --config.compression=store"
},
"author": "ente <code@ente.io>",
"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",
"@types/node": "18.15.0",
"@types/node-fetch": "^2.6.2",
"@types/promise-fs": "^2.1.1",
"@typescript-eslint/eslint-plugin": "^5.28.0",
"@typescript-eslint/parser": "^5.28.0",
"concurrently": "^7.0.0",
"cross-env": "^7.0.3",
"electron": "^25.8.4",
"electron-builder": "^24.6.4",
"electron-builder-notarize": "^1.2.0",
"electron-download": "^4.1.1",
"eslint": "^7.23.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^8.5.0",
"prettier": "2.5.1",
"typescript": "^4.2.3"
},
"dependencies": {
"@sentry/electron": "^2.5.1",
"any-shell-escape": "^0.1.1",
"auto-launch": "^5.0.5",
"chokidar": "^3.5.3",
"compare-versions": "^6.1.0",
"electron-log": "^4.3.5",
"electron-reload": "^2.0.0-alpha.1",
"electron-store": "^8.0.1",
"electron-updater": "^4.3.8",
"ffmpeg-static": "^5.1.0",
"get-folder-size": "^2.0.1",
"html-entities": "^2.4.0",
"jpeg-js": "^0.4.4",
"next-electron-server": "file:./thirdparty/next-electron-server",
"node-fetch": "^2.6.7",
"node-stream-zip": "^1.15.0",
"onnxruntime-node": "^1.16.3",
"promise-fs": "^2.1.1"
},
"productName": "ente",
"standard": {
"parser": "babel-eslint"
}

View file

@ -1,16 +1,16 @@
import { ipcRenderer } from 'electron/renderer';
import path from 'path';
import { existsSync, mkdir, rmSync } from 'promise-fs';
import { DiskCache } from '../services/diskCache';
import { ipcRenderer } from "electron/renderer";
import path from "path";
import { existsSync, mkdir, rmSync } from "promise-fs";
import { DiskCache } from "../services/diskCache";
const ENTE_CACHE_DIR_NAME = 'ente';
const ENTE_CACHE_DIR_NAME = "ente";
export const getCacheDirectory = async () => {
const customCacheDir = await getCustomCacheDirectory();
if (customCacheDir && existsSync(customCacheDir)) {
return customCacheDir;
}
const defaultSystemCacheDir = await ipcRenderer.invoke('get-path', 'cache');
const defaultSystemCacheDir = await ipcRenderer.invoke("get-path", "cache");
return path.join(defaultSystemCacheDir, ENTE_CACHE_DIR_NAME);
};
@ -44,9 +44,9 @@ export async function deleteDiskCache(cacheName: string) {
export async function setCustomCacheDirectory(
directory: string
): Promise<void> {
await ipcRenderer.invoke('set-custom-cache-directory', directory);
await ipcRenderer.invoke("set-custom-cache-directory", directory);
}
async function getCustomCacheDirectory(): Promise<string> {
return await ipcRenderer.invoke('get-custom-cache-directory');
return await ipcRenderer.invoke("get-custom-cache-directory");
}

View file

@ -1,8 +1,7 @@
import { ipcRenderer } from 'electron';
import { writeStream } from '../services/fs';
import { isExecError } from '../utils/error';
import { parseExecError } from '../utils/error';
import { Model } from '../types';
import { ipcRenderer } from "electron";
import { writeStream } from "../services/fs";
import { Model } from "../types";
import { isExecError, parseExecError } from "../utils/error";
export async function computeImageEmbedding(
model: Model,
@ -10,11 +9,11 @@ export async function computeImageEmbedding(
): Promise<Float32Array> {
let tempInputFilePath = null;
try {
tempInputFilePath = await ipcRenderer.invoke('get-temp-file-path', '');
tempInputFilePath = await ipcRenderer.invoke("get-temp-file-path", "");
const imageStream = new Response(imageData.buffer).body;
await writeStream(tempInputFilePath, imageStream);
const embedding = await ipcRenderer.invoke(
'compute-image-embedding',
"compute-image-embedding",
model,
tempInputFilePath
);
@ -28,7 +27,7 @@ export async function computeImageEmbedding(
}
} finally {
if (tempInputFilePath) {
await ipcRenderer.invoke('remove-temp-file', tempInputFilePath);
await ipcRenderer.invoke("remove-temp-file", tempInputFilePath);
}
}
}
@ -39,7 +38,7 @@ export async function computeTextEmbedding(
): Promise<Float32Array> {
try {
const embedding = await ipcRenderer.invoke(
'compute-text-embedding',
"compute-text-embedding",
model,
text
);

View file

@ -1,44 +1,44 @@
import { ipcRenderer } from 'electron/renderer';
import { logError } from '../services/logging';
import { ipcRenderer } from "electron/renderer";
import { logError } from "../services/logging";
export const selectDirectory = async (): Promise<string> => {
try {
return await ipcRenderer.invoke('select-dir');
return await ipcRenderer.invoke("select-dir");
} catch (e) {
logError(e, 'error while selecting root directory');
logError(e, "error while selecting root directory");
}
};
export const getAppVersion = async (): Promise<string> => {
try {
return await ipcRenderer.invoke('get-app-version');
return await ipcRenderer.invoke("get-app-version");
} catch (e) {
logError(e, 'failed to get release version');
logError(e, "failed to get release version");
throw e;
}
};
export const openDirectory = async (dirPath: string): Promise<void> => {
try {
await ipcRenderer.invoke('open-dir', dirPath);
await ipcRenderer.invoke("open-dir", dirPath);
} catch (e) {
logError(e, 'error while opening directory');
logError(e, "error while opening directory");
throw e;
}
};
export const getPlatform = async (): Promise<'mac' | 'windows' | 'linux'> => {
export const getPlatform = async (): Promise<"mac" | "windows" | "linux"> => {
try {
return await ipcRenderer.invoke('get-platform');
return await ipcRenderer.invoke("get-platform");
} catch (e) {
logError(e, 'failed to get platform');
logError(e, "failed to get platform");
throw e;
}
};
export {
getSentryUserID,
logToDisk,
openLogDirectory,
getSentryUserID,
updateOptOutOfCrashReports,
} from '../services/logging';
} from "../services/logging";

View file

@ -1,9 +1,9 @@
import { keysStore } from '../stores/keys.store';
import { safeStorageStore } from '../stores/safeStorage.store';
import { uploadStatusStore } from '../stores/upload.store';
import { logError } from '../services/logging';
import { userPreferencesStore } from '../stores/userPreferences.store';
import { watchStore } from '../stores/watch.store';
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 = () => {
try {
@ -11,9 +11,9 @@ export const clearElectronStore = () => {
keysStore.clear();
safeStorageStore.clear();
watchStore.clear();
userPreferencesStore.delete('optOutOfCrashReports');
userPreferencesStore.delete("optOutOfCrashReports");
} catch (e) {
logError(e, 'error while clearing electron store');
logError(e, "error while clearing electron store");
throw e;
}
};

View file

@ -1,5 +1,5 @@
import { writeStream } from './../services/fs';
import * as fs from 'promise-fs';
import * as fs from "promise-fs";
import { writeStream } from "./../services/fs";
export const exists = (path: string) => {
return fs.existsSync(path);

View file

@ -1,8 +1,8 @@
import { ipcRenderer } from 'electron';
import { existsSync } from 'fs';
import { writeStream } from '../services/fs';
import { logError } from '../services/logging';
import { ElectronFile } from '../types';
import { ipcRenderer } from "electron";
import { existsSync } from "fs";
import { writeStream } from "../services/fs";
import { logError } from "../services/logging";
import { ElectronFile } from "../types";
export async function runFFmpegCmd(
cmd: string[],
@ -15,7 +15,7 @@ export async function runFFmpegCmd(
try {
if (!existsSync(inputFile.path)) {
const tempFilePath = await ipcRenderer.invoke(
'get-temp-file-path',
"get-temp-file-path",
inputFile.name
);
await writeStream(tempFilePath, await inputFile.stream());
@ -25,7 +25,7 @@ export async function runFFmpegCmd(
inputFilePath = inputFile.path;
}
const outputFileData = await ipcRenderer.invoke(
'run-ffmpeg-cmd',
"run-ffmpeg-cmd",
cmd,
inputFilePath,
outputFileName,
@ -35,9 +35,9 @@ export async function runFFmpegCmd(
} finally {
if (createdTempInputFile) {
try {
await ipcRenderer.invoke('remove-temp-file', inputFilePath);
await ipcRenderer.invoke("remove-temp-file", inputFilePath);
} catch (e) {
logError(e, 'failed to deleteTempFile');
logError(e, "failed to deleteTempFile");
}
}
}

View file

@ -1,4 +1,4 @@
import { getElectronFile, getDirFilePaths } from '../services/fs';
import { getDirFilePaths, getElectronFile } from "../services/fs";
export async function getDirFiles(dirPath: string) {
const files = await getDirFilePaths(dirPath);
@ -6,10 +6,10 @@ export async function getDirFiles(dirPath: string) {
return electronFiles;
}
export {
deleteFile,
deleteFolder,
isFolder,
moveFile,
deleteFolder,
deleteFile,
rename,
readTextFile,
} from '../services/fs';
rename,
} from "../services/fs";

View file

@ -1,20 +1,20 @@
import { CustomErrors } from '../constants/errors';
import { ipcRenderer } from 'electron/renderer';
import { existsSync } from 'fs';
import { writeStream } from '../services/fs';
import { logError } from '../services/logging';
import { ElectronFile } from '../types';
import { isPlatform } from '../utils/common/platform';
import { ipcRenderer } from "electron/renderer";
import { existsSync } from "fs";
import { CustomErrors } from "../constants/errors";
import { writeStream } from "../services/fs";
import { logError } from "../services/logging";
import { ElectronFile } from "../types";
import { isPlatform } from "../utils/common/platform";
export async function convertToJPEG(
fileData: Uint8Array,
filename: string
): Promise<Uint8Array> {
if (isPlatform('windows')) {
if (isPlatform("windows")) {
throw Error(CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED);
}
const convertedFileData = await ipcRenderer.invoke(
'convert-to-jpeg',
"convert-to-jpeg",
fileData,
filename
);
@ -29,14 +29,14 @@ export async function generateImageThumbnail(
let inputFilePath = null;
let createdTempInputFile = null;
try {
if (isPlatform('windows')) {
if (isPlatform("windows")) {
throw Error(
CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED
);
}
if (!existsSync(inputFile.path)) {
const tempFilePath = await ipcRenderer.invoke(
'get-temp-file-path',
"get-temp-file-path",
inputFile.name
);
await writeStream(tempFilePath, await inputFile.stream());
@ -46,7 +46,7 @@ export async function generateImageThumbnail(
inputFilePath = inputFile.path;
}
const thumbnail = await ipcRenderer.invoke(
'generate-image-thumbnail',
"generate-image-thumbnail",
inputFilePath,
maxDimension,
maxSize
@ -55,9 +55,9 @@ export async function generateImageThumbnail(
} finally {
if (createdTempInputFile) {
try {
await ipcRenderer.invoke('remove-temp-file', inputFilePath);
await ipcRenderer.invoke("remove-temp-file", inputFilePath);
} catch (e) {
logError(e, 'failed to deleteTempFile');
logError(e, "failed to deleteTempFile");
}
}
}

View file

@ -1,32 +1,32 @@
import { ipcRenderer } from 'electron';
import { safeStorageStore } from '../stores/safeStorage.store';
import { logError } from '../services/logging';
import { ipcRenderer } from "electron";
import { logError } from "../services/logging";
import { safeStorageStore } from "../stores/safeStorage.store";
export async function setEncryptionKey(encryptionKey: string) {
try {
const encryptedKey: Buffer = await ipcRenderer.invoke(
'safeStorage-encrypt',
"safeStorage-encrypt",
encryptionKey
);
const b64EncryptedKey = Buffer.from(encryptedKey).toString('base64');
safeStorageStore.set('encryptionKey', b64EncryptedKey);
const b64EncryptedKey = Buffer.from(encryptedKey).toString("base64");
safeStorageStore.set("encryptionKey", b64EncryptedKey);
} catch (e) {
logError(e, 'setEncryptionKey failed');
logError(e, "setEncryptionKey failed");
throw e;
}
}
export async function getEncryptionKey(): Promise<string> {
try {
const b64EncryptedKey = safeStorageStore.get('encryptionKey');
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);
return await ipcRenderer.invoke("safeStorage-decrypt", keyBuffer);
}
} catch (e) {
logError(e, 'getEncryptionKey failed');
logError(e, "getEncryptionKey failed");
throw e;
}
}

View file

@ -1,37 +1,37 @@
import { ipcRenderer } from 'electron';
import { AppUpdateInfo } from '../types';
import { ipcRenderer } from "electron";
import { AppUpdateInfo } from "../types";
export const sendNotification = (content: string) => {
ipcRenderer.send('send-notification', content);
ipcRenderer.send("send-notification", content);
};
export const reloadWindow = () => {
ipcRenderer.send('reload-window');
ipcRenderer.send("reload-window");
};
export const registerUpdateEventListener = (
showUpdateDialog: (updateInfo: AppUpdateInfo) => void
) => {
ipcRenderer.removeAllListeners('show-update-dialog');
ipcRenderer.on('show-update-dialog', (_, updateInfo: AppUpdateInfo) => {
ipcRenderer.removeAllListeners("show-update-dialog");
ipcRenderer.on("show-update-dialog", (_, updateInfo: AppUpdateInfo) => {
showUpdateDialog(updateInfo);
});
};
export const registerForegroundEventListener = (onForeground: () => void) => {
ipcRenderer.removeAllListeners('app-in-foreground');
ipcRenderer.on('app-in-foreground', () => {
ipcRenderer.removeAllListeners("app-in-foreground");
ipcRenderer.on("app-in-foreground", () => {
onForeground();
});
};
export const updateAndRestart = () => {
ipcRenderer.send('update-and-restart');
ipcRenderer.send("update-and-restart");
};
export const skipAppUpdate = (version: string) => {
ipcRenderer.send('skip-app-update', version);
ipcRenderer.send("skip-app-update", version);
};
export const muteUpdateNotification = (version: string) => {
ipcRenderer.send('mute-update-notification', version);
ipcRenderer.send("mute-update-notification", version);
};

View file

@ -1,17 +1,17 @@
import { getElectronFile } from './../services/fs';
import { uploadStatusStore } from '../stores/upload.store';
import { ElectronFile, FILE_PATH_TYPE } from '../types';
import { logError } from '../services/logging';
import { ipcRenderer } from 'electron';
import { ipcRenderer } from "electron";
import { logError } from "../services/logging";
import {
getElectronFilesFromGoogleZip,
getSavedFilePaths,
} from '../services/upload';
} from "../services/upload";
import { uploadStatusStore } from "../stores/upload.store";
import { ElectronFile, FILE_PATH_TYPE } from "../types";
import { getElectronFile } from "./../services/fs";
export const getPendingUploads = async () => {
const filePaths = getSavedFilePaths(FILE_PATH_TYPE.FILES);
const zipPaths = getSavedFilePaths(FILE_PATH_TYPE.ZIPS);
const collectionName = uploadStatusStore.get('collectionName');
const collectionName = uploadStatusStore.get("collectionName");
let files: ElectronFile[] = [];
let type: FILE_PATH_TYPE;
@ -39,31 +39,31 @@ 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;
} catch (e) {
logError(e, 'error while selecting folders');
logError(e, "error while selecting folders");
}
};
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;
} catch (e) {
logError(e, 'error while selecting files');
logError(e, "error while selecting files");
}
};
export const showUploadZipDialog = async () => {
try {
const filePaths: string[] = await ipcRenderer.invoke(
'show-upload-zip-dialog'
"show-upload-zip-dialog"
);
let files: ElectronFile[] = [];
@ -79,12 +79,12 @@ export const showUploadZipDialog = async () => {
files,
};
} catch (e) {
logError(e, 'error while selecting zips');
logError(e, "error while selecting zips");
}
};
export {
setToUploadFiles,
getElectronFilesFromGoogleZip,
setToUploadCollection,
} from '../services/upload';
setToUploadFiles,
} from "../services/upload";

View file

@ -1,10 +1,10 @@
import { isMappingPresent } from '../utils/watch';
import path from 'path';
import { ipcRenderer } from 'electron';
import { ElectronFile, WatchMapping } from '../types';
import { getElectronFile } from '../services/fs';
import { getWatchMappings, setWatchMappings } from '../services/watch';
import ElectronLog from 'electron-log';
import { ipcRenderer } from "electron";
import ElectronLog from "electron-log";
import path from "path";
import { getElectronFile } from "../services/fs";
import { getWatchMappings, setWatchMappings } from "../services/watch";
import { ElectronFile, WatchMapping } from "../types";
import { isMappingPresent } from "../utils/watch";
export async function addWatchMapping(
rootFolderName: string,
@ -17,7 +17,7 @@ export async function addWatchMapping(
throw new Error(`Watch mapping already exists`);
}
await ipcRenderer.invoke('add-watcher', {
await ipcRenderer.invoke("add-watcher", {
dir: folderPath,
});
@ -42,7 +42,7 @@ export async function removeWatchMapping(folderPath: string) {
throw new Error(`Watch mapping does not exist`);
}
await ipcRenderer.invoke('remove-watcher', {
await ipcRenderer.invoke("remove-watcher", {
dir: watchMapping.folderPath,
});
@ -55,7 +55,7 @@ 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(
@ -72,7 +72,7 @@ export function updateWatchMappingSyncedFiles(
export function updateWatchMappingIgnoredFiles(
folderPath: string,
files: WatchMapping['ignoredFiles']
files: WatchMapping["ignoredFiles"]
): void {
const watchMappings = getWatchMappings();
const watchMapping = watchMappings.find(
@ -92,23 +92,23 @@ export function registerWatcherFunctions(
removeFile: (path: string) => Promise<void>,
removeFolder: (folderPath: string) => Promise<void>
) {
ipcRenderer.removeAllListeners('watch-add');
ipcRenderer.removeAllListeners('watch-change');
ipcRenderer.removeAllListeners('watch-unlink-dir');
ipcRenderer.on('watch-add', async (_, filePath: string) => {
ipcRenderer.removeAllListeners("watch-add");
ipcRenderer.removeAllListeners("watch-change");
ipcRenderer.removeAllListeners("watch-unlink-dir");
ipcRenderer.on("watch-add", async (_, filePath: string) => {
filePath = filePath.split(path.sep).join(path.posix.sep);
await addFile(await getElectronFile(filePath));
});
ipcRenderer.on('watch-unlink', async (_, filePath: string) => {
ipcRenderer.on("watch-unlink", async (_, filePath: string) => {
filePath = filePath.split(path.sep).join(path.posix.sep);
await removeFile(filePath);
});
ipcRenderer.on('watch-unlink-dir', async (_, folderPath: string) => {
ipcRenderer.on("watch-unlink-dir", async (_, folderPath: string) => {
folderPath = folderPath.split(path.sep).join(path.posix.sep);
await removeFolder(folderPath);
});
}
export { getWatchMappings } from '../services/watch';
export { getWatchMappings } from "../services/watch";

View file

@ -1,13 +1,13 @@
const PROD_HOST_URL: string = 'ente://app';
const RENDERER_OUTPUT_DIR: string = './ui/out';
const LOG_FILENAME = 'ente.log';
const PROD_HOST_URL: string = "ente://app";
const RENDERER_OUTPUT_DIR: string = "./ui/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 SENTRY_DSN = "https://759d8498487a81ac33a0c2efa2a42c4f@sentry.ente.io/9";
const RELEASE_VERSION = require('../../package.json').version;
const RELEASE_VERSION = require("../../package.json").version;
export {
PROD_HOST_URL,

View file

@ -1,12 +1,12 @@
export const CustomErrors = {
WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED:
'Windows native image processing is not supported',
"Windows native image processing is not supported",
INVALID_OS: (os: string) => `Invalid OS - ${os}`,
WAIT_TIME_EXCEEDED: 'Wait time exceeded',
WAIT_TIME_EXCEEDED: "Wait time exceeded",
UNSUPPORTED_PLATFORM: (platform: string, arch: string) =>
`Unsupported platform - ${platform} ${arch}`,
MODEL_DOWNLOAD_PENDING:
'Model download pending, skipping clip search request',
INVALID_FILE_PATH: 'Invalid file path',
"Model download pending, skipping clip search request",
INVALID_FILE_PATH: "Invalid file path",
INVALID_CLIP_MODEL: (model: string) => `Invalid Clip model - ${model}`,
};

View file

@ -1,27 +1,27 @@
import { app, BrowserWindow } from 'electron';
import { createWindow } from './utils/createWindow';
import setupIpcComs from './utils/ipcComms';
import { initWatcher } from './services/chokidar';
import { addAllowOriginHeader } from './utils/cors';
import { app, BrowserWindow } from "electron";
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";
import { setupAppEventEmitter } from "./utils/events";
import setupIpcComs from "./utils/ipcComms";
import { setupLogging } from "./utils/logging";
import {
setupTrayItem,
handleDownloads,
setupMacWindowOnDockIconClick,
setupMainMenu,
setupMainHotReload,
setupNextElectronServe,
enableSharedArrayBufferSupport,
handleDockIconHideOnAutoLaunch,
handleDownloads,
handleExternalLinks,
handleUpdates,
logSystemInfo,
handleExternalLinks,
} from './utils/main';
import { initSentry } from './services/sentry';
import { setupLogging } from './utils/logging';
import { isDev } from './utils/common';
import { setupMainProcessStatsLogger } from './utils/processStats';
import { setupAppEventEmitter } from './utils/events';
import { getOptOutOfCrashReports } from './services/userPreference';
setupMacWindowOnDockIconClick,
setupMainHotReload,
setupMainMenu,
setupNextElectronServe,
setupTrayItem,
} from "./utils/main";
import { setupMainProcessStatsLogger } from "./utils/processStats";
let mainWindow: BrowserWindow;
@ -66,7 +66,7 @@ if (!gotTheLock) {
} else {
handleDockIconHideOnAutoLaunch();
enableSharedArrayBufferSupport();
app.on('second-instance', () => {
app.on("second-instance", () => {
// Someone tried to run a second instance, we should focus our window.
if (mainWindow) {
mainWindow.show();
@ -80,7 +80,7 @@ if (!gotTheLock) {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
app.on("ready", async () => {
logSystemInfo();
setupMainProcessStatsLogger();
const hasOptedOutOfCrashReports = getOptOutOfCrashReports();
@ -101,5 +101,5 @@ if (!gotTheLock) {
setupAppEventEmitter(mainWindow);
});
app.on('before-quit', () => setIsAppQuitting(true));
app.on("before-quit", () => setIsAppQuitting(true));
}

View file

@ -1,71 +1,71 @@
import {
deleteDiskCache,
getCacheDirectory,
openDiskCache,
setCustomCacheDirectory,
} from "./api/cache";
import { computeImageEmbedding, computeTextEmbedding } from "./api/clip";
import {
getAppVersion,
getPlatform,
getSentryUserID,
logToDisk,
openDirectory,
openLogDirectory,
selectDirectory,
updateOptOutOfCrashReports,
} from "./api/common";
import { clearElectronStore } from "./api/electronStore";
import {
checkExistsAndCreateDir,
exists,
saveFileToDisk,
saveStreamToDisk,
} from "./api/export";
import { runFFmpegCmd } from "./api/ffmpeg";
import {
deleteFile,
deleteFolder,
getDirFiles,
isFolder,
moveFile,
readTextFile,
rename,
} from "./api/fs";
import { convertToJPEG, generateImageThumbnail } from "./api/imageProcessor";
import { getEncryptionKey, setEncryptionKey } from "./api/safeStorage";
import {
muteUpdateNotification,
registerForegroundEventListener,
registerUpdateEventListener,
reloadWindow,
sendNotification,
updateAndRestart,
skipAppUpdate,
muteUpdateNotification,
registerForegroundEventListener,
} from './api/system';
updateAndRestart,
} from "./api/system";
import {
getElectronFilesFromGoogleZip,
getPendingUploads,
setToUploadCollection,
setToUploadFiles,
showUploadDirsDialog,
showUploadFilesDialog,
showUploadZipDialog,
getPendingUploads,
setToUploadFiles,
getElectronFilesFromGoogleZip,
setToUploadCollection,
} from './api/upload';
} from "./api/upload";
import {
registerWatcherFunctions,
addWatchMapping,
removeWatchMapping,
updateWatchMappingSyncedFiles,
updateWatchMappingIgnoredFiles,
getWatchMappings,
} from './api/watch';
import { getEncryptionKey, setEncryptionKey } from './api/safeStorage';
import { clearElectronStore } from './api/electronStore';
registerWatcherFunctions,
removeWatchMapping,
updateWatchMappingIgnoredFiles,
updateWatchMappingSyncedFiles,
} from "./api/watch";
import { setupLogging } from "./utils/logging";
import { fixHotReloadNext12 } from "./utils/preload";
import {
openDiskCache,
deleteDiskCache,
getCacheDirectory,
setCustomCacheDirectory,
} from './api/cache';
import {
checkExistsAndCreateDir,
saveStreamToDisk,
saveFileToDisk,
exists,
} from './api/export';
import {
selectDirectory,
logToDisk,
openLogDirectory,
getSentryUserID,
getAppVersion,
openDirectory,
updateOptOutOfCrashReports,
getPlatform,
} from './api/common';
import { fixHotReloadNext12 } from './utils/preload';
import {
isFolder,
getDirFiles,
moveFile,
deleteFolder,
rename,
readTextFile,
deleteFile,
} from './api/fs';
import { convertToJPEG, generateImageThumbnail } from './api/imageProcessor';
import { setupLogging } from './utils/logging';
import {
setupRendererProcessStatsLogger,
logRendererProcessMemoryUsage,
} from './utils/processStats';
import { runFFmpegCmd } from './api/ffmpeg';
import { computeImageEmbedding, computeTextEmbedding } from './api/clip';
setupRendererProcessStatsLogger,
} from "./utils/processStats";
fixHotReloadNext12();
setupLogging();
@ -73,7 +73,7 @@ setupRendererProcessStatsLogger();
const windowObject: any = window;
windowObject['ElectronAPIs'] = {
windowObject["ElectronAPIs"] = {
exists,
checkExistsAndCreateDir,
saveStreamToDisk,

View file

@ -1,9 +1,12 @@
import { app, BrowserWindow } from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
import { setIsAppQuitting, setIsUpdateAvailable } from '../main';
import { compareVersions } from 'compare-versions';
import { AppUpdateInfo, GetFeatureFlagResponse } from '../types';
import { compareVersions } from "compare-versions";
import { app, BrowserWindow } from "electron";
import { default as ElectronLog, default as log } from "electron-log";
import { autoUpdater } from "electron-updater";
import fetch from "node-fetch";
import { setIsAppQuitting, setIsUpdateAvailable } from "../main";
import { AppUpdateInfo, GetFeatureFlagResponse } from "../types";
import { isPlatform } from "../utils/common/platform";
import { logErrorSentry } from "./sentry";
import {
clearMuteUpdateNotificationVersion,
clearSkipAppVersion,
@ -11,11 +14,7 @@ import {
getSkipAppVersion,
setMuteUpdateNotificationVersion,
setSkipAppVersion,
} from './userPreference';
import fetch from 'node-fetch';
import { logErrorSentry } from './sentry';
import ElectronLog from 'electron-log';
import { isPlatform } from '../utils/common/platform';
} from "./userPreference";
const FIVE_MIN_IN_MICROSECOND = 5 * 60 * 1000;
const ONE_DAY_IN_MICROSECOND = 1 * 24 * 60 * 60 * 1000;
@ -36,22 +35,22 @@ export function forceCheckForUpdateAndNotify(mainWindow: BrowserWindow) {
clearMuteUpdateNotificationVersion();
checkForUpdateAndNotify(mainWindow);
} catch (e) {
logErrorSentry(e, 'forceCheckForUpdateAndNotify failed');
logErrorSentry(e, "forceCheckForUpdateAndNotify failed");
}
}
async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
try {
log.debug('checkForUpdateAndNotify called');
log.debug("checkForUpdateAndNotify called");
const updateCheckResult = await autoUpdater.checkForUpdates();
log.debug('update version', updateCheckResult.updateInfo.version);
log.debug("update version", updateCheckResult.updateInfo.version);
if (
compareVersions(
updateCheckResult.updateInfo.version,
app.getVersion()
) <= 0
) {
log.debug('already at latest version');
log.debug("already at latest version");
return;
}
const skipAppVersion = getSkipAppVersion();
@ -60,7 +59,7 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
updateCheckResult.updateInfo.version === skipAppVersion
) {
log.info(
'user chose to skip version ',
"user chose to skip version ",
updateCheckResult.updateInfo.version
);
return;
@ -68,20 +67,20 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
const desktopCutoffVersion = await getDesktopCutoffVersion();
if (
desktopCutoffVersion &&
isPlatform('mac') &&
isPlatform("mac") &&
compareVersions(
updateCheckResult.updateInfo.version,
desktopCutoffVersion
) > 0
) {
log.debug('auto update not possible due to key change');
log.debug("auto update not possible due to key change");
showUpdateDialog(mainWindow, {
autoUpdatable: false,
version: updateCheckResult.updateInfo.version,
});
} else {
let timeout: NodeJS.Timeout;
log.debug('attempting auto update');
log.debug("attempting auto update");
autoUpdater.downloadUpdate();
const muteUpdateNotificationVersion =
getMuteUpdateNotificationVersion();
@ -91,12 +90,12 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
muteUpdateNotificationVersion
) {
log.info(
'user chose to mute update notification for version ',
"user chose to mute update notification for version ",
updateCheckResult.updateInfo.version
);
return;
}
autoUpdater.on('update-downloaded', () => {
autoUpdater.on("update-downloaded", () => {
timeout = setTimeout(
() =>
showUpdateDialog(mainWindow, {
@ -106,9 +105,9 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
FIVE_MIN_IN_MICROSECOND
);
});
autoUpdater.on('error', (error) => {
autoUpdater.on("error", (error) => {
clearTimeout(timeout);
logErrorSentry(error, 'auto update failed');
logErrorSentry(error, "auto update failed");
showUpdateDialog(mainWindow, {
autoUpdatable: false,
version: updateCheckResult.updateInfo.version,
@ -117,12 +116,12 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
}
setIsUpdateAvailable(true);
} catch (e) {
logErrorSentry(e, 'checkForUpdateAndNotify failed');
logErrorSentry(e, "checkForUpdateAndNotify failed");
}
}
export function updateAndRestart() {
ElectronLog.log('user quit the app');
ElectronLog.log("user quit the app");
setIsAppQuitting(true);
autoUpdater.quitAndInstall();
}
@ -142,11 +141,11 @@ export function muteUpdateNotification(version: string) {
async function getDesktopCutoffVersion() {
try {
const featureFlags = (
await fetch('https://static.ente.io/feature_flags.json')
await fetch("https://static.ente.io/feature_flags.json")
).json() as GetFeatureFlagResponse;
return featureFlags.desktopCutoffVersion;
} catch (e) {
logErrorSentry(e, 'failed to get feature flags');
logErrorSentry(e, "failed to get feature flags");
return undefined;
}
}
@ -155,5 +154,5 @@ function showUpdateDialog(
mainWindow: BrowserWindow,
updateInfo: AppUpdateInfo
) {
mainWindow.webContents.send('show-update-dialog', updateInfo);
mainWindow.webContents.send("show-update-dialog", updateInfo);
}

View file

@ -1,18 +1,18 @@
import { isPlatform } from '../utils/common/platform';
import { AutoLauncherClient } from '../types/autoLauncher';
import linuxAndWinAutoLauncher from './autoLauncherClients/linuxAndWinAutoLauncher';
import macAutoLauncher from './autoLauncherClients/macAutoLauncher';
import { AutoLauncherClient } from "../types/autoLauncher";
import { isPlatform } from "../utils/common/platform";
import linuxAndWinAutoLauncher from "./autoLauncherClients/linuxAndWinAutoLauncher";
import macAutoLauncher from "./autoLauncherClients/macAutoLauncher";
class AutoLauncher {
private client: AutoLauncherClient;
async init() {
if (isPlatform('linux') || isPlatform('windows')) {
if (isPlatform("linux") || isPlatform("windows")) {
this.client = linuxAndWinAutoLauncher;
} else {
this.client = macAutoLauncher;
}
// migrate old auto launch settings for windows from mac auto launcher to linux and windows auto launcher
if (isPlatform('windows') && (await macAutoLauncher.isEnabled())) {
if (isPlatform("windows") && (await macAutoLauncher.isEnabled())) {
await macAutoLauncher.toggleAutoLaunch();
await linuxAndWinAutoLauncher.toggleAutoLaunch();
}

View file

@ -1,14 +1,14 @@
import AutoLaunch from 'auto-launch';
import { AutoLauncherClient } from '../../types/autoLauncher';
import { app } from 'electron';
import AutoLaunch from "auto-launch";
import { app } from "electron";
import { AutoLauncherClient } from "../../types/autoLauncher";
const LAUNCHED_AS_HIDDEN_FLAG = 'hidden';
const LAUNCHED_AS_HIDDEN_FLAG = "hidden";
class LinuxAndWinAutoLauncher implements AutoLauncherClient {
private instance: AutoLaunch;
constructor() {
const autoLauncher = new AutoLaunch({
name: 'ente',
name: "ente",
isHidden: true,
});
this.instance = autoLauncher;

View file

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

View file

@ -1,7 +1,7 @@
import chokidar from 'chokidar';
import { BrowserWindow } from 'electron';
import { logError } from '../services/logging';
import { getWatchMappings } from '../api/watch';
import chokidar from "chokidar";
import { BrowserWindow } from "electron";
import { getWatchMappings } from "../api/watch";
import { logError } from "../services/logging";
export function initWatcher(mainWindow: BrowserWindow) {
const mappings = getWatchMappings();
@ -13,20 +13,20 @@ export function initWatcher(mainWindow: BrowserWindow) {
awaitWriteFinish: true,
});
watcher
.on('add', (path) => {
mainWindow.webContents.send('watch-add', path);
.on("add", (path) => {
mainWindow.webContents.send("watch-add", path);
})
.on('change', (path) => {
mainWindow.webContents.send('watch-change', path);
.on("change", (path) => {
mainWindow.webContents.send("watch-change", path);
})
.on('unlink', (path) => {
mainWindow.webContents.send('watch-unlink', path);
.on("unlink", (path) => {
mainWindow.webContents.send("watch-unlink", path);
})
.on('unlinkDir', (path) => {
mainWindow.webContents.send('watch-unlink-dir', path);
.on("unlinkDir", (path) => {
mainWindow.webContents.send("watch-unlink-dir", path);
})
.on('error', (error) => {
logError(error, 'error while watching files');
.on("error", (error) => {
logError(error, "error while watching files");
});
return watcher;

View file

@ -1,59 +1,59 @@
import * as log from 'electron-log';
import util from 'util';
import { logErrorSentry } from './sentry';
import { isDev } from '../utils/common';
import { app } from 'electron';
import path from 'path';
import { existsSync } from 'fs';
import fs from 'fs/promises';
const shellescape = require('any-shell-escape');
const execAsync = util.promisify(require('child_process').exec);
import fetch from 'node-fetch';
import { writeNodeStream } from './fs';
import { getPlatform } from '../utils/common/platform';
import { CustomErrors } from '../constants/errors';
const jpeg = require('jpeg-js');
import { app } from "electron";
import * as log from "electron-log";
import { existsSync } from "fs";
import fs from "fs/promises";
import fetch from "node-fetch";
import path from "path";
import { readFile } from "promise-fs";
import util from "util";
import { CustomErrors } from "../constants/errors";
import { Model } from "../types";
import Tokenizer from "../utils/clip-bpe-ts/mod";
import { isDev } from "../utils/common";
import { getPlatform } from "../utils/common/platform";
import { writeNodeStream } from "./fs";
import { logErrorSentry } from "./sentry";
const shellescape = require("any-shell-escape");
const execAsync = util.promisify(require("child_process").exec);
const jpeg = require("jpeg-js");
const CLIP_MODEL_PATH_PLACEHOLDER = 'CLIP_MODEL';
const GGMLCLIP_PATH_PLACEHOLDER = 'GGML_PATH';
const INPUT_PATH_PLACEHOLDER = 'INPUT';
const CLIP_MODEL_PATH_PLACEHOLDER = "CLIP_MODEL";
const GGMLCLIP_PATH_PLACEHOLDER = "GGML_PATH";
const INPUT_PATH_PLACEHOLDER = "INPUT";
const IMAGE_EMBEDDING_EXTRACT_CMD: string[] = [
GGMLCLIP_PATH_PLACEHOLDER,
'-mv',
"-mv",
CLIP_MODEL_PATH_PLACEHOLDER,
'--image',
"--image",
INPUT_PATH_PLACEHOLDER,
];
const TEXT_EMBEDDING_EXTRACT_CMD: string[] = [
GGMLCLIP_PATH_PLACEHOLDER,
'-mt',
"-mt",
CLIP_MODEL_PATH_PLACEHOLDER,
'--text',
"--text",
INPUT_PATH_PLACEHOLDER,
];
const ort = require('onnxruntime-node');
import Tokenizer from '../utils/clip-bpe-ts/mod';
import { readFile } from 'promise-fs';
import { Model } from '../types';
const ort = require("onnxruntime-node");
const TEXT_MODEL_DOWNLOAD_URL = {
ggml: 'https://models.ente.io/clip-vit-base-patch32_ggml-text-model-f16.gguf',
onnx: 'https://models.ente.io/clip-text-vit-32-uint8.onnx',
ggml: "https://models.ente.io/clip-vit-base-patch32_ggml-text-model-f16.gguf",
onnx: "https://models.ente.io/clip-text-vit-32-uint8.onnx",
};
const IMAGE_MODEL_DOWNLOAD_URL = {
ggml: 'https://models.ente.io/clip-vit-base-patch32_ggml-vision-model-f16.gguf',
onnx: 'https://models.ente.io/clip-image-vit-32-float32.onnx',
ggml: "https://models.ente.io/clip-vit-base-patch32_ggml-vision-model-f16.gguf",
onnx: "https://models.ente.io/clip-image-vit-32-float32.onnx",
};
const TEXT_MODEL_NAME = {
ggml: 'clip-vit-base-patch32_ggml-text-model-f16.gguf',
onnx: 'clip-text-vit-32-uint8.onnx',
ggml: "clip-vit-base-patch32_ggml-text-model-f16.gguf",
onnx: "clip-text-vit-32-uint8.onnx",
};
const IMAGE_MODEL_NAME = {
ggml: 'clip-vit-base-patch32_ggml-vision-model-f16.gguf',
onnx: 'clip-image-vit-32-float32.onnx',
ggml: "clip-vit-base-patch32_ggml-vision-model-f16.gguf",
onnx: "clip-image-vit-32-float32.onnx",
};
const IMAGE_MODEL_SIZE_IN_BYTES = {
@ -65,14 +65,14 @@ const TEXT_MODEL_SIZE_IN_BYTES = {
onnx: 64173509, // 61.2 MB
};
const MODEL_SAVE_FOLDER = 'models';
const MODEL_SAVE_FOLDER = "models";
function getModelSavePath(modelName: string) {
let userDataDir: string;
if (isDev) {
userDataDir = '.';
userDataDir = ".";
} else {
userDataDir = app.getPath('userData');
userDataDir = app.getPath("userData");
}
return path.join(userDataDir, MODEL_SAVE_FOLDER, modelName);
}
@ -81,26 +81,26 @@ async function downloadModel(saveLocation: string, url: string) {
// confirm that the save location exists
const saveDir = path.dirname(saveLocation);
if (!existsSync(saveDir)) {
log.info('creating model save dir');
log.info("creating model save dir");
await fs.mkdir(saveDir, { recursive: true });
}
log.info('downloading clip model');
log.info("downloading clip model");
const resp = await fetch(url);
await writeNodeStream(saveLocation, resp.body);
log.info('clip model downloaded');
log.info("clip model downloaded");
}
let imageModelDownloadInProgress: Promise<void> = null;
export async function getClipImageModelPath(type: 'ggml' | 'onnx') {
export async function getClipImageModelPath(type: "ggml" | "onnx") {
try {
const modelSavePath = getModelSavePath(IMAGE_MODEL_NAME[type]);
if (imageModelDownloadInProgress) {
log.info('waiting for image model download to finish');
log.info("waiting for image model download to finish");
await imageModelDownloadInProgress;
} else {
if (!existsSync(modelSavePath)) {
log.info('clip image model not found, downloading');
log.info("clip image model not found, downloading");
imageModelDownloadInProgress = downloadModel(
modelSavePath,
IMAGE_MODEL_DOWNLOAD_URL[type]
@ -110,7 +110,7 @@ export async function getClipImageModelPath(type: 'ggml' | 'onnx') {
const localFileSize = (await fs.stat(modelSavePath)).size;
if (localFileSize !== IMAGE_MODEL_SIZE_IN_BYTES[type]) {
log.info(
'clip image model size mismatch, downloading again got:',
"clip image model size mismatch, downloading again got:",
localFileSize
);
imageModelDownloadInProgress = downloadModel(
@ -129,13 +129,13 @@ export async function getClipImageModelPath(type: 'ggml' | 'onnx') {
let textModelDownloadInProgress: boolean = false;
export async function getClipTextModelPath(type: 'ggml' | 'onnx') {
export async function getClipTextModelPath(type: "ggml" | "onnx") {
const modelSavePath = getModelSavePath(TEXT_MODEL_NAME[type]);
if (textModelDownloadInProgress) {
throw Error(CustomErrors.MODEL_DOWNLOAD_PENDING);
} else {
if (!existsSync(modelSavePath)) {
log.info('clip text model not found, downloading');
log.info("clip text model not found, downloading");
textModelDownloadInProgress = true;
downloadModel(modelSavePath, TEXT_MODEL_DOWNLOAD_URL[type])
.catch(() => {
@ -149,7 +149,7 @@ export async function getClipTextModelPath(type: 'ggml' | 'onnx') {
const localFileSize = (await fs.stat(modelSavePath)).size;
if (localFileSize !== TEXT_MODEL_SIZE_IN_BYTES[type]) {
log.info(
'clip text model size mismatch, downloading again got:',
"clip text model size mismatch, downloading again got:",
localFileSize
);
textModelDownloadInProgress = true;
@ -169,7 +169,7 @@ export async function getClipTextModelPath(type: 'ggml' | 'onnx') {
function getGGMLClipPath() {
return isDev
? path.join('./build', `ggmlclip-${getPlatform()}`)
? path.join("./build", `ggmlclip-${getPlatform()}`)
: path.join(process.resourcesPath, `ggmlclip-${getPlatform()}`);
}
@ -185,7 +185,7 @@ let onnxImageSessionPromise: Promise<any> = null;
async function getOnnxImageSession() {
if (!onnxImageSessionPromise) {
onnxImageSessionPromise = (async () => {
const clipModelPath = await getClipImageModelPath('onnx');
const clipModelPath = await getClipImageModelPath("onnx");
return createOnnxSession(clipModelPath);
})();
}
@ -196,7 +196,7 @@ let onnxTextSession: any = null;
async function getOnnxTextSession() {
if (!onnxTextSession) {
const clipModelPath = await getClipTextModelPath('onnx');
const clipModelPath = await getClipTextModelPath("onnx");
onnxTextSession = await createOnnxSession(clipModelPath);
}
return onnxTextSession;
@ -230,7 +230,7 @@ export async function computeGGMLImageEmbedding(
inputFilePath: string
): Promise<Float32Array> {
try {
const clipModelPath = await getClipImageModelPath('ggml');
const clipModelPath = await getClipImageModelPath("ggml");
const ggmlclipPath = getGGMLClipPath();
const cmd = IMAGE_EMBEDDING_EXTRACT_CMD.map((cmdPart) => {
if (cmdPart === GGMLCLIP_PATH_PLACEHOLDER) {
@ -245,19 +245,19 @@ export async function computeGGMLImageEmbedding(
});
const escapedCmd = shellescape(cmd);
log.info('running clip command', escapedCmd);
log.info("running clip command", escapedCmd);
const startTime = Date.now();
const { stdout } = await execAsync(escapedCmd);
log.info('clip command execution time ', Date.now() - startTime);
log.info("clip command execution time ", Date.now() - startTime);
// parse stdout and return embedding
// get the last line of stdout
const lines = stdout.split('\n');
const lines = stdout.split("\n");
const lastLine = lines[lines.length - 1];
const embedding = JSON.parse(lastLine);
const embeddingArray = new Float32Array(embedding);
return embeddingArray;
} catch (err) {
logErrorSentry(err, 'Error in computeGGMLImageEmbedding');
logErrorSentry(err, "Error in computeGGMLImageEmbedding");
throw err;
}
}
@ -270,7 +270,7 @@ export async function computeONNXImageEmbedding(
const t1 = Date.now();
const rgbData = await getRGBData(inputFilePath);
const feeds = {
input: new ort.Tensor('float32', rgbData, [1, 3, 224, 224]),
input: new ort.Tensor("float32", rgbData, [1, 3, 224, 224]),
};
const t2 = Date.now();
const results = await imageSession.run(feeds);
@ -279,10 +279,10 @@ export async function computeONNXImageEmbedding(
t2 - t1
} ms, extraction: ${Date.now() - t2} ms)`
);
const imageEmbedding = results['output'].data; // Float32Array
const imageEmbedding = results["output"].data; // Float32Array
return normalizeEmbedding(imageEmbedding);
} catch (err) {
logErrorSentry(err, 'Error in computeONNXImageEmbedding');
logErrorSentry(err, "Error in computeONNXImageEmbedding");
throw err;
}
}
@ -302,7 +302,7 @@ export async function computeGGMLTextEmbedding(
text: string
): Promise<Float32Array> {
try {
const clipModelPath = await getClipTextModelPath('ggml');
const clipModelPath = await getClipTextModelPath("ggml");
const ggmlclipPath = getGGMLClipPath();
const cmd = TEXT_EMBEDDING_EXTRACT_CMD.map((cmdPart) => {
if (cmdPart === GGMLCLIP_PATH_PLACEHOLDER) {
@ -317,13 +317,13 @@ export async function computeGGMLTextEmbedding(
});
const escapedCmd = shellescape(cmd);
log.info('running clip command', escapedCmd);
log.info("running clip command", escapedCmd);
const startTime = Date.now();
const { stdout } = await execAsync(escapedCmd);
log.info('clip command execution time ', Date.now() - startTime);
log.info("clip command execution time ", Date.now() - startTime);
// parse stdout and return embedding
// get the last line of stdout
const lines = stdout.split('\n');
const lines = stdout.split("\n");
const lastLine = lines[lines.length - 1];
const embedding = JSON.parse(lastLine);
const embeddingArray = new Float32Array(embedding);
@ -332,7 +332,7 @@ export async function computeGGMLTextEmbedding(
if (err.message === CustomErrors.MODEL_DOWNLOAD_PENDING) {
log.info(CustomErrors.MODEL_DOWNLOAD_PENDING);
} else {
logErrorSentry(err, 'Error in computeGGMLTextEmbedding');
logErrorSentry(err, "Error in computeGGMLTextEmbedding");
}
throw err;
}
@ -347,7 +347,7 @@ export async function computeONNXTextEmbedding(
const tokenizer = getTokenizer();
const tokenizedText = Int32Array.from(tokenizer.encodeForCLIP(text));
const feeds = {
input: new ort.Tensor('int32', tokenizedText, [1, 77]),
input: new ort.Tensor("int32", tokenizedText, [1, 77]),
};
const t2 = Date.now();
const results = await imageSession.run(feeds);
@ -356,13 +356,13 @@ export async function computeONNXTextEmbedding(
t2 - t1
} ms, extraction: ${Date.now() - t2} ms)`
);
const textEmbedding = results['output'].data; // Float32Array
const textEmbedding = results["output"].data; // Float32Array
return normalizeEmbedding(textEmbedding);
} catch (err) {
if (err.message === CustomErrors.MODEL_DOWNLOAD_PENDING) {
log.info(CustomErrors.MODEL_DOWNLOAD_PENDING);
} else {
logErrorSentry(err, 'Error in computeONNXTextEmbedding');
logErrorSentry(err, "Error in computeONNXTextEmbedding");
}
throw err;
}
@ -377,7 +377,7 @@ async function getRGBData(inputFilePath: string) {
formatAsRGBA: false,
});
} catch (err) {
logErrorSentry(err, 'JPEG decode error');
logErrorSentry(err, "JPEG decode error");
throw err;
}
@ -447,7 +447,7 @@ export const computeClipMatchScore = async (
textEmbedding: Float32Array
) => {
if (imageEmbedding.length !== textEmbedding.length) {
throw Error('imageEmbedding and textEmbedding length mismatch');
throw Error("imageEmbedding and textEmbedding length mismatch");
}
let score = 0;
for (let index = 0; index < imageEmbedding.length; index++) {

View file

@ -1,10 +1,10 @@
import DiskLRUService from '../services/diskLRU';
import crypto from 'crypto';
import { existsSync, unlink, rename, stat } from 'promise-fs';
import path from 'path';
import { LimitedCache } from '../types/cache';
import { logError } from './logging';
import { getFileStream, writeStream } from './fs';
import crypto from "crypto";
import path from "path";
import { existsSync, rename, stat, unlink } from "promise-fs";
import DiskLRUService from "../services/diskLRU";
import { LimitedCache } from "../types/cache";
import { getFileStream, writeStream } from "./fs";
import { logError } from "./logging";
const DEFAULT_CACHE_LIMIT = 1000 * 1000 * 1000; // 1GB
@ -33,11 +33,11 @@ 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;
logError(e, 'Failed to delete cache key');
if (e.code === "ENOENT") return;
logError(e, "Failed to delete cache key");
});
return undefined;
}
@ -54,11 +54,11 @@ export class DiskCache implements LimitedCache {
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;
logError(e, 'Failed to delete cache key');
if (e.code === "ENOENT") return;
logError(e, "Failed to delete cache key");
});
return undefined;
}
@ -83,9 +83,9 @@ export class DiskCache implements LimitedCache {
function getOldAssetCachePath(cacheDir: string, cacheKey: string) {
// hashing the key to prevent illegal filenames
const cacheKeyHash = crypto
.createHash('sha256')
.createHash("sha256")
.update(cacheKey)
.digest('hex');
.digest("hex");
return path.join(cacheDir, cacheKeyHash);
}
@ -93,6 +93,6 @@ async function migrateOldCacheKey(oldCacheKey: string, newCacheKey: string) {
try {
await rename(oldCacheKey, newCacheKey);
} catch (e) {
logError(e, 'Failed to move cache key to new cache key');
logError(e, "Failed to move cache key to new cache key");
}
}

View file

@ -1,8 +1,7 @@
import path from 'path';
import { readdir, stat, unlink } from 'promise-fs';
import getFolderSize from 'get-folder-size';
import { utimes, close, open } from 'promise-fs';
import { logError } from '../services/logging';
import getFolderSize from "get-folder-size";
import path from "path";
import { close, open, readdir, stat, unlink, utimes } from "promise-fs";
import { logError } from "../services/logging";
export interface LeastRecentlyUsedResult {
atime: Date;
@ -18,11 +17,11 @@ class DiskLRUService {
const time = new Date();
await utimes(path, time, time);
} catch (err) {
logError(err, 'utimes method touch failed');
logError(err, "utimes method touch failed");
try {
await close(await open(path, 'w'));
await close(await open(path, "w"));
} catch (e) {
logError(e, 'open-close method touch failed');
logError(e, "open-close method touch failed");
}
// log and ignore
}
@ -58,10 +57,10 @@ class DiskLRUService {
} catch (e) {
// ENOENT: File not found
// which can be ignored as we are trying to delete the file anyway
if (e.code !== 'ENOENT') {
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
@ -72,7 +71,7 @@ class DiskLRUService {
});
});
} catch (e) {
logError(e, 'evictLeastRecentlyUsed failed');
logError(e, "evictLeastRecentlyUsed failed");
}
}
@ -80,7 +79,7 @@ class DiskLRUService {
dir: string,
result?: LeastRecentlyUsedResult
): Promise<LeastRecentlyUsedResult> {
result = result || { atime: new Date(), path: '' };
result = result || { atime: new Date(), path: "" };
const files = await readdir(dir);
for (const file of files) {

View file

@ -1,23 +1,23 @@
import pathToFfmpeg from 'ffmpeg-static';
const shellescape = require('any-shell-escape');
import util from 'util';
import log from 'electron-log';
import { readFile, rmSync, writeFile } from 'promise-fs';
import { logErrorSentry } from './sentry';
import { generateTempFilePath, getTempDirPath } from '../utils/temp';
import { existsSync } from 'fs';
import { promiseWithTimeout } from '../utils/common';
import log from "electron-log";
import pathToFfmpeg from "ffmpeg-static";
import { existsSync } from "fs";
import { readFile, rmSync, writeFile } from "promise-fs";
import util from "util";
import { promiseWithTimeout } from "../utils/common";
import { generateTempFilePath, getTempDirPath } from "../utils/temp";
import { logErrorSentry } from "./sentry";
const shellescape = require("any-shell-escape");
const execAsync = util.promisify(require('child_process').exec);
const execAsync = util.promisify(require("child_process").exec);
const FFMPEG_EXECUTION_WAIT_TIME = 30 * 1000;
const INPUT_PATH_PLACEHOLDER = 'INPUT';
const FFMPEG_PLACEHOLDER = 'FFMPEG';
const OUTPUT_PATH_PLACEHOLDER = 'OUTPUT';
const INPUT_PATH_PLACEHOLDER = "INPUT";
const FFMPEG_PLACEHOLDER = "FFMPEG";
const OUTPUT_PATH_PLACEHOLDER = "OUTPUT";
function getFFmpegStaticPath() {
return pathToFfmpeg.replace('app.asar', 'app.asar.unpacked');
return pathToFfmpeg.replace("app.asar", "app.asar.unpacked");
}
export async function runFFmpegCmd(
@ -42,7 +42,7 @@ export async function runFFmpegCmd(
}
});
const escapedCmd = shellescape(cmd);
log.info('running ffmpeg command', escapedCmd);
log.info("running ffmpeg command", escapedCmd);
const startTime = Date.now();
if (dontTimeout) {
await execAsync(escapedCmd);
@ -53,25 +53,25 @@ export async function runFFmpegCmd(
);
}
if (!existsSync(tempOutputFilePath)) {
throw new Error('ffmpeg output file not found');
throw new Error("ffmpeg output file not found");
}
log.info(
'ffmpeg command execution time ',
"ffmpeg command execution time ",
escapedCmd,
Date.now() - startTime,
'ms'
"ms"
);
const outputFile = await readFile(tempOutputFilePath);
return new Uint8Array(outputFile);
} catch (e) {
logErrorSentry(e, 'ffmpeg run command error');
logErrorSentry(e, "ffmpeg run command error");
throw e;
} finally {
try {
rmSync(tempOutputFilePath, { force: true });
} catch (e) {
logErrorSentry(e, 'failed to remove tempOutputFile');
logErrorSentry(e, "failed to remove tempOutputFile");
}
}
}
@ -86,8 +86,8 @@ export async function deleteTempFile(tempFilePath: string) {
const tempDirPath = await getTempDirPath();
if (!tempFilePath.startsWith(tempDirPath)) {
logErrorSentry(
Error('not a temp file'),
'tried to delete a non temp file'
Error("not a temp file"),
"tried to delete a non temp file"
);
}
rmSync(tempFilePath, { force: true });

View file

@ -1,11 +1,11 @@
import { FILE_STREAM_CHUNK_SIZE } from '../config';
import path from 'path';
import * as fs from 'promise-fs';
import { ElectronFile } from '../types';
import StreamZip from 'node-stream-zip';
import { Readable } from 'stream';
import { logError } from './logging';
import { existsSync } from 'fs';
import { existsSync } from "fs";
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";
// https://stackoverflow.com/a/63111390
export const getDirFilePaths = async (dirPath: string) => {
@ -25,7 +25,7 @@ export const getDirFilePaths = async (dirPath: string) => {
};
export const getFileStream = async (filePath: string) => {
const file = await fs.open(filePath, 'r');
const file = await fs.open(filePath, "r");
let offset = 0;
const readableStream = new ReadableStream<Uint8Array>({
async pull(controller) {
@ -66,20 +66,20 @@ export async function getElectronFile(filePath: string): Promise<ElectronFile> {
lastModified: fileStats.mtime.valueOf(),
stream: async () => {
if (!existsSync(filePath)) {
throw new Error('electronFile does not exist');
throw new Error("electronFile does not exist");
}
return await getFileStream(filePath);
},
blob: async () => {
if (!existsSync(filePath)) {
throw new Error('electronFile does not exist');
throw new Error("electronFile does not exist");
}
const blob = await fs.readFile(filePath);
return new Blob([new Uint8Array(blob)]);
},
arrayBuffer: async () => {
if (!existsSync(filePath)) {
throw new Error('electronFile does not exist');
throw new Error("electronFile does not exist");
}
const blob = await fs.readFile(filePath);
return new Uint8Array(blob);
@ -113,7 +113,7 @@ export const getZipFileStream = async (
};
let resolveObj: (value?: any) => void = null;
let rejectObj: (reason?: any) => void = null;
stream.on('readable', () => {
stream.on("readable", () => {
try {
if (resolveObj) {
inProgress.current = true;
@ -128,7 +128,7 @@ export const getZipFileStream = async (
rejectObj(e);
}
});
stream.on('end', () => {
stream.on("end", () => {
try {
done.current = true;
if (resolveObj && !inProgress.current) {
@ -139,7 +139,7 @@ export const getZipFileStream = async (
rejectObj(e);
}
});
stream.on('error', (e) => {
stream.on("error", (e) => {
try {
done.current = true;
if (rejectObj) {
@ -175,7 +175,7 @@ export const getZipFileStream = async (
controller.close();
}
} catch (e) {
logError(e, 'readableStream pull failed');
logError(e, "readableStream pull failed");
controller.close();
}
},
@ -190,14 +190,14 @@ export async function isFolder(dirPath: string) {
} catch (e) {
let err = e;
// if code is defined, it's an error from fs.stat
if (typeof e.code !== 'undefined') {
if (typeof e.code !== "undefined") {
// ENOENT means the file does not exist
if (e.code === 'ENOENT') {
if (e.code === "ENOENT") {
return false;
}
err = Error(`fs error code: ${e.code}`);
}
logError(err, 'isFolder failed');
logError(err, "isFolder failed");
return false;
}
}
@ -219,7 +219,7 @@ export const convertBrowserStreamToNode = (
return;
}
} catch (e) {
rs.emit('error', e);
rs.emit("error", e);
}
};
@ -232,15 +232,15 @@ export async function writeNodeStream(
) {
const writeable = fs.createWriteStream(filePath);
fileStream.on('error', (error) => {
fileStream.on("error", (error) => {
writeable.destroy(error); // Close the writable stream with an error
});
fileStream.pipe(writeable);
await new Promise((resolve, reject) => {
writeable.on('finish', resolve);
writeable.on('error', async (e) => {
writeable.on("finish", resolve);
writeable.on("error", async (e) => {
if (existsSync(filePath)) {
await fs.unlink(filePath);
}
@ -259,9 +259,9 @@ export async function writeStream(
export async function readTextFile(filePath: string) {
if (!existsSync(filePath)) {
throw new Error('File does not exist');
throw new Error("File does not exist");
}
return await fs.readFile(filePath, 'utf-8');
return await fs.readFile(filePath, "utf-8");
}
export async function moveFile(
@ -269,10 +269,10 @@ export async function moveFile(
destinationPath: string
): Promise<void> {
if (!existsSync(sourcePath)) {
throw new Error('File does not exist');
throw new Error("File does not exist");
}
if (existsSync(destinationPath)) {
throw new Error('Destination file already exists');
throw new Error("Destination file already exists");
}
// check if destination folder exists
const destinationFolder = path.dirname(destinationPath);
@ -287,19 +287,19 @@ export async function deleteFolder(folderPath: string): Promise<void> {
return;
}
if (!fs.statSync(folderPath).isDirectory()) {
throw new Error('Path is not a folder');
throw new Error("Path is not a folder");
}
// check if folder is empty
const files = await fs.readdir(folderPath);
if (files.length > 0) {
throw new Error('Folder is not empty');
throw new Error("Folder is not empty");
}
await fs.rmdir(folderPath);
}
export async function rename(oldPath: string, newPath: string) {
if (!existsSync(oldPath)) {
throw new Error('Path does not exist');
throw new Error("Path does not exist");
}
await fs.rename(oldPath, newPath);
}
@ -309,7 +309,7 @@ export function deleteFile(filePath: string): void {
return;
}
if (!fs.statSync(filePath).isFile()) {
throw new Error('Path is not a file');
throw new Error("Path is not a file");
}
fs.rmSync(filePath);
}

View file

@ -1,81 +1,81 @@
import util from 'util';
import { exec } from 'child_process';
import { exec } from "child_process";
import util from "util";
import { existsSync, rmSync } from 'fs';
import { readFile, writeFile } from 'promise-fs';
import { generateTempFilePath } from '../utils/temp';
import { logErrorSentry } from './sentry';
import { isPlatform } from '../utils/common/platform';
import { isDev } from '../utils/common';
import path from 'path';
import log from 'electron-log';
import { CustomErrors } from '../constants/errors';
const shellescape = require('any-shell-escape');
import log from "electron-log";
import { existsSync, rmSync } from "fs";
import path from "path";
import { readFile, writeFile } from "promise-fs";
import { CustomErrors } from "../constants/errors";
import { isDev } from "../utils/common";
import { isPlatform } from "../utils/common/platform";
import { generateTempFilePath } from "../utils/temp";
import { logErrorSentry } from "./sentry";
const shellescape = require("any-shell-escape");
const asyncExec = util.promisify(exec);
const IMAGE_MAGICK_PLACEHOLDER = 'IMAGE_MAGICK';
const MAX_DIMENSION_PLACEHOLDER = 'MAX_DIMENSION';
const SAMPLE_SIZE_PLACEHOLDER = 'SAMPLE_SIZE';
const INPUT_PATH_PLACEHOLDER = 'INPUT';
const OUTPUT_PATH_PLACEHOLDER = 'OUTPUT';
const QUALITY_PLACEHOLDER = 'QUALITY';
const IMAGE_MAGICK_PLACEHOLDER = "IMAGE_MAGICK";
const MAX_DIMENSION_PLACEHOLDER = "MAX_DIMENSION";
const SAMPLE_SIZE_PLACEHOLDER = "SAMPLE_SIZE";
const INPUT_PATH_PLACEHOLDER = "INPUT";
const OUTPUT_PATH_PLACEHOLDER = "OUTPUT";
const QUALITY_PLACEHOLDER = "QUALITY";
const MAX_QUALITY = 70;
const MIN_QUALITY = 50;
const SIPS_HEIC_CONVERT_COMMAND_TEMPLATE = [
'sips',
'-s',
'format',
'jpeg',
"sips",
"-s",
"format",
"jpeg",
INPUT_PATH_PLACEHOLDER,
'--out',
"--out",
OUTPUT_PATH_PLACEHOLDER,
];
const SIPS_THUMBNAIL_GENERATE_COMMAND_TEMPLATE = [
'sips',
'-s',
'format',
'jpeg',
'-s',
'formatOptions',
"sips",
"-s",
"format",
"jpeg",
"-s",
"formatOptions",
QUALITY_PLACEHOLDER,
'-Z',
"-Z",
MAX_DIMENSION_PLACEHOLDER,
INPUT_PATH_PLACEHOLDER,
'--out',
"--out",
OUTPUT_PATH_PLACEHOLDER,
];
const IMAGEMAGICK_HEIC_CONVERT_COMMAND_TEMPLATE = [
IMAGE_MAGICK_PLACEHOLDER,
INPUT_PATH_PLACEHOLDER,
'-quality',
'100%',
"-quality",
"100%",
OUTPUT_PATH_PLACEHOLDER,
];
const IMAGE_MAGICK_THUMBNAIL_GENERATE_COMMAND_TEMPLATE = [
IMAGE_MAGICK_PLACEHOLDER,
'-auto-orient',
'-define',
"-auto-orient",
"-define",
`jpeg:size=${SAMPLE_SIZE_PLACEHOLDER}x${SAMPLE_SIZE_PLACEHOLDER}`,
INPUT_PATH_PLACEHOLDER,
'-thumbnail',
"-thumbnail",
`${MAX_DIMENSION_PLACEHOLDER}x${MAX_DIMENSION_PLACEHOLDER}>`,
'-unsharp',
'0x.5',
'-quality',
"-unsharp",
"0x.5",
"-quality",
QUALITY_PLACEHOLDER,
OUTPUT_PATH_PLACEHOLDER,
];
function getImageMagickStaticPath() {
return isDev
? 'build/image-magick'
: path.join(process.resourcesPath, 'image-magick');
? "build/image-magick"
: path.join(process.resourcesPath, "image-magick");
}
export async function convertToJPEG(
@ -86,32 +86,32 @@ export async function convertToJPEG(
let tempOutputFilePath: string;
try {
tempInputFilePath = await generateTempFilePath(filename);
tempOutputFilePath = await generateTempFilePath('output.jpeg');
tempOutputFilePath = await generateTempFilePath("output.jpeg");
await writeFile(tempInputFilePath, fileData);
await runConvertCommand(tempInputFilePath, tempOutputFilePath);
if (!existsSync(tempOutputFilePath)) {
throw new Error('heic convert output file not found');
throw new Error("heic convert output file not found");
}
const convertedFileData = new Uint8Array(
await readFile(tempOutputFilePath)
);
return convertedFileData;
} catch (e) {
logErrorSentry(e, 'failed to convert heic');
logErrorSentry(e, "failed to convert heic");
throw e;
} finally {
try {
rmSync(tempInputFilePath, { force: true });
} catch (e) {
logErrorSentry(e, 'failed to remove tempInputFile');
logErrorSentry(e, "failed to remove tempInputFile");
}
try {
rmSync(tempOutputFilePath, { force: true });
} catch (e) {
logErrorSentry(e, 'failed to remove tempOutputFile');
logErrorSentry(e, "failed to remove tempOutputFile");
}
}
}
@ -125,7 +125,7 @@ async function runConvertCommand(
tempOutputFilePath
);
const escapedCmd = shellescape(convertCmd);
log.info('running convert command: ' + escapedCmd);
log.info("running convert command: " + escapedCmd);
await asyncExec(escapedCmd);
}
@ -134,7 +134,7 @@ function constructConvertCommand(
tempOutputFilePath: string
) {
let convertCmd: string[];
if (isPlatform('mac')) {
if (isPlatform("mac")) {
convertCmd = SIPS_HEIC_CONVERT_COMMAND_TEMPLATE.map((cmdPart) => {
if (cmdPart === INPUT_PATH_PLACEHOLDER) {
return tempInputFilePath;
@ -144,7 +144,7 @@ function constructConvertCommand(
}
return cmdPart;
});
} else if (isPlatform('linux')) {
} else if (isPlatform("linux")) {
convertCmd = IMAGEMAGICK_HEIC_CONVERT_COMMAND_TEMPLATE.map(
(cmdPart) => {
if (cmdPart === IMAGE_MAGICK_PLACEHOLDER) {
@ -173,7 +173,7 @@ export async function generateImageThumbnail(
let tempOutputFilePath: string;
let quality = MAX_QUALITY;
try {
tempOutputFilePath = await generateTempFilePath('thumb.jpeg');
tempOutputFilePath = await generateTempFilePath("thumb.jpeg");
let thumbnail: Uint8Array;
do {
await runThumbnailGenerationCommand(
@ -184,20 +184,20 @@ export async function generateImageThumbnail(
);
if (!existsSync(tempOutputFilePath)) {
throw new Error('output thumbnail file not found');
throw new Error("output thumbnail file not found");
}
thumbnail = new Uint8Array(await readFile(tempOutputFilePath));
quality -= 10;
} while (thumbnail.length > maxSize && quality > MIN_QUALITY);
return thumbnail;
} catch (e) {
logErrorSentry(e, 'generate image thumbnail failed');
logErrorSentry(e, "generate image thumbnail failed");
throw e;
} finally {
try {
rmSync(tempOutputFilePath, { force: true });
} catch (e) {
logErrorSentry(e, 'failed to remove tempOutputFile');
logErrorSentry(e, "failed to remove tempOutputFile");
}
}
}
@ -216,7 +216,7 @@ async function runThumbnailGenerationCommand(
quality
);
const escapedCmd = shellescape(thumbnailGenerationCmd);
log.info('running thumbnail generation command: ' + escapedCmd);
log.info("running thumbnail generation command: " + escapedCmd);
await asyncExec(escapedCmd);
}
function constructThumbnailGenerationCommand(
@ -226,7 +226,7 @@ function constructThumbnailGenerationCommand(
quality: number
) {
let thumbnailGenerationCmd: string[];
if (isPlatform('mac')) {
if (isPlatform("mac")) {
thumbnailGenerationCmd = SIPS_THUMBNAIL_GENERATE_COMMAND_TEMPLATE.map(
(cmdPart) => {
if (cmdPart === INPUT_PATH_PLACEHOLDER) {
@ -244,7 +244,7 @@ function constructThumbnailGenerationCommand(
return cmdPart;
}
);
} else if (isPlatform('linux')) {
} else if (isPlatform("linux")) {
thumbnailGenerationCmd =
IMAGE_MAGICK_THUMBNAIL_GENERATE_COMMAND_TEMPLATE.map((cmdPart) => {
if (cmdPart === IMAGE_MAGICK_PLACEHOLDER) {

View file

@ -1,22 +1,22 @@
import log from 'electron-log';
import { ipcRenderer } from 'electron';
import { ipcRenderer } from "electron";
import log from "electron-log";
export function logToDisk(logLine: string) {
log.info(logLine);
}
export function openLogDirectory() {
ipcRenderer.invoke('open-log-dir');
ipcRenderer.invoke("open-log-dir");
}
export function logError(error: Error, message: string, info?: string): void {
ipcRenderer.invoke('log-error', error, message, info);
ipcRenderer.invoke("log-error", error, message, info);
}
export function getSentryUserID(): Promise<string> {
return ipcRenderer.invoke('get-sentry-id');
return ipcRenderer.invoke("get-sentry-id");
}
export function updateOptOutOfCrashReports(optOut: boolean) {
return ipcRenderer.invoke('update-opt-out-crash-reports', optOut);
return ipcRenderer.invoke("update-opt-out-crash-reports", optOut);
}

View file

@ -1,12 +1,12 @@
import * as Sentry from '@sentry/electron/dist/main';
import { makeID } from '../utils/logging';
import { keysStore } from '../stores/keys.store';
import { SENTRY_DSN, RELEASE_VERSION } from '../config';
import { isDev } from '../utils/common';
import { logToDisk } from './logging';
import { hasOptedOutOfCrashReports } from '../main';
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 ENV_DEVELOPMENT = "development";
const isDEVSentryENV = () =>
process.env.NEXT_PUBLIC_SENTRY_ENV === ENV_DEVELOPMENT;
@ -15,7 +15,7 @@ export function initSentry(): void {
Sentry.init({
dsn: SENTRY_DSN,
release: RELEASE_VERSION,
environment: isDev ? 'development' : 'production',
environment: isDev ? "development" : "production",
});
Sentry.setUser({ id: getSentryUserID() });
}
@ -52,17 +52,17 @@ export function logErrorSentry(
function errorWithContext(originalError: Error, context: string) {
const errorWithContext = new Error(context);
errorWithContext.stack =
errorWithContext.stack.split('\n').slice(2, 4).join('\n') +
'\n' +
errorWithContext.stack.split("\n").slice(2, 4).join("\n") +
"\n" +
originalError.stack;
return errorWithContext;
}
export function getSentryUserID() {
let anonymizeUserID = keysStore.get('AnonymizeUserID')?.id;
let anonymizeUserID = keysStore.get("AnonymizeUserID")?.id;
if (!anonymizeUserID) {
anonymizeUserID = makeID(6);
keysStore.set('AnonymizeUserID', { id: anonymizeUserID });
keysStore.set("AnonymizeUserID", { id: anonymizeUserID });
}
return anonymizeUserID;
}

View file

@ -1,8 +1,8 @@
import StreamZip from 'node-stream-zip';
import path from 'path';
import { uploadStatusStore } from '../stores/upload.store';
import { FILE_PATH_TYPE, FILE_PATH_KEYS, ElectronFile } from '../types';
import { getValidPaths, getZipFileStream } from './fs';
import StreamZip from "node-stream-zip";
import path from "path";
import { uploadStatusStore } from "../stores/upload.store";
import { ElectronFile, FILE_PATH_KEYS, FILE_PATH_TYPE } from "../types";
import { getValidPaths, getZipFileStream } from "./fs";
export const getSavedFilePaths = (type: FILE_PATH_TYPE) => {
const paths =
@ -52,9 +52,9 @@ export const setToUploadFiles = (type: FILE_PATH_TYPE, filePaths: string[]) => {
export const setToUploadCollection = (collectionName: string) => {
if (collectionName) {
uploadStatusStore.set('collectionName', collectionName);
uploadStatusStore.set("collectionName", collectionName);
} else {
uploadStatusStore.delete('collectionName');
uploadStatusStore.delete("collectionName");
}
};
@ -62,14 +62,14 @@ export const getElectronFilesFromGoogleZip = async (filePath: string) => {
const zip = new StreamZip.async({
file: filePath,
});
const zipName = path.basename(filePath, '.zip');
const zipName = path.basename(filePath, ".zip");
const entries = await zip.entries();
const files: ElectronFile[] = [];
for (const entry of Object.values(entries)) {
const basename = path.basename(entry.name);
if (entry.isFile && basename.length > 0 && basename[0] !== '.') {
if (entry.isFile && basename.length > 0 && basename[0] !== ".") {
files.push(await getZipEntryAsElectronFile(zipName, zip, entry));
}
}

View file

@ -1,49 +1,49 @@
import { userPreferencesStore } from '../stores/userPreferences.store';
import { userPreferencesStore } from "../stores/userPreferences.store";
export function getHideDockIconPreference() {
return userPreferencesStore.get('hideDockIcon');
return userPreferencesStore.get("hideDockIcon");
}
export function setHideDockIconPreference(shouldHideDockIcon: boolean) {
userPreferencesStore.set('hideDockIcon', shouldHideDockIcon);
userPreferencesStore.set("hideDockIcon", shouldHideDockIcon);
}
export function getSkipAppVersion() {
return userPreferencesStore.get('skipAppVersion');
return userPreferencesStore.get("skipAppVersion");
}
export function setSkipAppVersion(version: string) {
userPreferencesStore.set('skipAppVersion', version);
userPreferencesStore.set("skipAppVersion", version);
}
export function getMuteUpdateNotificationVersion() {
return userPreferencesStore.get('muteUpdateNotificationVersion');
return userPreferencesStore.get("muteUpdateNotificationVersion");
}
export function setMuteUpdateNotificationVersion(version: string) {
userPreferencesStore.set('muteUpdateNotificationVersion', version);
userPreferencesStore.set("muteUpdateNotificationVersion", version);
}
export function getOptOutOfCrashReports() {
return userPreferencesStore.get('optOutOfCrashReports') ?? false;
return userPreferencesStore.get("optOutOfCrashReports") ?? false;
}
export function setOptOutOfCrashReports(optOut: boolean) {
userPreferencesStore.set('optOutOfCrashReports', optOut);
userPreferencesStore.set("optOutOfCrashReports", optOut);
}
export function clearSkipAppVersion() {
userPreferencesStore.delete('skipAppVersion');
userPreferencesStore.delete("skipAppVersion");
}
export function clearMuteUpdateNotificationVersion() {
userPreferencesStore.delete('muteUpdateNotificationVersion');
userPreferencesStore.delete("muteUpdateNotificationVersion");
}
export function setCustomCacheDirectory(directory: string) {
userPreferencesStore.set('customCacheDirectory', directory);
userPreferencesStore.set("customCacheDirectory", directory);
}
export function getCustomCacheDirectory(): string {
return userPreferencesStore.get('customCacheDirectory');
return userPreferencesStore.get("customCacheDirectory");
}

View file

@ -1,11 +1,11 @@
import { WatchStoreType } from '../types';
import { watchStore } from '../stores/watch.store';
import { watchStore } from "../stores/watch.store";
import { WatchStoreType } from "../types";
export function getWatchMappings() {
const mappings = watchStore.get('mappings') ?? [];
const mappings = watchStore.get("mappings") ?? [];
return mappings;
}
export function setWatchMappings(watchMappings: WatchStoreType['mappings']) {
watchStore.set('mappings', watchMappings);
export function setWatchMappings(watchMappings: WatchStoreType["mappings"]) {
watchStore.set("mappings", watchMappings);
}

View file

@ -1,18 +1,18 @@
import Store, { Schema } from 'electron-store';
import { KeysStoreType } from '../types';
import Store, { Schema } from "electron-store";
import { KeysStoreType } from "../types";
const keysStoreSchema: Schema<KeysStoreType> = {
AnonymizeUserID: {
type: 'object',
type: "object",
properties: {
id: {
type: 'string',
type: "string",
},
},
},
};
export const keysStore = new Store({
name: 'keys',
name: "keys",
schema: keysStoreSchema,
});

View file

@ -1,13 +1,13 @@
import Store, { Schema } from 'electron-store';
import { SafeStorageStoreType } from '../types';
import Store, { Schema } from "electron-store";
import { SafeStorageStoreType } from "../types";
const safeStorageSchema: Schema<SafeStorageStoreType> = {
encryptionKey: {
type: 'string',
type: "string",
},
};
export const safeStorageStore = new Store({
name: 'safeStorage',
name: "safeStorage",
schema: safeStorageSchema,
});

View file

@ -1,25 +1,25 @@
import Store, { Schema } from 'electron-store';
import { UploadStoreType } from '../types';
import Store, { Schema } from "electron-store";
import { UploadStoreType } from "../types";
const uploadStoreSchema: Schema<UploadStoreType> = {
filePaths: {
type: 'array',
type: "array",
items: {
type: 'string',
type: "string",
},
},
zipPaths: {
type: 'array',
type: "array",
items: {
type: 'string',
type: "string",
},
},
collectionName: {
type: 'string',
type: "string",
},
};
export const uploadStatusStore = new Store({
name: 'upload-status',
name: "upload-status",
schema: uploadStoreSchema,
});

View file

@ -1,25 +1,25 @@
import Store, { Schema } from 'electron-store';
import { UserPreferencesType } from '../types';
import Store, { Schema } from "electron-store";
import { UserPreferencesType } from "../types";
const userPreferencesSchema: Schema<UserPreferencesType> = {
hideDockIcon: {
type: 'boolean',
type: "boolean",
},
skipAppVersion: {
type: 'string',
type: "string",
},
muteUpdateNotificationVersion: {
type: 'string',
type: "string",
},
optOutOfCrashReports: {
type: 'boolean',
type: "boolean",
},
customCacheDirectory: {
type: 'string',
type: "string",
},
};
export const userPreferencesStore = new Store({
name: 'userPreferences',
name: "userPreferences",
schema: userPreferencesSchema,
});

View file

@ -1,39 +1,39 @@
import Store, { Schema } from 'electron-store';
import { WatchStoreType } from '../types';
import Store, { Schema } from "electron-store";
import { WatchStoreType } from "../types";
const watchStoreSchema: Schema<WatchStoreType> = {
mappings: {
type: 'array',
type: "array",
items: {
type: 'object',
type: "object",
properties: {
rootFolderName: {
type: 'string',
type: "string",
},
uploadStrategy: {
type: 'number',
type: "number",
},
folderPath: {
type: 'string',
type: "string",
},
syncedFiles: {
type: 'array',
type: "array",
items: {
type: 'object',
type: "object",
properties: {
path: {
type: 'string',
type: "string",
},
id: {
type: 'number',
type: "number",
},
},
},
},
ignoredFiles: {
type: 'array',
type: "array",
items: {
type: 'string',
type: "string",
},
},
},
@ -42,6 +42,6 @@ const watchStoreSchema: Schema<WatchStoreType> = {
};
export const watchStore = new Store({
name: 'watch-status',
name: "watch-status",
schema: watchStoreSchema,
});

View file

@ -39,15 +39,15 @@ export interface WatchStoreType {
}
export enum FILE_PATH_TYPE {
FILES = 'files',
ZIPS = 'zips',
FILES = "files",
ZIPS = "zips",
}
export const FILE_PATH_KEYS: {
[k in FILE_PATH_TYPE]: keyof UploadStoreType;
} = {
[FILE_PATH_TYPE.ZIPS]: 'zipPaths',
[FILE_PATH_TYPE.FILES]: 'filePaths',
[FILE_PATH_TYPE.ZIPS]: "zipPaths",
[FILE_PATH_TYPE.FILES]: "filePaths",
};
export interface SafeStorageStoreType {
@ -72,6 +72,6 @@ export interface GetFeatureFlagResponse {
}
export enum Model {
GGML_CLIP = 'ggml-clip',
ONNX_CLIP = 'onnx-clip',
GGML_CLIP = "ggml-clip",
ONNX_CLIP = "onnx-clip",
}

View file

@ -1,5 +1,5 @@
import * as htmlEntities from 'html-entities';
import bpeVocabData from './bpe_simple_vocab_16e6';
import * as htmlEntities from "html-entities";
import bpeVocabData from "./bpe_simple_vocab_16e6";
// import ftfy from "https://deno.land/x/ftfy_pyodide@v0.1.1/mod.js";
function ord(c: string) {
@ -25,9 +25,9 @@ function range(start: number, stop?: number, step: number = 1) {
function bytesToUnicode() {
const bs = [
...range(ord('!'), ord('~') + 1),
...range(ord('¡'), ord('¬') + 1),
...range(ord('®'), ord('ÿ') + 1),
...range(ord("!"), ord("~") + 1),
...range(ord("¡"), ord("¬") + 1),
...range(ord("®"), ord("ÿ") + 1),
];
const cs = bs.slice(0);
let n = 0;
@ -59,7 +59,7 @@ function basicClean(text: string) {
}
function whitespaceClean(text: string) {
return text.replace(/\s+/g, ' ').trim();
return text.replace(/\s+/g, " ").trim();
}
export default class {
@ -77,283 +77,283 @@ export default class {
this.byteDecoder = Object.fromEntries(
Object.entries(this.byteEncoder).map(([k, v]) => [v, Number(k)])
);
let merges = bpeVocabData.text.split('\n');
let merges = bpeVocabData.text.split("\n");
merges = merges.slice(1, 49152 - 256 - 2 + 1);
const mergedMerges = merges.map((merge) => merge.split(' '));
const mergedMerges = merges.map((merge) => merge.split(" "));
// There was a bug related to the ordering of Python's .values() output. I'm lazy do I've just copy-pasted the Python output:
let vocab = [
'!',
"!",
'"',
'#',
'$',
'%',
'&',
"#",
"$",
"%",
"&",
"'",
'(',
')',
'*',
'+',
',',
'-',
'.',
'/',
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
':',
';',
'<',
'=',
'>',
'?',
'@',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
'[',
'\\',
']',
'^',
'_',
'`',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
'{',
'|',
'}',
'~',
'¡',
'¢',
'£',
'¤',
'¥',
'¦',
'§',
'¨',
'©',
'ª',
'«',
'¬',
'®',
'¯',
'°',
'±',
'²',
'³',
'´',
'µ',
'¶',
'·',
'¸',
'¹',
'º',
'»',
'¼',
'½',
'¾',
'¿',
'À',
'Á',
'Â',
'Ã',
'Ä',
'Å',
'Æ',
'Ç',
'È',
'É',
'Ê',
'Ë',
'Ì',
'Í',
'Î',
'Ï',
'Ð',
'Ñ',
'Ò',
'Ó',
'Ô',
'Õ',
'Ö',
'×',
'Ø',
'Ù',
'Ú',
'Û',
'Ü',
'Ý',
'Þ',
'ß',
'à',
'á',
'â',
'ã',
'ä',
'å',
'æ',
'ç',
'è',
'é',
'ê',
'ë',
'ì',
'í',
'î',
'ï',
'ð',
'ñ',
'ò',
'ó',
'ô',
'õ',
'ö',
'÷',
'ø',
'ù',
'ú',
'û',
'ü',
'ý',
'þ',
'ÿ',
'Ā',
'ā',
'Ă',
'ă',
'Ą',
'ą',
'Ć',
'ć',
'Ĉ',
'ĉ',
'Ċ',
'ċ',
'Č',
'č',
'Ď',
'ď',
'Đ',
'đ',
'Ē',
'ē',
'Ĕ',
'ĕ',
'Ė',
'ė',
'Ę',
'ę',
'Ě',
'ě',
'Ĝ',
'ĝ',
'Ğ',
'ğ',
'Ġ',
'ġ',
'Ģ',
'ģ',
'Ĥ',
'ĥ',
'Ħ',
'ħ',
'Ĩ',
'ĩ',
'Ī',
'ī',
'Ĭ',
'ĭ',
'Į',
'į',
'İ',
'ı',
'IJ',
'ij',
'Ĵ',
'ĵ',
'Ķ',
'ķ',
'ĸ',
'Ĺ',
'ĺ',
'Ļ',
'ļ',
'Ľ',
'ľ',
'Ŀ',
'ŀ',
'Ł',
'ł',
'Ń',
"(",
")",
"*",
"+",
",",
"-",
".",
"/",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
":",
";",
"<",
"=",
">",
"?",
"@",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"[",
"\\",
"]",
"^",
"_",
"`",
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z",
"{",
"|",
"}",
"~",
"¡",
"¢",
"£",
"¤",
"¥",
"¦",
"§",
"¨",
"©",
"ª",
"«",
"¬",
"®",
"¯",
"°",
"±",
"²",
"³",
"´",
"µ",
"¶",
"·",
"¸",
"¹",
"º",
"»",
"¼",
"½",
"¾",
"¿",
"À",
"Á",
"Â",
"Ã",
"Ä",
"Å",
"Æ",
"Ç",
"È",
"É",
"Ê",
"Ë",
"Ì",
"Í",
"Î",
"Ï",
"Ð",
"Ñ",
"Ò",
"Ó",
"Ô",
"Õ",
"Ö",
"×",
"Ø",
"Ù",
"Ú",
"Û",
"Ü",
"Ý",
"Þ",
"ß",
"à",
"á",
"â",
"ã",
"ä",
"å",
"æ",
"ç",
"è",
"é",
"ê",
"ë",
"ì",
"í",
"î",
"ï",
"ð",
"ñ",
"ò",
"ó",
"ô",
"õ",
"ö",
"÷",
"ø",
"ù",
"ú",
"û",
"ü",
"ý",
"þ",
"ÿ",
"Ā",
"ā",
"Ă",
"ă",
"Ą",
"ą",
"Ć",
"ć",
"Ĉ",
"ĉ",
"Ċ",
"ċ",
"Č",
"č",
"Ď",
"ď",
"Đ",
"đ",
"Ē",
"ē",
"Ĕ",
"ĕ",
"Ė",
"ė",
"Ę",
"ę",
"Ě",
"ě",
"Ĝ",
"ĝ",
"Ğ",
"ğ",
"Ġ",
"ġ",
"Ģ",
"ģ",
"Ĥ",
"ĥ",
"Ħ",
"ħ",
"Ĩ",
"ĩ",
"Ī",
"ī",
"Ĭ",
"ĭ",
"Į",
"į",
"İ",
"ı",
"IJ",
"ij",
"Ĵ",
"ĵ",
"Ķ",
"ķ",
"ĸ",
"Ĺ",
"ĺ",
"Ļ",
"ļ",
"Ľ",
"ľ",
"Ŀ",
"ŀ",
"Ł",
"ł",
"Ń",
];
vocab = [...vocab, ...vocab.map((v) => v + '</w>')];
vocab = [...vocab, ...vocab.map((v) => v + "</w>")];
for (const merge of mergedMerges) {
vocab.push(merge.join(''));
vocab.push(merge.join(""));
}
vocab.push('<|startoftext|>', '<|endoftext|>');
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])
);
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|>',
'<|endoftext|>': '<|endoftext|>',
"<|startoftext|>": "<|startoftext|>",
"<|endoftext|>": "<|endoftext|>",
};
this.pat =
/<\|startoftext\|>|<\|endoftext\|>|'s|'t|'re|'ve|'m|'ll|'d|[\p{L}]+|[\p{N}]|[^\s\p{L}\p{N}]+/giu;
@ -364,11 +364,11 @@ export default class {
return this.cache[token];
}
let word = [...token.slice(0, -1), token.slice(-1) + '</w>'];
let word = [...token.slice(0, -1), token.slice(-1) + "</w>"];
let pairs = getPairs(word);
if (pairs.length === 0) {
return token + '</w>';
return token + "</w>";
}
// eslint-disable-next-line no-constant-condition
@ -376,7 +376,7 @@ export default class {
let bigram: [string, string] | null = null;
let minRank = Infinity;
for (const p of pairs) {
const r = this.bpeRanks[p.join('·😎·')];
const r = this.bpeRanks[p.join("·😎·")];
if (r === undefined) continue;
if (r < minRank) {
minRank = r;
@ -421,7 +421,7 @@ export default class {
pairs = getPairs(word);
}
}
const joinedWord = word.join(' ');
const joinedWord = word.join(" ");
this.cache[token] = joinedWord;
return joinedWord;
}
@ -432,10 +432,10 @@ export default class {
for (let token of [...text.matchAll(this.pat)].map((m) => m[0])) {
token = [...token]
.map((b) => this.byteEncoder[b.charCodeAt(0) as number])
.join('');
.join("");
bpeTokens.push(
...this.bpe(token)
.split(' ')
.split(" ")
.map((bpeToken: string) => this.encoder[bpeToken])
);
}
@ -455,12 +455,12 @@ export default class {
decode(tokens: any[]) {
let text = tokens
.map((token: string | number) => this.decoder[token])
.join('');
.join("");
text = [...text]
.map((c) => this.byteDecoder[c])
.map((v) => String.fromCharCode(v))
.join('')
.replace(/<\/w>/g, ' ');
.join("")
.replace(/<\/w>/g, " ");
return text;
}
}

View file

@ -1,5 +1,5 @@
import { CustomErrors } from '../../constants/errors';
import { app } from 'electron';
import { app } from "electron";
import { CustomErrors } from "../../constants/errors";
export const isDev = !app.isPackaged;
export const promiseWithTimeout = async <T>(

View file

@ -1,19 +1,19 @@
export function isPlatform(platform: 'mac' | 'windows' | 'linux') {
export function isPlatform(platform: "mac" | "windows" | "linux") {
return getPlatform() === platform;
}
export function getPlatform(): 'mac' | 'windows' | 'linux' {
export function getPlatform(): "mac" | "windows" | "linux" {
switch (process.platform) {
case 'aix':
case 'freebsd':
case 'linux':
case 'openbsd':
case 'android':
return 'linux';
case 'darwin':
case 'sunos':
return 'mac';
case 'win32':
return 'windows';
case "aix":
case "freebsd":
case "linux":
case "openbsd":
case "android":
return "linux";
case "darwin":
case "sunos":
return "mac";
case "win32":
return "windows";
}
}

View file

@ -1,4 +1,4 @@
import { BrowserWindow } from 'electron';
import { BrowserWindow } from "electron";
function lowerCaseHeaders(responseHeaders: Record<string, string[]>) {
const headers: Record<string, string[]> = {};
@ -12,7 +12,7 @@ export function addAllowOriginHeader(mainWindow: BrowserWindow) {
mainWindow.webContents.session.webRequest.onHeadersReceived(
(details, callback) => {
details.responseHeaders = lowerCaseHeaders(details.responseHeaders);
details.responseHeaders['access-control-allow-origin'] = ['*'];
details.responseHeaders["access-control-allow-origin"] = ["*"];
callback({
responseHeaders: details.responseHeaders,
});

View file

@ -1,37 +1,37 @@
import { app, BrowserWindow, nativeImage } from 'electron';
import * as path from 'path';
import { isDev } from './common';
import { isAppQuitting } from '../main';
import { PROD_HOST_URL } from '../config';
import { isPlatform } from './common/platform';
import { getHideDockIconPreference } from '../services/userPreference';
import autoLauncher from '../services/autoLauncher';
import ElectronLog from 'electron-log';
import { logErrorSentry } from '../services/sentry';
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 autoLauncher from "../services/autoLauncher";
import { logErrorSentry } from "../services/sentry";
import { getHideDockIconPreference } from "../services/userPreference";
import { isDev } from "./common";
import { isPlatform } from "./common/platform";
export async function createWindow(): Promise<BrowserWindow> {
const appImgPath = isDev
? 'build/window-icon.png'
: path.join(process.resourcesPath, 'window-icon.png');
? "build/window-icon.png"
: path.join(process.resourcesPath, "window-icon.png");
const appIcon = nativeImage.createFromPath(appImgPath);
// Create the browser window.
const mainWindow = new BrowserWindow({
webPreferences: {
sandbox: false,
preload: path.join(__dirname, '../preload.js'),
preload: path.join(__dirname, "../preload.js"),
contextIsolation: false,
},
icon: appIcon,
show: false, // don't show the main window on load,
});
const wasAutoLaunched = await autoLauncher.wasAutoLaunched();
ElectronLog.log('wasAutoLaunched', wasAutoLaunched);
ElectronLog.log("wasAutoLaunched", wasAutoLaunched);
const splash = new BrowserWindow({
transparent: true,
show: false,
});
if (isPlatform('mac') && wasAutoLaunched) {
if (isPlatform("mac") && wasAutoLaunched) {
app.dock.hide();
}
if (!wasAutoLaunched) {
@ -46,21 +46,21 @@ export async function createWindow(): Promise<BrowserWindow> {
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.webContents.on('did-fail-load', () => {
mainWindow.webContents.on("did-fail-load", () => {
splash.close();
isDev
? mainWindow.loadFile(`../../build/error.html`)
: splash.loadURL(
`file://${path.join(process.resourcesPath, 'error.html')}`
`file://${path.join(process.resourcesPath, "error.html")}`
);
mainWindow.maximize();
mainWindow.show();
});
mainWindow.once('ready-to-show', async () => {
mainWindow.once("ready-to-show", async () => {
try {
splash.destroy();
if (!wasAutoLaunched) {
@ -71,18 +71,18 @@ export async function createWindow(): Promise<BrowserWindow> {
// ignore
}
});
mainWindow.webContents.on('render-process-gone', (event, details) => {
mainWindow.webContents.on("render-process-gone", (event, details) => {
mainWindow.webContents.reload();
logErrorSentry(
Error('render-process-gone'),
'webContents event render-process-gone',
Error("render-process-gone"),
"webContents event render-process-gone",
{ details }
);
ElectronLog.log('webContents event render-process-gone', details);
ElectronLog.log("webContents event render-process-gone", details);
});
mainWindow.webContents.on('unresponsive', () => {
mainWindow.webContents.on("unresponsive", () => {
mainWindow.webContents.forcefullyCrashRenderer();
ElectronLog.log('webContents event unresponsive');
ElectronLog.log("webContents event unresponsive");
});
setTimeout(() => {
@ -96,21 +96,21 @@ export async function createWindow(): Promise<BrowserWindow> {
// ignore
}
}, 2000);
mainWindow.on('close', function (event) {
mainWindow.on("close", function (event) {
if (!isAppQuitting()) {
event.preventDefault();
mainWindow.hide();
}
return false;
});
mainWindow.on('hide', () => {
mainWindow.on("hide", () => {
const shouldHideDockIcon = getHideDockIconPreference();
if (isPlatform('mac') && shouldHideDockIcon) {
if (isPlatform("mac") && shouldHideDockIcon) {
app.dock.hide();
}
});
mainWindow.on('show', () => {
if (isPlatform('mac')) {
mainWindow.on("show", () => {
if (isPlatform("mac")) {
app.dock.show();
}
});

View file

@ -1,12 +1,12 @@
import { CustomErrors } from '../constants/errors';
import { CustomErrors } from "../constants/errors";
export const isExecError = (err: any) => {
return err.message.includes('Command failed:');
return err.message.includes("Command failed:");
};
export const parseExecError = (err: any) => {
const errMessage = err.message;
if (errMessage.includes('Bad CPU type in executable')) {
if (errMessage.includes("Bad CPU type in executable")) {
return CustomErrors.UNSUPPORTED_PLATFORM(
process.platform,
process.arch

View file

@ -1,8 +1,8 @@
import { BrowserWindow } from 'electron';
import { BrowserWindow } from "electron";
export function setupAppEventEmitter(mainWindow: BrowserWindow) {
// fire event when mainWindow is in foreground
mainWindow.on('focus', () => {
mainWindow.webContents.send('app-in-foreground');
mainWindow.on("focus", () => {
mainWindow.webContents.send("app-in-foreground");
});
}

View file

@ -1,87 +1,87 @@
import chokidar from "chokidar";
import {
app,
BrowserWindow,
dialog,
ipcMain,
Tray,
Notification,
safeStorage,
app,
shell,
} from 'electron';
import { createWindow } from './createWindow';
import { getSentryUserID, logErrorSentry } from '../services/sentry';
import chokidar from 'chokidar';
import path from 'path';
import { getDirFilePaths } from '../services/fs';
import {
convertToJPEG,
generateImageThumbnail,
} from '../services/imageProcessor';
Tray,
} from "electron";
import path from "path";
import { updateOptOutOfCrashReports } from "../main";
import {
getAppVersion,
muteUpdateNotification,
skipAppUpdate,
updateAndRestart,
} from '../services/appUpdater';
import { deleteTempFile, runFFmpegCmd } from '../services/ffmpeg';
import { generateTempFilePath } from './temp';
} from "../services/appUpdater";
import {
computeImageEmbedding,
computeTextEmbedding,
} from "../services/clipService";
import { deleteTempFile, runFFmpegCmd } from "../services/ffmpeg";
import { getDirFilePaths } from "../services/fs";
import {
convertToJPEG,
generateImageThumbnail,
} from "../services/imageProcessor";
import { getSentryUserID, logErrorSentry } from "../services/sentry";
import {
getCustomCacheDirectory,
setCustomCacheDirectory,
setOptOutOfCrashReports,
} from '../services/userPreference';
import { updateOptOutOfCrashReports } from '../main';
import {
computeImageEmbedding,
computeTextEmbedding,
} from '../services/clipService';
import { getPlatform } from './common/platform';
} from "../services/userPreference";
import { getPlatform } from "./common/platform";
import { createWindow } from "./createWindow";
import { generateTempFilePath } from "./temp";
export default function setupIpcComs(
tray: Tray,
mainWindow: BrowserWindow,
watcher: chokidar.FSWatcher
): void {
ipcMain.handle('select-dir', async () => {
ipcMain.handle("select-dir", async () => {
const result = await dialog.showOpenDialog({
properties: ['openDirectory'],
properties: ["openDirectory"],
});
if (result.filePaths && result.filePaths.length > 0) {
return result.filePaths[0]?.split(path.sep)?.join(path.posix.sep);
}
});
ipcMain.on('send-notification', (_, args) => {
ipcMain.on("send-notification", (_, args) => {
const notification = {
title: 'ente',
title: "ente",
body: args,
};
new Notification(notification).show();
});
ipcMain.on('reload-window', async () => {
ipcMain.on("reload-window", async () => {
const secondWindow = await createWindow();
mainWindow.destroy();
mainWindow = secondWindow;
});
ipcMain.handle('show-upload-files-dialog', async () => {
ipcMain.handle("show-upload-files-dialog", async () => {
const files = await dialog.showOpenDialog({
properties: ['openFile', 'multiSelections'],
properties: ["openFile", "multiSelections"],
});
return files.filePaths;
});
ipcMain.handle('show-upload-zip-dialog', async () => {
ipcMain.handle("show-upload-zip-dialog", async () => {
const files = await dialog.showOpenDialog({
properties: ['openFile', 'multiSelections'],
filters: [{ name: 'Zip File', extensions: ['zip'] }],
properties: ["openFile", "multiSelections"],
filters: [{ name: "Zip File", extensions: ["zip"] }],
});
return files.filePaths;
});
ipcMain.handle('show-upload-dirs-dialog', async () => {
ipcMain.handle("show-upload-dirs-dialog", async () => {
const dir = await dialog.showOpenDialog({
properties: ['openDirectory', 'multiSelections'],
properties: ["openDirectory", "multiSelections"],
});
let files: string[] = [];
@ -92,27 +92,27 @@ export default function setupIpcComs(
return files;
});
ipcMain.handle('add-watcher', async (_, args: { dir: string }) => {
ipcMain.handle("add-watcher", async (_, args: { dir: string }) => {
watcher.add(args.dir);
});
ipcMain.handle('remove-watcher', async (_, args: { dir: string }) => {
ipcMain.handle("remove-watcher", async (_, args: { dir: string }) => {
watcher.unwatch(args.dir);
});
ipcMain.handle('log-error', (_, err, msg, info?) => {
ipcMain.handle("log-error", (_, err, msg, info?) => {
logErrorSentry(err, msg, info);
});
ipcMain.handle('safeStorage-encrypt', (_, message) => {
ipcMain.handle("safeStorage-encrypt", (_, message) => {
return safeStorage.encryptString(message);
});
ipcMain.handle('safeStorage-decrypt', (_, message) => {
ipcMain.handle("safeStorage-decrypt", (_, message) => {
return safeStorage.decryptString(message);
});
ipcMain.handle('get-path', (_, message) => {
ipcMain.handle("get-path", (_, message) => {
// By default, these paths are at the following locations:
//
// * macOS: `~/Library/Application Support/ente`
@ -124,38 +124,38 @@ export default function setupIpcComs(
return app.getPath(message);
});
ipcMain.handle('convert-to-jpeg', (_, fileData, filename) => {
ipcMain.handle("convert-to-jpeg", (_, fileData, filename) => {
return convertToJPEG(fileData, filename);
});
ipcMain.handle('open-log-dir', () => {
shell.openPath(app.getPath('logs'));
ipcMain.handle("open-log-dir", () => {
shell.openPath(app.getPath("logs"));
});
ipcMain.handle('open-dir', (_, dirPath) => {
ipcMain.handle("open-dir", (_, dirPath) => {
shell.openPath(path.normalize(dirPath));
});
ipcMain.on('update-and-restart', () => {
ipcMain.on("update-and-restart", () => {
updateAndRestart();
});
ipcMain.on('skip-app-update', (_, version) => {
ipcMain.on("skip-app-update", (_, version) => {
skipAppUpdate(version);
});
ipcMain.on('mute-update-notification', (_, version) => {
ipcMain.on("mute-update-notification", (_, version) => {
muteUpdateNotification(version);
});
ipcMain.handle('get-sentry-id', () => {
ipcMain.handle("get-sentry-id", () => {
return getSentryUserID();
});
ipcMain.handle('get-app-version', () => {
ipcMain.handle("get-app-version", () => {
return getAppVersion();
});
ipcMain.handle(
'run-ffmpeg-cmd',
"run-ffmpeg-cmd",
(_, cmd, inputFilePath, outputFileName, dontTimeout) => {
return runFFmpegCmd(
cmd,
@ -165,39 +165,39 @@ export default function setupIpcComs(
);
}
);
ipcMain.handle('get-temp-file-path', (_, formatSuffix) => {
ipcMain.handle("get-temp-file-path", (_, formatSuffix) => {
return generateTempFilePath(formatSuffix);
});
ipcMain.handle('remove-temp-file', (_, tempFilePath: string) => {
ipcMain.handle("remove-temp-file", (_, tempFilePath: string) => {
return deleteTempFile(tempFilePath);
});
ipcMain.handle(
'generate-image-thumbnail',
"generate-image-thumbnail",
(_, fileData, maxDimension, maxSize) => {
return generateImageThumbnail(fileData, maxDimension, maxSize);
}
);
ipcMain.handle('update-opt-out-crash-reports', (_, optOut) => {
ipcMain.handle("update-opt-out-crash-reports", (_, optOut) => {
setOptOutOfCrashReports(optOut);
updateOptOutOfCrashReports(optOut);
});
ipcMain.handle('compute-image-embedding', (_, model, inputFilePath) => {
ipcMain.handle("compute-image-embedding", (_, model, inputFilePath) => {
return computeImageEmbedding(model, inputFilePath);
});
ipcMain.handle('compute-text-embedding', (_, model, text) => {
ipcMain.handle("compute-text-embedding", (_, model, text) => {
return computeTextEmbedding(model, text);
});
ipcMain.handle('get-platform', () => {
ipcMain.handle("get-platform", () => {
return getPlatform();
});
ipcMain.handle('set-custom-cache-directory', (_, directory: string) => {
ipcMain.handle("set-custom-cache-directory", (_, directory: string) => {
setCustomCacheDirectory(directory);
});
ipcMain.handle('get-custom-cache-directory', async () => {
ipcMain.handle("get-custom-cache-directory", async () => {
return getCustomCacheDirectory();
});
}

View file

@ -1,5 +1,5 @@
import log from 'electron-log';
import { LOG_FILENAME, MAX_LOG_SIZE } from '../config';
import log from "electron-log";
import { LOG_FILENAME, MAX_LOG_SIZE } from "../config";
export function setupLogging(isDev?: boolean) {
log.transports.file.fileName = LOG_FILENAME;
@ -8,13 +8,13 @@ export function setupLogging(isDev?: boolean) {
log.transports.console.level = false;
}
log.transports.file.format =
'[{y}-{m}-{d}T{h}:{i}:{s}{z}] [{level}]{scope} {text}';
"[{y}-{m}-{d}T{h}:{i}:{s}{z}] [{level}]{scope} {text}";
}
export function makeID(length: number) {
let result = '';
let result = "";
const characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(
@ -29,10 +29,10 @@ export function convertBytesToHumanReadable(
precision = 2
): string {
if (bytes === 0 || isNaN(bytes)) {
return '0 MB';
return "0 MB";
}
const i = Math.floor(Math.log(bytes) / Math.log(1024));
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
return (bytes / Math.pow(1024, i)).toFixed(precision) + ' ' + sizes[i];
const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
return (bytes / Math.pow(1024, i)).toFixed(precision) + " " + sizes[i];
}

View file

@ -1,19 +1,19 @@
import { PROD_HOST_URL, RENDERER_OUTPUT_DIR } from '../config';
import { nativeImage, Tray, app, BrowserWindow, Menu } from 'electron';
import electronReload from 'electron-reload';
import serveNextAt from 'next-electron-server';
import path from 'path';
import { existsSync } from 'promise-fs';
import { isDev } from './common';
import { buildContextMenu, buildMenuBar } from './menu';
import autoLauncher from '../services/autoLauncher';
import { getHideDockIconPreference } from '../services/userPreference';
import { setupAutoUpdater } from '../services/appUpdater';
import ElectronLog from 'electron-log';
import os from 'os';
import util from 'util';
import { isPlatform } from './common/platform';
const execAsync = util.promisify(require('child_process').exec);
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 { setupAutoUpdater } from "../services/appUpdater";
import autoLauncher from "../services/autoLauncher";
import { getHideDockIconPreference } from "../services/userPreference";
import { isDev } from "./common";
import { isPlatform } from "./common/platform";
import { buildContextMenu, buildMenuBar } from "./menu";
const execAsync = util.promisify(require("child_process").exec);
export async function handleUpdates(mainWindow: BrowserWindow) {
const isInstalledViaBrew = await checkIfInstalledViaBrew();
@ -22,24 +22,24 @@ export async function handleUpdates(mainWindow: BrowserWindow) {
}
}
export function setupTrayItem(mainWindow: BrowserWindow) {
const iconName = isPlatform('mac')
? 'taskbar-icon-Template.png'
: 'taskbar-icon.png';
const iconName = isPlatform("mac")
? "taskbar-icon-Template.png"
: "taskbar-icon.png";
const trayImgPath = path.join(
isDev ? 'build' : process.resourcesPath,
isDev ? "build" : process.resourcesPath,
iconName
);
const trayIcon = nativeImage.createFromPath(trayImgPath);
const tray = new Tray(trayIcon);
tray.setToolTip('ente');
tray.setToolTip("ente");
tray.setContextMenu(buildContextMenu(mainWindow));
return tray;
}
export function handleDownloads(mainWindow: BrowserWindow) {
mainWindow.webContents.session.on('will-download', (_, item) => {
mainWindow.webContents.session.on("will-download", (_, item) => {
item.setSavePath(
getUniqueSavePath(item.getFilename(), app.getPath('downloads'))
getUniqueSavePath(item.getFilename(), app.getPath("downloads"))
);
});
}
@ -58,14 +58,14 @@ export function getUniqueSavePath(filename: string, directory: string): string {
extension,
]
.filter((x) => x) // filters out undefined/null values
.join('');
.join("");
uniqueFileSavePath = path.join(directory, fileNameWithNumberedSuffix);
}
return uniqueFileSavePath;
}
export function setupMacWindowOnDockIconClick() {
app.on('activate', function () {
app.on("activate", function () {
const windows = BrowserWindow.getAllWindows();
// we allow only one window
windows[0].show();
@ -92,13 +92,13 @@ export async function handleDockIconHideOnAutoLaunch() {
const shouldHideDockIcon = getHideDockIconPreference();
const wasAutoLaunched = await autoLauncher.wasAutoLaunched();
if (isPlatform('mac') && shouldHideDockIcon && wasAutoLaunched) {
if (isPlatform("mac") && shouldHideDockIcon && wasAutoLaunched) {
app.dock.hide();
}
}
export function enableSharedArrayBufferSupport() {
app.commandLine.appendSwitch('enable-features', 'SharedArrayBuffer');
app.commandLine.appendSwitch("enable-features", "SharedArrayBuffer");
}
export function logSystemInfo() {
@ -113,24 +113,24 @@ export function logSystemInfo() {
export function handleExternalLinks(mainWindow: BrowserWindow) {
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (!url.startsWith(PROD_HOST_URL)) {
require('electron').shell.openExternal(url);
return { action: 'deny' };
require("electron").shell.openExternal(url);
return { action: "deny" };
} else {
return { action: 'allow' };
return { action: "allow" };
}
});
}
export async function checkIfInstalledViaBrew() {
if (!isPlatform('mac')) {
if (!isPlatform("mac")) {
return false;
}
try {
await execAsync('brew list --cask ente');
ElectronLog.info('ente installed via brew');
await execAsync("brew list --cask ente");
ElectronLog.info("ente installed via brew");
return true;
} catch (e) {
ElectronLog.info('ente not installed via brew');
ElectronLog.info("ente not installed via brew");
return false;
}
}

View file

@ -1,34 +1,34 @@
import {
Menu,
app,
shell,
BrowserWindow,
Menu,
MenuItemConstructorOptions,
} from 'electron';
shell,
} from "electron";
import ElectronLog from "electron-log";
import { setIsAppQuitting } from "../main";
import { forceCheckForUpdateAndNotify } from "../services/appUpdater";
import autoLauncher from "../services/autoLauncher";
import {
getHideDockIconPreference,
setHideDockIconPreference,
} from '../services/userPreference';
import { setIsAppQuitting } from '../main';
import autoLauncher from '../services/autoLauncher';
import { isPlatform } from './common/platform';
import ElectronLog from 'electron-log';
import { forceCheckForUpdateAndNotify } from '../services/appUpdater';
} from "../services/userPreference";
import { isPlatform } from "./common/platform";
export function buildContextMenu(mainWindow: BrowserWindow): Menu {
// eslint-disable-next-line camelcase
const contextMenu = Menu.buildFromTemplate([
{
label: 'Open ente',
label: "Open ente",
click: function () {
mainWindow.maximize();
mainWindow.show();
},
},
{
label: 'Quit ente',
label: "Quit ente",
click: function () {
ElectronLog.log('user quit the app');
ElectronLog.log("user quit the app");
setIsAppQuitting(true);
app.quit();
},
@ -39,43 +39,43 @@ export function buildContextMenu(mainWindow: BrowserWindow): Menu {
export async function buildMenuBar(mainWindow: BrowserWindow): Promise<Menu> {
let isAutoLaunchEnabled = await autoLauncher.isEnabled();
const isMac = isPlatform('mac');
const isMac = isPlatform("mac");
let shouldHideDockIcon = getHideDockIconPreference();
const template: MenuItemConstructorOptions[] = [
{
label: 'ente',
label: "ente",
submenu: [
...((isMac
? [
{
label: 'About ente',
role: 'about',
label: "About ente",
role: "about",
},
]
: []) as MenuItemConstructorOptions[]),
{ type: 'separator' },
{ type: "separator" },
{
label: 'Check for updates...',
label: "Check for updates...",
click: () => {
forceCheckForUpdateAndNotify(mainWindow);
},
},
{
label: 'View Changelog',
label: "View Changelog",
click: () => {
shell.openExternal(
'https://github.com/ente-io/ente/blob/main/desktop/CHANGELOG.md'
"https://github.com/ente-io/ente/blob/main/desktop/CHANGELOG.md"
);
},
},
{ type: 'separator' },
{ type: "separator" },
{
label: 'Preferences',
label: "Preferences",
submenu: [
{
label: 'Open ente on startup',
type: 'checkbox',
label: "Open ente on startup",
type: "checkbox",
checked: isAutoLaunchEnabled,
click: () => {
autoLauncher.toggleAutoLaunch();
@ -83,8 +83,8 @@ export async function buildMenuBar(mainWindow: BrowserWindow): Promise<Menu> {
},
},
{
label: 'Hide dock icon',
type: 'checkbox',
label: "Hide dock icon",
type: "checkbox",
checked: shouldHideDockIcon,
click: () => {
setHideDockIconPreference(!shouldHideDockIcon);
@ -94,121 +94,121 @@ export async function buildMenuBar(mainWindow: BrowserWindow): Promise<Menu> {
],
},
{ type: 'separator' },
{ type: "separator" },
...((isMac
? [
{
label: 'Hide ente',
role: 'hide',
label: "Hide ente",
role: "hide",
},
{
label: 'Hide others',
role: 'hideOthers',
label: "Hide others",
role: "hideOthers",
},
]
: []) as MenuItemConstructorOptions[]),
{ type: 'separator' },
{ type: "separator" },
{
label: 'Quit ente',
role: 'quit',
label: "Quit ente",
role: "quit",
},
],
},
{
label: 'Edit',
label: "Edit",
submenu: [
{ role: 'undo', label: 'Undo' },
{ role: 'redo', label: 'Redo' },
{ type: 'separator' },
{ role: 'cut', label: 'Cut' },
{ role: 'copy', label: 'Copy' },
{ role: 'paste', label: 'Paste' },
{ role: "undo", label: "Undo" },
{ role: "redo", label: "Redo" },
{ type: "separator" },
{ role: "cut", label: "Cut" },
{ role: "copy", label: "Copy" },
{ role: "paste", label: "Paste" },
...((isMac
? [
{
role: 'pasteAndMatchStyle',
label: 'Paste and match style',
role: "pasteAndMatchStyle",
label: "Paste and match style",
},
{ role: 'delete', label: 'Delete' },
{ role: 'selectAll', label: 'Select all' },
{ type: 'separator' },
{ role: "delete", label: "Delete" },
{ role: "selectAll", label: "Select all" },
{ type: "separator" },
{
label: 'Speech',
label: "Speech",
submenu: [
{
role: 'startSpeaking',
label: 'start speaking',
role: "startSpeaking",
label: "start speaking",
},
{
role: 'stopSpeaking',
label: 'stop speaking',
role: "stopSpeaking",
label: "stop speaking",
},
],
},
]
: [
{ type: 'separator' },
{ role: 'selectAll', label: 'Select all' },
{ type: "separator" },
{ role: "selectAll", label: "Select all" },
]) as MenuItemConstructorOptions[]),
],
},
{
label: 'View',
label: "View",
submenu: [
{ role: 'reload', label: 'Reload' },
{ role: 'forceReload', label: 'Force reload' },
{ role: 'toggleDevTools', label: 'Toggle dev tools' },
{ type: 'separator' },
{ role: 'resetZoom', label: 'Reset zoom' },
{ role: 'zoomIn', label: 'Zoom in' },
{ role: 'zoomOut', label: 'Zoom out' },
{ type: 'separator' },
{ role: 'togglefullscreen', label: 'Toggle fullscreen' },
{ role: "reload", label: "Reload" },
{ role: "forceReload", label: "Force reload" },
{ role: "toggleDevTools", label: "Toggle dev tools" },
{ type: "separator" },
{ role: "resetZoom", label: "Reset zoom" },
{ role: "zoomIn", label: "Zoom in" },
{ role: "zoomOut", label: "Zoom out" },
{ type: "separator" },
{ role: "togglefullscreen", label: "Toggle fullscreen" },
],
},
{
label: 'Window',
label: "Window",
submenu: [
{ role: 'close', label: 'Close' },
{ role: 'minimize', label: 'Minimize' },
{ role: "close", label: "Close" },
{ role: "minimize", label: "Minimize" },
...((isMac
? [
{ type: 'separator' },
{ role: 'front', label: 'Bring to front' },
{ type: 'separator' },
{ role: 'window', label: 'ente' },
{ type: "separator" },
{ role: "front", label: "Bring to front" },
{ type: "separator" },
{ role: "window", label: "ente" },
]
: []) as MenuItemConstructorOptions[]),
],
},
{
label: 'Help',
label: "Help",
submenu: [
{
label: 'FAQ',
click: () => shell.openExternal('https://ente.io/faq/'),
label: "FAQ",
click: () => shell.openExternal("https://ente.io/faq/"),
},
{ type: 'separator' },
{ type: "separator" },
{
label: 'Support',
click: () => shell.openExternal('mailto:support@ente.io'),
label: "Support",
click: () => shell.openExternal("mailto:support@ente.io"),
},
{
label: 'Product updates',
click: () => shell.openExternal('https://ente.io/blog/'),
label: "Product updates",
click: () => shell.openExternal("https://ente.io/blog/"),
},
{ type: 'separator' },
{ type: "separator" },
{
label: 'View crash reports',
label: "View crash reports",
click: () => {
shell.openPath(app.getPath('crashDumps'));
shell.openPath(app.getPath("crashDumps"));
},
},
{
label: 'View logs',
label: "View logs",
click: () => {
shell.openPath(app.getPath('logs'));
shell.openPath(app.getPath("logs"));
},
},
],

View file

@ -1,4 +1,4 @@
import { webFrame } from 'electron';
import { webFrame } from "electron";
export const fixHotReloadNext12 = () => {
webFrame.executeJavaScript(`Object.defineProperty(globalThis, 'WebSocket', {

View file

@ -1,6 +1,6 @@
import ElectronLog from 'electron-log';
import { webFrame } from 'electron/renderer';
import { convertBytesToHumanReadable } from './logging';
import ElectronLog from "electron-log";
import { webFrame } from "electron/renderer";
import { convertBytesToHumanReadable } from "./logging";
const LOGGING_INTERVAL_IN_MICROSECONDS = 30 * 1000; // 30 seconds
@ -23,7 +23,7 @@ async function logMainProcessStats() {
process.getHeapStatistics()
);
ElectronLog.log('main process stats', {
ElectronLog.log("main process stats", {
processMemoryInfo,
heapStatistics,
cpuUsage,
@ -69,7 +69,7 @@ async function logSpikeMainMemoryUsage() {
process.getHeapStatistics()
);
ElectronLog.log('reporting main memory usage spike', {
ElectronLog.log("reporting main memory usage spike", {
currentProcessMemoryInfo: normalizedCurrentProcessMemoryInfo,
previousProcessMemoryInfo: normalizedPreviousProcessMemoryInfo,
heapStatistics,
@ -124,7 +124,7 @@ async function logSpikeRendererMemoryUsage() {
process.getHeapStatistics()
);
ElectronLog.log('reporting renderer memory usage spike', {
ElectronLog.log("reporting renderer memory usage spike", {
currentProcessMemoryInfo: normalizedCurrentProcessMemoryInfo,
previousProcessMemoryInfo: normalizedPreviousProcessMemoryInfo,
heapStatistics,
@ -146,7 +146,7 @@ async function logRendererProcessStats() {
const processMemoryInfo = await getNormalizedProcessMemoryInfo(
await process.getProcessMemoryInfo()
);
ElectronLog.log('renderer process stats', {
ElectronLog.log("renderer process stats", {
blinkMemoryInfo,
heapStatistics,
processMemoryInfo,
@ -177,7 +177,7 @@ export async function logRendererProcessMemoryUsage(message: string) {
processMemoryInfo.residentSet ?? 0
);
ElectronLog.log(
'renderer ProcessMemory',
"renderer ProcessMemory",
message,
convertBytesToHumanReadable(processMemory * 1024)
);

View file

@ -1,14 +1,14 @@
import { app } from 'electron';
import path from 'path';
import { existsSync, mkdir } from 'promise-fs';
import { app } from "electron";
import path from "path";
import { existsSync, mkdir } from "promise-fs";
const ENTE_TEMP_DIRECTORY = 'ente';
const ENTE_TEMP_DIRECTORY = "ente";
const CHARACTERS =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
export async function getTempDirPath() {
const tempDirPath = path.join(app.getPath('temp'), ENTE_TEMP_DIRECTORY);
const tempDirPath = path.join(app.getPath("temp"), ENTE_TEMP_DIRECTORY);
if (!existsSync(tempDirPath)) {
await mkdir(tempDirPath);
}
@ -16,7 +16,7 @@ export async function getTempDirPath() {
}
function generateTempName(length: number) {
let result = '';
let result = "";
const charactersLength = CHARACTERS.length;
for (let i = 0; i < length; i++) {
@ -32,7 +32,7 @@ export async function generateTempFilePath(formatSuffix: string) {
do {
const tempDirPath = await getTempDirPath();
const namePrefix = generateTempName(10);
tempFilePath = path.join(tempDirPath, namePrefix + '-' + formatSuffix);
tempFilePath = path.join(tempDirPath, namePrefix + "-" + formatSuffix);
} while (existsSync(tempFilePath));
return tempFilePath;
}

View file

@ -1,4 +1,4 @@
import { WatchMapping } from '../types';
import { WatchMapping } from "../types";
export function isMappingPresent(
watchMappings: WatchMapping[],

View file

@ -183,6 +183,11 @@
resolved "https://registry.yarnpkg.com/@octetstream/promisify/-/promisify-2.0.2.tgz#29ac3bd7aefba646db670227f895d812c1a19615"
integrity sha512-7XHoRB61hxsz8lBQrjC1tq/3OEIgpvGWg6DKAdwi7WRzruwkmsdwmOoUXbU4Dtd4RSOMDwed0SkP3y8UlMt1Bg==
"@pkgr/core@^0.1.0":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
"@sentry/browser@6.7.1":
version "6.7.1"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.7.1.tgz#e01144a08984a486ecc91d7922cc457e9c9bd6b7"
@ -1208,6 +1213,16 @@ delegates@^1.0.0:
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
detect-indent@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-7.0.1.tgz#cbb060a12842b9c4d333f1cac4aa4da1bb66bc25"
integrity sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==
detect-newline@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-4.0.1.tgz#fcefdb5713e1fb8cb2839b8b6ee22e6716ab8f23"
integrity sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==
detect-node@^2.0.4:
version "2.1.0"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
@ -1645,6 +1660,17 @@ fast-glob@^3.2.9:
merge2 "^1.3.0"
micromatch "^4.0.4"
fast-glob@^3.3.0:
version "3.3.2"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
glob-parent "^5.1.2"
merge2 "^1.3.0"
micromatch "^4.0.4"
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@ -1865,6 +1891,11 @@ get-intrinsic@^1.1.1:
has "^1.0.3"
has-symbols "^1.0.3"
get-stdin@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575"
integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==
get-stream@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
@ -1879,6 +1910,11 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"
git-hooks-list@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-3.1.0.tgz#386dc531dcc17474cf094743ff30987a3d3e70fc"
integrity sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==
glob-parent@^5.1.2, glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
@ -1936,6 +1972,17 @@ globby@^11.1.0:
merge2 "^1.4.1"
slash "^3.0.0"
globby@^13.1.2:
version "13.2.2"
resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592"
integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==
dependencies:
dir-glob "^3.0.1"
fast-glob "^3.3.0"
ignore "^5.2.4"
merge2 "^1.4.1"
slash "^4.0.0"
got@^11.8.5:
version "11.8.6"
resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a"
@ -2098,6 +2145,11 @@ ignore@^5.2.0:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
ignore@^5.2.4:
version "5.3.1"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
import-fresh@^3.0.0, import-fresh@^3.2.1:
version "3.3.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
@ -2189,6 +2241,11 @@ is-obj@^2.0.0:
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
is-plain-obj@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@ -2810,6 +2867,19 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prettier-plugin-organize-imports@^3.2:
version "3.2.4"
resolved "https://registry.yarnpkg.com/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz#77967f69d335e9c8e6e5d224074609309c62845e"
integrity sha512-6m8WBhIp0dfwu0SkgfOxJqh+HpdyfqSSLfKKRZSFbDuEQXDDndb8fTpRWkUrX/uBenkex3MgnVk0J3b3Y5byog==
prettier-plugin-packagejson@^2.4:
version "2.4.12"
resolved "https://registry.yarnpkg.com/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.4.12.tgz#eeb917dad83ae42d0caccc9f26d3728b5c4f2434"
integrity sha512-hifuuOgw5rHHTdouw9VrhT8+Nd7UwxtL1qco8dUfd4XUFQL6ia3xyjSxhPQTsGnSYFraTWy5Omb+MZm/OWDTpQ==
dependencies:
sort-package-json "2.8.0"
synckit "0.9.0"
prettier@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a"
@ -3190,6 +3260,11 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
slash@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
slice-ansi@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
@ -3213,6 +3288,24 @@ smart-buffer@^4.0.2:
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
sort-object-keys@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45"
integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==
sort-package-json@2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/sort-package-json/-/sort-package-json-2.8.0.tgz#6a46439ad0fef77f091e678e103f03ecbea575c8"
integrity sha512-PxeNg93bTJWmDGnu0HADDucoxfFiKkIr73Kv85EBThlI1YQPdc0XovBgg2llD0iABZbu2SlKo8ntGmOP9wOj/g==
dependencies:
detect-indent "^7.0.1"
detect-newline "^4.0.0"
get-stdin "^9.0.0"
git-hooks-list "^3.0.0"
globby "^13.1.2"
is-plain-obj "^4.1.0"
sort-object-keys "^1.1.3"
source-map-support@^0.5.19:
version "0.5.21"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
@ -3393,6 +3486,14 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
synckit@0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.0.tgz#5b33b458b3775e4466a5b377fba69c63572ae449"
integrity sha512-7RnqIMq572L8PeEzKeBINYEJDDxpcH8JEgLwUqBd3TkofhFRbkq4QLR0u+36avGAhCRbk2nnmjcW9SE531hPDg==
dependencies:
"@pkgr/core" "^0.1.0"
tslib "^2.6.2"
table@^6.0.9:
version "6.8.0"
resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca"
@ -3503,6 +3604,11 @@ tslib@^2.1.0, tslib@^2.2.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
tslib@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"