diff --git a/src/common/input-override-common.ts b/src/common/input-override-common.ts new file mode 100644 index 0000000000..c329bdfe5a --- /dev/null +++ b/src/common/input-override-common.ts @@ -0,0 +1,9 @@ +import { InputId } from './common-types'; + +export type InputOverrideId = string & { readonly __input_override_id?: never }; + +export const createInputOverrideId = (nodeId: string, inputId: InputId): InputOverrideId => { + if (nodeId.length !== 36) + throw new Error('Expected node id to be a 36 character hexadecimal UUID.'); + return `#${nodeId}:${inputId}`; +}; diff --git a/src/common/input-override.ts b/src/common/input-override.ts index 193711f02d..bcdd82c669 100644 --- a/src/common/input-override.ts +++ b/src/common/input-override.ts @@ -1,19 +1,12 @@ import { readFile } from 'fs/promises'; import { extname } from 'path'; import { EdgeData, InputData, InputId, InputValue, Mutable, NodeData } from './common-types'; +import { InputOverrideId } from './input-override-common'; import { log } from './log'; import { SchemaMap } from './SchemaMap'; import { joinEnglish } from './util'; import type { Edge, Node } from 'reactflow'; -export type InputOverrideId = string & { readonly __input_override_id?: never }; - -export const createInputOverrideId = (nodeId: string, inputId: InputId): InputOverrideId => { - if (nodeId.length !== 36) - throw new Error('Expected node id to be a 36 character hexadecimal UUID.'); - return `#${nodeId}:${inputId}`; -}; - const isValidInputOverrideId = (id: InputOverrideId) => /^#[a-f0-9-]{36}:\d+$/.test(id); export const parseInputOverrideId = (id: InputOverrideId): { nodeId: string; inputId: InputId } => { if (!isValidInputOverrideId(id)) throw new Error(`"${id}" is not a valid input override id.`); diff --git a/src/common/safeIpc.ts b/src/common/safeIpc.ts index fcd6bb0404..eca7c641f5 100644 --- a/src/common/safeIpc.ts +++ b/src/common/safeIpc.ts @@ -8,6 +8,8 @@ import { ipcMain as unsafeIpcMain, ipcRenderer as unsafeIpcRenderer, } from 'electron'; +import { MakeDirectoryOptions } from 'fs'; +import { Mode, ObjectEncodingOptions, OpenMode, PathLike } from 'original-fs'; import { FileOpenResult, FileSaveResult, PythonInfo, Version } from './common-types'; import { ParsedSaveData, SaveData } from './SaveFile'; import { ChainnerSettings } from './settings/settings'; @@ -50,6 +52,45 @@ export interface InvokeChannels { // settings 'get-settings': ChannelInfo; 'set-settings': ChannelInfo; + + // fs + 'fs-read-file': ChannelInfo< + string, + // Mostly copied this from the original fs.readFile, but had to remove some bits due to being unable to import the types + [ + path: PathLike, + options: + | { + encoding: BufferEncoding; + flag?: OpenMode | undefined; + } + | BufferEncoding + ] + >; + 'fs-write-file': ChannelInfo< + void, + // Mostly copied this from the original fs.writeFile, but had to remove some bits due to being unable to import the types + [ + file: PathLike, + data: + | string + | NodeJS.ArrayBufferView + | Iterable + | AsyncIterable, + options?: + | (ObjectEncodingOptions & { + mode?: Mode | undefined; + flag?: OpenMode | undefined; + }) + | BufferEncoding + | null + ] + >; + 'fs-exists': ChannelInfo; + 'fs-mkdir': ChannelInfo; + 'fs-readdir': ChannelInfo; + 'fs-unlink': ChannelInfo; + 'fs-access': ChannelInfo; } export interface SendChannels { diff --git a/src/main/gui/main-window.ts b/src/main/gui/main-window.ts index 6d9ab4d16f..728904b491 100644 --- a/src/main/gui/main-window.ts +++ b/src/main/gui/main-window.ts @@ -1,5 +1,6 @@ import { BrowserWindow, app, dialog, nativeTheme, powerSaveBlocker, shell } from 'electron'; import EventSource from 'eventsource'; +import fs, { constants } from 'fs/promises'; import { t } from 'i18next'; import { BackendEventMap } from '../../common/Backend'; import { Version } from '../../common/common-types'; @@ -195,6 +196,24 @@ const registerEventHandlerPreSetup = ( })().catch(log.error); }); } + + // Handle filesystem + ipcMain.handle('fs-read-file', async (event, path, options) => fs.readFile(path, options)); + ipcMain.handle('fs-write-file', async (event, path, content, options) => + fs.writeFile(path, content, options) + ); + ipcMain.handle('fs-exists', async (event, path) => { + try { + await fs.access(path, constants.F_OK); + return true; + } catch { + return false; + } + }); + ipcMain.handle('fs-mkdir', async (event, path, options) => fs.mkdir(path, options)); + ipcMain.handle('fs-readdir', async (event, path) => fs.readdir(path)); + ipcMain.handle('fs-unlink', async (event, path) => fs.unlink(path)); + ipcMain.handle('fs-access', async (event, path) => fs.access(path)); }; const registerEventHandlerPostSetup = ( diff --git a/src/renderer/components/groups/NcnnFileInputsGroup.tsx b/src/renderer/components/groups/NcnnFileInputsGroup.tsx index 18f2cab9c6..5dd9854200 100644 --- a/src/renderer/components/groups/NcnnFileInputsGroup.tsx +++ b/src/renderer/components/groups/NcnnFileInputsGroup.tsx @@ -1,11 +1,13 @@ import { memo, useEffect } from 'react'; import { log } from '../../../common/log'; -import { checkFileExists, getInputValue } from '../../../common/util'; +import { ipcRenderer } from '../../../common/safeIpc'; +import { getInputValue } from '../../../common/util'; import { SchemaInput } from '../inputs/SchemaInput'; import { GroupProps } from './props'; const ifExists = (file: string, then: () => void) => { - checkFileExists(file) + ipcRenderer + .invoke('fs-exists', file) .then((exists) => { if (exists) { then(); diff --git a/src/renderer/components/settings/components.tsx b/src/renderer/components/settings/components.tsx index 44224c8922..9a38627ea9 100644 --- a/src/renderer/components/settings/components.tsx +++ b/src/renderer/components/settings/components.tsx @@ -9,7 +9,6 @@ import { Select, Switch, } from '@chakra-ui/react'; -import { access, mkdir, readdir, unlink } from 'fs/promises'; import path from 'path'; import { memo, useEffect, useMemo } from 'react'; import { Setting } from '../../../common/common-types'; @@ -130,9 +129,14 @@ const CacheSetting = memo(({ setting, value, setValue }: SettingsProps<'cache'>) onClick={() => { locationPromise .then(async (cacheLocation) => { - const files = await readdir(cacheLocation); + const files = await ipcRenderer.invoke('fs-readdir', cacheLocation); await Promise.all( - files.map((file) => unlink(path.join(cacheLocation, file))) + files.map((file) => + ipcRenderer.invoke( + 'fs-unlink', + path.join(cacheLocation, file) + ) + ) ); }) .catch(log.error); @@ -151,9 +155,11 @@ const CacheSetting = memo(({ setting, value, setValue }: SettingsProps<'cache'>) locationPromise .then(async (cacheLocation) => { try { - await access(cacheLocation); + await ipcRenderer.invoke('fs-access', cacheLocation); } catch (error) { - await mkdir(cacheLocation, { recursive: true }); + await ipcRenderer.invoke('fs-mkdir', cacheLocation, { + recursive: true, + }); } setValue(cacheLocation); }) diff --git a/src/renderer/helpers/copyAndPaste.ts b/src/renderer/helpers/copyAndPaste.ts index 51327c92e5..b42e0407d0 100644 --- a/src/renderer/helpers/copyAndPaste.ts +++ b/src/renderer/helpers/copyAndPaste.ts @@ -1,11 +1,11 @@ import { clipboard } from 'electron'; -import { writeFile } from 'fs/promises'; import os from 'os'; import path from 'path'; import { Edge, Node, Project } from 'reactflow'; import { v4 as uuid4 } from 'uuid'; import { EdgeData, InputId, NodeData, SchemaId } from '../../common/common-types'; import { log } from '../../common/log'; +import { ipcRenderer } from '../../common/safeIpc'; import { createUniqueId, deriveUniqueId } from '../../common/util'; import { NodeProto, copyEdges, copyNodes, setSelected } from './reactFlowUtil'; import { SetState } from './types'; @@ -108,7 +108,8 @@ export const pasteFromClipboard = ( case 'image/png': { const imgData = clipboard.readImage().toPNG(); const imgPath = path.join(os.tmpdir(), `chaiNNer-clipboard-${uuid4()}.png`); - writeFile(imgPath, imgData) + ipcRenderer + .invoke('fs-write-file', imgPath, imgData) .then(() => { log.debug('Clipboard image', imgPath); let positionX = 0; diff --git a/src/renderer/hooks/useInputRefactor.tsx b/src/renderer/hooks/useInputRefactor.tsx index 6d2f371fa3..03b61cae6d 100644 --- a/src/renderer/hooks/useInputRefactor.tsx +++ b/src/renderer/hooks/useInputRefactor.tsx @@ -16,7 +16,7 @@ import { PartialBy, SchemaId, } from '../../common/common-types'; -import { createInputOverrideId } from '../../common/input-override'; +import { createInputOverrideId } from '../../common/input-override-common'; import { createUniqueId } from '../../common/util'; import { BackendContext } from '../contexts/BackendContext'; import { FakeNodeContext } from '../contexts/FakeExampleContext';