mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 19:54:44 -07:00
more strict type-based linting, ensure that mispelled words are added to the context menu for replacement
This commit is contained in:
parent
c728cf0205
commit
b5b7310958
52 changed files with 278 additions and 206 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { defineConfig } from 'eslint/config';
|
||||||
import js from '@eslint/js';
|
import js from '@eslint/js';
|
||||||
import globals from 'globals';
|
import globals from 'globals';
|
||||||
import tseslint from '@typescript-eslint/eslint-plugin';
|
import tseslint from '@typescript-eslint/eslint-plugin';
|
||||||
|
|
@ -11,7 +12,7 @@ import noComments from 'eslint-plugin-no-comments';
|
||||||
// @ts-ignore - No types available
|
// @ts-ignore - No types available
|
||||||
import promise from 'eslint-plugin-promise';
|
import promise from 'eslint-plugin-promise';
|
||||||
|
|
||||||
const config = [
|
const config = defineConfig([
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
{
|
{
|
||||||
ignores: [
|
ignores: [
|
||||||
|
|
@ -50,8 +51,8 @@ const config = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
'@typescript-eslint': tseslint,
|
'@typescript-eslint': tseslint as any,
|
||||||
'react-hooks': reactHooks,
|
'react-hooks': reactHooks as any,
|
||||||
react,
|
react,
|
||||||
sonarjs,
|
sonarjs,
|
||||||
'no-comments': noComments,
|
'no-comments': noComments,
|
||||||
|
|
@ -69,6 +70,7 @@ const config = [
|
||||||
...importPlugin.configs.recommended.rules,
|
...importPlugin.configs.recommended.rules,
|
||||||
...importPlugin.configs.typescript.rules,
|
...importPlugin.configs.typescript.rules,
|
||||||
...tseslint.configs.recommended.rules,
|
...tseslint.configs.recommended.rules,
|
||||||
|
...tseslint.configs['recommended-type-checked'].rules,
|
||||||
...tseslint.configs.stylistic.rules,
|
...tseslint.configs.stylistic.rules,
|
||||||
|
|
||||||
'@typescript-eslint/no-unused-vars': [
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
|
@ -82,6 +84,12 @@ const config = [
|
||||||
'no-unused-vars': 'off',
|
'no-unused-vars': 'off',
|
||||||
'@typescript-eslint/no-explicit-any': 'warn',
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
|
||||||
|
'@typescript-eslint/no-unsafe-argument': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-call': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-return': 'off',
|
||||||
|
|
||||||
'react/function-component-definition': [
|
'react/function-component-definition': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
|
|
@ -226,6 +234,6 @@ const config = [
|
||||||
'import/no-default-export': 'off',
|
'import/no-default-export': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "gerbil",
|
"name": "gerbil",
|
||||||
"productName": "Gerbil",
|
"productName": "Gerbil",
|
||||||
"version": "1.18.1",
|
"version": "1.18.2",
|
||||||
"description": "Run Large Language Models locally",
|
"description": "Run Large Language Models locally",
|
||||||
"main": "out/main/index.js",
|
"main": "out/main/index.js",
|
||||||
"homepage": "./",
|
"homepage": "./",
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ const ErrorFallback = ({
|
||||||
leftSection={
|
leftSection={
|
||||||
<FolderOpen style={{ width: rem(16), height: rem(16) }} />
|
<FolderOpen style={{ width: rem(16), height: rem(16) }} />
|
||||||
}
|
}
|
||||||
onClick={() => window.electronAPI.app.showLogsFolder()}
|
onClick={() => void window.electronAPI.app.showLogsFolder()}
|
||||||
>
|
>
|
||||||
Show Logs Folder
|
Show Logs Folder
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -51,7 +51,7 @@ const ErrorFallback = ({
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.electronAPI?.app?.closeWindow();
|
void window.electronAPI?.app?.closeWindow();
|
||||||
}}
|
}}
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
color="gray"
|
color="gray"
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,11 @@ export const PerformanceBadge = ({
|
||||||
if (iconOnly) {
|
if (iconOnly) {
|
||||||
return (
|
return (
|
||||||
<Tooltip label={tooltipLabel} position="top">
|
<Tooltip label={tooltipLabel} position="top">
|
||||||
<ActionIcon size="sm" variant="subtle" onClick={handlePerformanceClick}>
|
<ActionIcon
|
||||||
|
size="sm"
|
||||||
|
variant="subtle"
|
||||||
|
onClick={() => void handlePerformanceClick()}
|
||||||
|
>
|
||||||
<Activity size="1.125rem" />
|
<Activity size="1.125rem" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
@ -49,7 +53,7 @@ export const PerformanceBadge = ({
|
||||||
fontSize: '0.7em',
|
fontSize: '0.7em',
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
}}
|
}}
|
||||||
onClick={handlePerformanceClick}
|
onClick={() => void handlePerformanceClick()}
|
||||||
>
|
>
|
||||||
{label}: {value}
|
{label}: {value}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ export const TitleBar = ({
|
||||||
setIsMaximized(currentMaximizedState);
|
setIsMaximized(currentMaximizedState);
|
||||||
};
|
};
|
||||||
|
|
||||||
initializeState();
|
void initializeState();
|
||||||
|
|
||||||
const cleanup = window.electronAPI.app.onWindowStateToggle(() =>
|
const cleanup = window.electronAPI.app.onWindowStateToggle(() =>
|
||||||
setIsMaximized((prev) => !prev)
|
setIsMaximized((prev) => !prev)
|
||||||
|
|
@ -112,7 +112,7 @@ export const TitleBar = ({
|
||||||
w={24}
|
w={24}
|
||||||
h={24}
|
h={24}
|
||||||
style={getLogoStyles()}
|
style={getLogoStyles()}
|
||||||
onClick={handleLogoClick}
|
onClick={() => void handleLogoClick()}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
|
|
@ -186,19 +186,19 @@ export const TitleBar = ({
|
||||||
{[
|
{[
|
||||||
{
|
{
|
||||||
icon: <Minus size="1rem" />,
|
icon: <Minus size="1rem" />,
|
||||||
onClick: () => window.electronAPI.app.minimizeWindow(),
|
onClick: () => void window.electronAPI.app.minimizeWindow(),
|
||||||
color: undefined,
|
color: undefined,
|
||||||
label: 'Minimize window',
|
label: 'Minimize window',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: isMaximized ? <Copy size="1rem" /> : <Square size="1rem" />,
|
icon: isMaximized ? <Copy size="1rem" /> : <Square size="1rem" />,
|
||||||
onClick: () => window.electronAPI.app.maximizeWindow(),
|
onClick: () => void window.electronAPI.app.maximizeWindow(),
|
||||||
color: undefined,
|
color: undefined,
|
||||||
label: isMaximized ? 'Restore window' : 'Maximize window',
|
label: isMaximized ? 'Restore window' : 'Maximize window',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <X size="1.25rem" />,
|
icon: <X size="1.25rem" />,
|
||||||
onClick: () => window.electronAPI.app.closeWindow(),
|
onClick: () => void window.electronAPI.app.closeWindow(),
|
||||||
color: 'red' as const,
|
color: 'red' as const,
|
||||||
label: 'Close window',
|
label: 'Close window',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ export const UpdateAvailableModal = ({
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={handleUpdate}
|
onClick={() => void handleUpdate()}
|
||||||
loading={isDownloading || isUpdating}
|
loading={isDownloading || isUpdating}
|
||||||
disabled={isDownloading || isUpdating}
|
disabled={isDownloading || isUpdating}
|
||||||
leftSection={
|
leftSection={
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export const UpdateButton = () => {
|
||||||
color = 'blue';
|
color = 'blue';
|
||||||
label = 'Download and install update';
|
label = 'Download and install update';
|
||||||
onClick = () => {
|
onClick = () => {
|
||||||
downloadUpdate();
|
void downloadUpdate();
|
||||||
setShowDownload(false);
|
setShowDownload(false);
|
||||||
};
|
};
|
||||||
icon = <Download size="1.25rem" />;
|
icon = <Download size="1.25rem" />;
|
||||||
|
|
@ -47,7 +47,7 @@ export const UpdateButton = () => {
|
||||||
: 'Update available - Click to view';
|
: 'Update available - Click to view';
|
||||||
onClick = () => {
|
onClick = () => {
|
||||||
if (releaseUrl) {
|
if (releaseUrl) {
|
||||||
window.electronAPI.app.openExternal(releaseUrl);
|
void window.electronAPI.app.openExternal(releaseUrl);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +56,7 @@ export const UpdateButton = () => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (canAutoUpdate && !isDownloading && !isUpdateDownloaded) {
|
if (canAutoUpdate && !isDownloading && !isUpdateDownloaded) {
|
||||||
setShowDownload(true);
|
setShowDownload(true);
|
||||||
downloadUpdate();
|
void downloadUpdate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ export const App = () => {
|
||||||
? displayModel.split('/').pop() || displayModel
|
? displayModel.split('/').pop() || displayModel
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
window.electronAPI.app.updateTrayState({
|
void window.electronAPI.app.updateTrayState({
|
||||||
screen: currentScreen,
|
screen: currentScreen,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
config: config || null,
|
config: config || null,
|
||||||
|
|
@ -158,7 +158,7 @@ export const App = () => {
|
||||||
setHasInitialized(true);
|
setHasInitialized(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
checkInstallation();
|
void checkInstallation();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -169,12 +169,12 @@ export const App = () => {
|
||||||
await window.electronAPI.kobold.getCurrentBackend();
|
await window.electronAPI.kobold.getCurrentBackend();
|
||||||
if (currentBackend) {
|
if (currentBackend) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
checkForUpdates();
|
void checkForUpdates();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
const interval = setInterval(
|
const interval = setInterval(
|
||||||
() => {
|
() => {
|
||||||
checkForUpdates();
|
void checkForUpdates();
|
||||||
},
|
},
|
||||||
6 * 60 * 60 * 1000
|
6 * 60 * 60 * 1000
|
||||||
);
|
);
|
||||||
|
|
@ -183,7 +183,7 @@ export const App = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
runUpdateCheck();
|
void runUpdateCheck();
|
||||||
}, [loadingRemote, hasInitialized, checkForUpdates]);
|
}, [loadingRemote, hasInitialized, checkForUpdates]);
|
||||||
|
|
||||||
const handleBinaryUpdate = async (download: DownloadItem) => {
|
const handleBinaryUpdate = async (download: DownloadItem) => {
|
||||||
|
|
@ -240,7 +240,7 @@ export const App = () => {
|
||||||
<TitleBar
|
<TitleBar
|
||||||
currentScreen={currentScreen || 'launch'}
|
currentScreen={currentScreen || 'launch'}
|
||||||
currentTab={activeInterfaceTab}
|
currentTab={activeInterfaceTab}
|
||||||
onEject={handleEject}
|
onEject={() => void handleEject()}
|
||||||
onTabChange={setActiveInterfaceTab}
|
onTabChange={setActiveInterfaceTab}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -274,8 +274,8 @@ export const App = () => {
|
||||||
hasInitialized={hasInitialized}
|
hasInitialized={hasInitialized}
|
||||||
activeInterfaceTab={activeInterfaceTab}
|
activeInterfaceTab={activeInterfaceTab}
|
||||||
isServerReady={isServerReady}
|
isServerReady={isServerReady}
|
||||||
onWelcomeComplete={handleWelcomeComplete}
|
onWelcomeComplete={() => void handleWelcomeComplete()}
|
||||||
onDownloadComplete={handleDownloadComplete}
|
onDownloadComplete={() => void handleDownloadComplete()}
|
||||||
onLaunch={handleLaunch}
|
onLaunch={handleLaunch}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ export const ImportBackendLink = ({
|
||||||
type="button"
|
type="button"
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
onClick={handleImport}
|
onClick={() => void handleImport()}
|
||||||
>
|
>
|
||||||
{importing ? 'Importing...' : 'Select a local file'}
|
{importing ? 'Importing...' : 'Select a local file'}
|
||||||
</Anchor>
|
</Anchor>
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ export const InfoCard = ({ title, items, loading = false }: InfoCardProps) => {
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={copyInfo}
|
onClick={() => void copyInfo()}
|
||||||
aria-label={`Copy ${title}`}
|
aria-label={`Copy ${title}`}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ export const NotepadEditor = ({ tab }: NotepadEditorProps) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
saveTabContent(tab.title, newContent);
|
void saveTabContent(tab.title, newContent);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
setSaveTimeout(timeout);
|
setSaveTimeout(timeout);
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ export const NotepadTabs = ({
|
||||||
|
|
||||||
const handleTabRename = (title: string, newTitle: string) => {
|
const handleTabRename = (title: string, newTitle: string) => {
|
||||||
updateTab(title, { title: newTitle });
|
updateTab(title, { title: newTitle });
|
||||||
window.electronAPI.notepad.renameTab(title, newTitle);
|
void window.electronAPI.notepad.renameTab(title, newTitle);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTabBarContextMenu = (e: MouseEvent) => {
|
const handleTabBarContextMenu = (e: MouseEvent) => {
|
||||||
|
|
@ -118,7 +118,7 @@ export const NotepadTabs = ({
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={onCreateNewTab}
|
onClick={() => void onCreateNewTab()}
|
||||||
style={{
|
style={{
|
||||||
margin: '0.25rem',
|
margin: '0.25rem',
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
||||||
}
|
}
|
||||||
onDownload={(e) => {
|
onDownload={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleDownload(download);
|
void handleDownload(download);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ export const ConfigFileManager = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value) {
|
if (value) {
|
||||||
onFileSelection(value);
|
void onFileSelection(value);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
data={selectData}
|
data={selectData}
|
||||||
|
|
@ -124,7 +124,7 @@ export const ConfigFileManager = ({
|
||||||
variant="outline"
|
variant="outline"
|
||||||
leftSection={saveSuccess ? <Check size={14} /> : <Save size={14} />}
|
leftSection={saveSuccess ? <Check size={14} /> : <Save size={14} />}
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={handleSaveClick}
|
onClick={() => void handleSaveClick()}
|
||||||
color={saveSuccess ? 'green' : undefined}
|
color={saveSuccess ? 'green' : undefined}
|
||||||
style={{ width: '6rem' }}
|
style={{ width: '6rem' }}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export const CreateConfigModal = ({
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={!trimmedConfigName || !!configNameExists}
|
disabled={!trimmedConfigName || !!configNameExists}
|
||||||
onClick={handleSubmit}
|
onClick={() => void handleSubmit()}
|
||||||
>
|
>
|
||||||
Create
|
Create
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ export const DeleteConfigModal = ({
|
||||||
<Button variant="subtle" onClick={onClose}>
|
<Button variant="subtle" onClick={onClose}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="red" onClick={onConfirm}>
|
<Button color="red" onClick={() => void onConfirm()}>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,12 @@ export const AccelerationSelector = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!hasInitialized.current) {
|
if (!hasInitialized.current) {
|
||||||
loadAccelerations();
|
void loadAccelerations();
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleanup = window.electronAPI.kobold.onVersionsUpdated(() => {
|
const cleanup = window.electronAPI.kobold.onVersionsUpdated(() => {
|
||||||
hasInitialized.current = false;
|
hasInitialized.current = false;
|
||||||
loadAccelerations();
|
void loadAccelerations();
|
||||||
});
|
});
|
||||||
|
|
||||||
return cleanup;
|
return cleanup;
|
||||||
|
|
@ -133,7 +133,7 @@ export const AccelerationSelector = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
calculateLayers();
|
void calculateLayers();
|
||||||
}, [
|
}, [
|
||||||
autoGpuLayers,
|
autoGpuLayers,
|
||||||
model,
|
model,
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export const GeneralTab = ({ configLoaded = true }: GeneralTabProps) => {
|
||||||
placeholder="Select a .gguf model file or enter a direct URL to file"
|
placeholder="Select a .gguf model file or enter a direct URL to file"
|
||||||
tooltip="Select a GGUF text generation model file for chat and completion tasks."
|
tooltip="Select a GGUF text generation model file for chat and completion tasks."
|
||||||
onChange={setModel}
|
onChange={setModel}
|
||||||
onSelectFile={() => selectFile('model', 'Select Text Model')}
|
onSelectFile={() => void selectFile('model', 'Select Text Model')}
|
||||||
searchParams={{
|
searchParams={{
|
||||||
pipelineTag: 'text-generation',
|
pipelineTag: 'text-generation',
|
||||||
filter: 'gguf',
|
filter: 'gguf',
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ export const ModelCard = ({ modelId }: ModelCardProps) => {
|
||||||
return (
|
return (
|
||||||
<Accordion>
|
<Accordion>
|
||||||
<Accordion.Item value="readme">
|
<Accordion.Item value="readme">
|
||||||
<Accordion.Control onClick={loadReadme}>Model Card</Accordion.Control>
|
<Accordion.Control onClick={() => void loadReadme()}>
|
||||||
|
Model Card
|
||||||
|
</Accordion.Control>
|
||||||
<Accordion.Panel>
|
<Accordion.Panel>
|
||||||
{loading && (
|
{loading && (
|
||||||
<Group justify="center" py="md">
|
<Group justify="center" py="md">
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export const ModelsTable = ({
|
||||||
key={model.id}
|
key={model.id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (model.gated) {
|
if (model.gated) {
|
||||||
window.electronAPI.app.openExternal(
|
void window.electronAPI.app.openExternal(
|
||||||
`${HUGGINGFACE_BASE_URL}/${model.id}`
|
`${HUGGINGFACE_BASE_URL}/${model.id}`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ export const HuggingFaceSearchModal = ({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (opened && !prevOpenedRef.current) {
|
if (opened && !prevOpenedRef.current) {
|
||||||
searchModels();
|
void searchModels();
|
||||||
}
|
}
|
||||||
if (!opened && prevOpenedRef.current) {
|
if (!opened && prevOpenedRef.current) {
|
||||||
reset();
|
reset();
|
||||||
|
|
@ -81,7 +81,7 @@ export const HuggingFaceSearchModal = ({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (opened && debouncedQuery !== undefined) {
|
if (opened && debouncedQuery !== undefined) {
|
||||||
searchModels(debouncedQuery);
|
void searchModels(debouncedQuery);
|
||||||
}
|
}
|
||||||
}, [debouncedQuery, opened, searchModels]);
|
}, [debouncedQuery, opened, searchModels]);
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ export const HuggingFaceSearchModal = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModelSelect = (model: HuggingFaceModelInfo) => {
|
const handleModelSelect = (model: HuggingFaceModelInfo) => {
|
||||||
loadModelFiles(model);
|
void loadModelFiles(model);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFileSelect = (file: HuggingFaceFileInfo) => {
|
const handleFileSelect = (file: HuggingFaceFileInfo) => {
|
||||||
|
|
@ -103,12 +103,12 @@ export const HuggingFaceSearchModal = ({
|
||||||
|
|
||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
reset();
|
reset();
|
||||||
searchModels(searchQuery);
|
void searchModels(searchQuery);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOpenExternal = () => {
|
const handleOpenExternal = () => {
|
||||||
if (selectedModel) {
|
if (selectedModel) {
|
||||||
window.electronAPI.app.openExternal(
|
void window.electronAPI.app.openExternal(
|
||||||
`${HUGGINGFACE_BASE_URL}/${selectedModel.id}`
|
`${HUGGINGFACE_BASE_URL}/${selectedModel.id}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -196,7 +196,7 @@ export const HuggingFaceSearchModal = ({
|
||||||
const isNearBottom =
|
const isNearBottom =
|
||||||
target.scrollHeight - y <= target.clientHeight + 100;
|
target.scrollHeight - y <= target.clientHeight + 100;
|
||||||
if (isNearBottom && hasMore && !loading && !selectedModel) {
|
if (isNearBottom && hasMore && !loading && !selectedModel) {
|
||||||
loadMoreModels();
|
void loadMoreModels();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
viewportRef={scrollAreaRef}
|
viewportRef={scrollAreaRef}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ export const ImageGenerationTab = () => {
|
||||||
placeholder="Select a model file or enter a direct URL"
|
placeholder="Select a model file or enter a direct URL"
|
||||||
tooltip="The primary image generation model. This is the main model that will generate images."
|
tooltip="The primary image generation model. This is the main model that will generate images."
|
||||||
onChange={setSdmodel}
|
onChange={setSdmodel}
|
||||||
onSelectFile={() => selectFile('sdmodel', 'Select Image Model')}
|
onSelectFile={() => void selectFile('sdmodel', 'Select Image Model')}
|
||||||
searchParams={{
|
searchParams={{
|
||||||
pipelineTag: 'text-to-image',
|
pipelineTag: 'text-to-image',
|
||||||
filter: 'gguf',
|
filter: 'gguf',
|
||||||
|
|
@ -84,7 +84,7 @@ export const ImageGenerationTab = () => {
|
||||||
placeholder="Select a T5-XXL encoder file or enter a direct URL"
|
placeholder="Select a T5-XXL encoder file or enter a direct URL"
|
||||||
tooltip="T5-XXL text encoder model for advanced text understanding."
|
tooltip="T5-XXL text encoder model for advanced text understanding."
|
||||||
onChange={setSdt5xxl}
|
onChange={setSdt5xxl}
|
||||||
onSelectFile={() => selectFile('sdt5xxl', 'Select T5XXL Model')}
|
onSelectFile={() => void selectFile('sdt5xxl', 'Select T5XXL Model')}
|
||||||
searchParams={{
|
searchParams={{
|
||||||
search: 't5xxl',
|
search: 't5xxl',
|
||||||
filter: 'safetensors',
|
filter: 'safetensors',
|
||||||
|
|
@ -99,7 +99,7 @@ export const ImageGenerationTab = () => {
|
||||||
placeholder="Select a Clip-L file or enter a direct URL"
|
placeholder="Select a Clip-L file or enter a direct URL"
|
||||||
tooltip="CLIP-L text encoder model for text-image understanding."
|
tooltip="CLIP-L text encoder model for text-image understanding."
|
||||||
onChange={setSdclipl}
|
onChange={setSdclipl}
|
||||||
onSelectFile={() => selectFile('sdclipl', 'Select CLIP-L Model')}
|
onSelectFile={() => void selectFile('sdclipl', 'Select CLIP-L Model')}
|
||||||
searchParams={{
|
searchParams={{
|
||||||
search: 'clip',
|
search: 'clip',
|
||||||
filter: 'safetensors',
|
filter: 'safetensors',
|
||||||
|
|
@ -114,7 +114,7 @@ export const ImageGenerationTab = () => {
|
||||||
placeholder="Select a Clip-G file or enter a direct URL"
|
placeholder="Select a Clip-G file or enter a direct URL"
|
||||||
tooltip="CLIP-G text encoder model for enhanced text-image understanding, or mmproj files for vision-language models."
|
tooltip="CLIP-G text encoder model for enhanced text-image understanding, or mmproj files for vision-language models."
|
||||||
onChange={setSdclipg}
|
onChange={setSdclipg}
|
||||||
onSelectFile={() => selectFile('sdclipg', 'Select CLIP-G Model')}
|
onSelectFile={() => void selectFile('sdclipg', 'Select CLIP-G Model')}
|
||||||
searchParams={{
|
searchParams={{
|
||||||
search: 'clip',
|
search: 'clip',
|
||||||
filter: 'gguf',
|
filter: 'gguf',
|
||||||
|
|
@ -130,7 +130,7 @@ export const ImageGenerationTab = () => {
|
||||||
tooltip="PhotoMaker is a model that allows face cloning. Select a .safetensors PhotoMaker file to be loaded (SDXL only)."
|
tooltip="PhotoMaker is a model that allows face cloning. Select a .safetensors PhotoMaker file to be loaded (SDXL only)."
|
||||||
onChange={setSdphotomaker}
|
onChange={setSdphotomaker}
|
||||||
onSelectFile={() =>
|
onSelectFile={() =>
|
||||||
selectFile('sdphotomaker', 'Select PhotoMaker Model')
|
void selectFile('sdphotomaker', 'Select PhotoMaker Model')
|
||||||
}
|
}
|
||||||
searchParams={{
|
searchParams={{
|
||||||
search: 'photomaker',
|
search: 'photomaker',
|
||||||
|
|
@ -146,7 +146,7 @@ export const ImageGenerationTab = () => {
|
||||||
placeholder="Select a VAE file or enter a direct URL"
|
placeholder="Select a VAE file or enter a direct URL"
|
||||||
tooltip="Variational Autoencoder model for improved image quality."
|
tooltip="Variational Autoencoder model for improved image quality."
|
||||||
onChange={setSdvae}
|
onChange={setSdvae}
|
||||||
onSelectFile={() => selectFile('sdvae', 'Select VAE Model')}
|
onSelectFile={() => void selectFile('sdvae', 'Select VAE Model')}
|
||||||
searchParams={{
|
searchParams={{
|
||||||
search: 'vae',
|
search: 'vae',
|
||||||
filter: 'safetensors',
|
filter: 'safetensors',
|
||||||
|
|
@ -161,7 +161,7 @@ export const ImageGenerationTab = () => {
|
||||||
placeholder="Select a LoRa file or enter a direct URL"
|
placeholder="Select a LoRa file or enter a direct URL"
|
||||||
tooltip="LoRa (Low-Rank Adaptation) file for customizing image generation. Select a .safetensors or .gguf LoRa file to be loaded. Should be unquantized."
|
tooltip="LoRa (Low-Rank Adaptation) file for customizing image generation. Select a .safetensors or .gguf LoRa file to be loaded. Should be unquantized."
|
||||||
onChange={setSdlora}
|
onChange={setSdlora}
|
||||||
onSelectFile={() => selectFile('sdlora', 'Select LoRA Model')}
|
onSelectFile={() => void selectFile('sdlora', 'Select LoRA Model')}
|
||||||
searchParams={{
|
searchParams={{
|
||||||
search: 'lora',
|
search: 'lora',
|
||||||
filter: 'safetensors',
|
filter: 'safetensors',
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ export const ModelFileField = ({
|
||||||
const combobox = useCombobox();
|
const combobox = useCombobox();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
const models =
|
const models =
|
||||||
await window.electronAPI.kobold.getLocalModels(paramType);
|
await window.electronAPI.kobold.getLocalModels(paramType);
|
||||||
|
|
@ -168,7 +168,7 @@ export const ModelFileField = ({
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
onClick={handleAnalyzeModel}
|
onClick={() => void handleAnalyzeModel()}
|
||||||
variant="light"
|
variant="light"
|
||||||
color="blue"
|
color="blue"
|
||||||
size="lg"
|
size="lg"
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,7 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
|
||||||
}, [loadConfigFiles]);
|
}, [loadConfigFiles]);
|
||||||
|
|
||||||
const handleLaunchClick = useCallback(() => {
|
const handleLaunchClick = useCallback(() => {
|
||||||
handleLaunch({
|
void handleLaunch({
|
||||||
autoGpuLayers,
|
autoGpuLayers,
|
||||||
gpuLayers,
|
gpuLayers,
|
||||||
contextSize,
|
contextSize,
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ export const AboutTab = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadVersionInfo();
|
void loadVersionInfo();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!versionInfo) {
|
if (!versionInfo) {
|
||||||
|
|
@ -60,7 +60,7 @@ export const AboutTab = () => {
|
||||||
alt={PRODUCT_NAME}
|
alt={PRODUCT_NAME}
|
||||||
w={64}
|
w={64}
|
||||||
h={64}
|
h={64}
|
||||||
onClick={handleLogoClick}
|
onClick={() => void handleLogoClick()}
|
||||||
style={{
|
style={{
|
||||||
minWidth: 64,
|
minWidth: 64,
|
||||||
minHeight: 64,
|
minHeight: 64,
|
||||||
|
|
@ -93,7 +93,7 @@ export const AboutTab = () => {
|
||||||
leftSection={
|
leftSection={
|
||||||
<button.icon style={{ width: rem(16), height: rem(16) }} />
|
<button.icon style={{ width: rem(16), height: rem(16) }} />
|
||||||
}
|
}
|
||||||
onClick={button.onClick}
|
onClick={() => void button.onClick()}
|
||||||
style={
|
style={
|
||||||
button.label === 'GitHub'
|
button.label === 'GitHub'
|
||||||
? { textDecoration: 'none' }
|
? { textDecoration: 'none' }
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ export const BackendsTab = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
init();
|
void init();
|
||||||
}, [getLatestReleaseWithDownloadStatus, refreshDownloads]);
|
}, [getLatestReleaseWithDownloadStatus, refreshDownloads]);
|
||||||
|
|
||||||
const loadInstalledBackends = useCallback(async () => {
|
const loadInstalledBackends = useCallback(async () => {
|
||||||
|
|
@ -230,7 +230,7 @@ export const BackendsTab = () => {
|
||||||
setCurrentBackend(targetBackend);
|
setCurrentBackend(targetBackend);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.electronAPI.kobold.setCurrentBackend(backend.installedPath);
|
void window.electronAPI.kobold.setCurrentBackend(backend.installedPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loadingInstalled) {
|
if (loadingInstalled) {
|
||||||
|
|
@ -301,19 +301,19 @@ export const BackendsTab = () => {
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
onDownload={(e) => {
|
onDownload={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleDownload(backend);
|
void handleDownload(backend);
|
||||||
}}
|
}}
|
||||||
onUpdate={(e) => {
|
onUpdate={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleUpdate(backend);
|
void handleUpdate(backend);
|
||||||
}}
|
}}
|
||||||
onRedownload={(e) => {
|
onRedownload={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleRedownload(backend);
|
void handleRedownload(backend);
|
||||||
}}
|
}}
|
||||||
onDelete={(e) => {
|
onDelete={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleDelete(backend);
|
void handleDelete(backend);
|
||||||
}}
|
}}
|
||||||
onMakeCurrent={() => makeCurrent(backend)}
|
onMakeCurrent={() => makeCurrent(backend)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -212,10 +212,10 @@ export const FrontendInterfaceSelector = ({
|
||||||
const initialize = async () => {
|
const initialize = async () => {
|
||||||
await checkAllFrontendRequirements();
|
await checkAllFrontendRequirements();
|
||||||
};
|
};
|
||||||
initialize();
|
void initialize();
|
||||||
|
|
||||||
const handleFocus = () => {
|
const handleFocus = () => {
|
||||||
checkAllFrontendRequirements();
|
void checkAllFrontendRequirements();
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('focus', handleFocus);
|
window.addEventListener('focus', handleFocus);
|
||||||
|
|
@ -225,7 +225,7 @@ export const FrontendInterfaceSelector = ({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (frontendPreference) {
|
if (frontendPreference) {
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
checkAllFrontendRequirements();
|
void checkAllFrontendRequirements();
|
||||||
}
|
}
|
||||||
}, [frontendPreference, checkAllFrontendRequirements]);
|
}, [frontendPreference, checkAllFrontendRequirements]);
|
||||||
|
|
||||||
|
|
@ -323,7 +323,7 @@ export const FrontendInterfaceSelector = ({
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="red" onClick={handleClearOpenWebUIData}>
|
<Button color="red" onClick={() => void handleClearOpenWebUIData()}>
|
||||||
Clear Data
|
Clear Data
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export const GeneralTab = () => {
|
||||||
setStartMinimizedToTray(startMinimized);
|
setStartMinimizedToTray(startMinimized);
|
||||||
};
|
};
|
||||||
|
|
||||||
loadSystemTrayPreference();
|
void loadSystemTrayPreference();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSystemTrayToggle = async (checked: boolean) => {
|
const handleSystemTrayToggle = async (checked: boolean) => {
|
||||||
|
|
@ -66,7 +66,7 @@ export const GeneralTab = () => {
|
||||||
label="Enable system tray icon"
|
label="Enable system tray icon"
|
||||||
checked={enableSystemTray}
|
checked={enableSystemTray}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
handleSystemTrayToggle(event.currentTarget.checked)
|
void handleSystemTrayToggle(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -83,7 +83,7 @@ export const GeneralTab = () => {
|
||||||
checked={startMinimizedToTray}
|
checked={startMinimizedToTray}
|
||||||
disabled={!enableSystemTray}
|
disabled={!enableSystemTray}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
handleStartMinimizedToggle(event.currentTarget.checked)
|
void handleStartMinimizedToggle(event.currentTarget.checked)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ export const SystemTab = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadAll();
|
void loadAll();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (loading || !versionInfo) {
|
if (loading || !versionInfo) {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export const TroubleshootingTab = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadCurrentInstallDir();
|
void loadCurrentInstallDir();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSelectInstallDir = async () => {
|
const handleSelectInstallDir = async () => {
|
||||||
|
|
@ -49,7 +49,7 @@ export const TroubleshootingTab = () => {
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={handleSelectInstallDir}
|
onClick={() => void handleSelectInstallDir()}
|
||||||
leftSection={
|
leftSection={
|
||||||
<FolderOpen style={{ width: rem(16), height: rem(16) }} />
|
<FolderOpen style={{ width: rem(16), height: rem(16) }} />
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +58,7 @@ export const TroubleshootingTab = () => {
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={handleOpenInstallDir}
|
onClick={() => void handleOpenInstallDir()}
|
||||||
disabled={!installDir}
|
disabled={!installDir}
|
||||||
leftSection={
|
leftSection={
|
||||||
<ExternalLink style={{ width: rem(16), height: rem(16) }} />
|
<ExternalLink style={{ width: rem(16), height: rem(16) }} />
|
||||||
|
|
@ -83,7 +83,7 @@ export const TroubleshootingTab = () => {
|
||||||
leftSection={
|
leftSection={
|
||||||
<FolderOpen style={{ width: rem(16), height: rem(16) }} />
|
<FolderOpen style={{ width: rem(16), height: rem(16) }} />
|
||||||
}
|
}
|
||||||
onClick={() => window.electronAPI.app.showLogsFolder()}
|
onClick={() => void window.electronAPI.app.showLogsFolder()}
|
||||||
>
|
>
|
||||||
Show Logs
|
Show Logs
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -93,7 +93,7 @@ export const TroubleshootingTab = () => {
|
||||||
leftSection={
|
leftSection={
|
||||||
<Monitor style={{ width: rem(16), height: rem(16) }} />
|
<Monitor style={{ width: rem(16), height: rem(16) }} />
|
||||||
}
|
}
|
||||||
onClick={() => window.electronAPI.app.viewConfigFile()}
|
onClick={() => void window.electronAPI.app.viewConfigFile()}
|
||||||
>
|
>
|
||||||
View Config
|
View Config
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ export const useAppUpdateChecker = () => {
|
||||||
|
|
||||||
const installUpdate = useCallback(() => {
|
const installUpdate = useCallback(() => {
|
||||||
if (isUpdateDownloaded) {
|
if (isUpdateDownloaded) {
|
||||||
window.electronAPI.updater.quitAndInstall();
|
void window.electronAPI.updater.quitAndInstall();
|
||||||
}
|
}
|
||||||
}, [isUpdateDownloaded]);
|
}, [isUpdateDownloaded]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ export const useHuggingFaceSearch = (
|
||||||
|
|
||||||
const changeSortOrder = useCallback(
|
const changeSortOrder = useCallback(
|
||||||
(sort: HuggingFaceSortOption, query?: string) => {
|
(sort: HuggingFaceSortOption, query?: string) => {
|
||||||
searchModels(query, true, sort);
|
void searchModels(query, true, sort);
|
||||||
},
|
},
|
||||||
[searchModels]
|
[searchModels]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export const useUpdateChecker = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadDismissedUpdates();
|
void loadDismissedUpdates();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const checkForUpdates = useCallback(async () => {
|
const checkForUpdates = useCallback(async () => {
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,7 @@ export const useWarnings = ({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
updateBackendWarnings();
|
void updateBackendWarnings();
|
||||||
}, [updateBackendWarnings]);
|
}, [updateBackendWarnings]);
|
||||||
|
|
||||||
const allWarnings = useMemo(
|
const allWarnings = useMemo(
|
||||||
|
|
|
||||||
|
|
@ -86,10 +86,10 @@ export async function handleCliMode(args: string[]) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
on('SIGINT', handleSignal);
|
on('SIGINT', () => void handleSignal());
|
||||||
on('SIGTERM', handleSignal);
|
on('SIGTERM', () => void handleSignal());
|
||||||
if (platform === 'win32') {
|
if (platform === 'win32') {
|
||||||
on('SIGBREAK', handleSignal);
|
on('SIGBREAK', () => void handleSignal());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,12 +67,12 @@ export async function initializeApp(options?: { startMinimized?: boolean }) {
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('before-quit', async (event) => {
|
app.on('before-quit', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
await safeExecute(async () => {
|
void safeExecute(async () => {
|
||||||
const cleanupPromises = [
|
const cleanupPromises = [
|
||||||
cleanupWindow(),
|
Promise.resolve(cleanupWindow()),
|
||||||
stopKoboldCpp(),
|
stopKoboldCpp(),
|
||||||
stopSillyTavern(),
|
stopSillyTavern(),
|
||||||
stopOpenWebUI(),
|
stopOpenWebUI(),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { argv, exit } from 'process';
|
import { argv, exit } from 'process';
|
||||||
|
|
||||||
if (argv[1] === '--version') {
|
if (argv[1] === '--version') {
|
||||||
(async () => {
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
const { app } = await import('electron');
|
const { app } = await import('electron');
|
||||||
const version = await app.getVersion();
|
const version = app.getVersion();
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(version);
|
console.log(version);
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -14,7 +14,7 @@ if (argv[1] === '--version') {
|
||||||
exit(0);
|
exit(0);
|
||||||
})();
|
})();
|
||||||
} else {
|
} else {
|
||||||
(async () => {
|
void (async () => {
|
||||||
const isCliMode = argv.includes('--cli');
|
const isCliMode = argv.includes('--cli');
|
||||||
|
|
||||||
if (isCliMode) {
|
if (isCliMode) {
|
||||||
|
|
|
||||||
|
|
@ -150,9 +150,9 @@ export function setupIPCHandlers() {
|
||||||
);
|
);
|
||||||
|
|
||||||
ipcMain.handle('kobold:stopKoboldCpp', () => {
|
ipcMain.handle('kobold:stopKoboldCpp', () => {
|
||||||
stopKoboldCpp();
|
void stopKoboldCpp();
|
||||||
stopSillyTavernFrontend();
|
void stopSillyTavernFrontend();
|
||||||
stopOpenWebUIFrontend();
|
void stopOpenWebUIFrontend();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('kobold:parseConfigFile', (_, filePath) =>
|
ipcMain.handle('kobold:parseConfigFile', (_, filePath) =>
|
||||||
|
|
@ -196,13 +196,13 @@ export function setupIPCHandlers() {
|
||||||
|
|
||||||
ipcMain.handle('config:get', (_, key) => getConfig(key));
|
ipcMain.handle('config:get', (_, key) => getConfig(key));
|
||||||
|
|
||||||
ipcMain.on('config:set', (_, key, value) => setConfig(key, value));
|
ipcMain.on('config:set', (_, key, value) => void setConfig(key, value));
|
||||||
|
|
||||||
ipcMain.handle('app:getVersion', () => app.getVersion());
|
ipcMain.handle('app:getVersion', () => app.getVersion());
|
||||||
|
|
||||||
ipcMain.handle('app:getVersionInfo', () => getVersionInfo());
|
ipcMain.handle('app:getVersionInfo', () => getVersionInfo());
|
||||||
|
|
||||||
ipcMain.handle('app:openPath', (_, path) => openPathHandler(path));
|
ipcMain.handle('app:openPath', async (_, path) => openPathHandler(path));
|
||||||
|
|
||||||
ipcMain.handle('app:showLogsFolder', () =>
|
ipcMain.handle('app:showLogsFolder', () =>
|
||||||
openPathHandler(join(app.getPath('userData'), 'logs'))
|
openPathHandler(join(app.getPath('userData'), 'logs'))
|
||||||
|
|
@ -230,12 +230,12 @@ export function setupIPCHandlers() {
|
||||||
|
|
||||||
ipcMain.handle('app:setZoomLevel', (_, level) => {
|
ipcMain.handle('app:setZoomLevel', (_, level) => {
|
||||||
mainWindow.webContents.setZoomLevel(level);
|
mainWindow.webContents.setZoomLevel(level);
|
||||||
setConfig('zoomLevel', level);
|
void setConfig('zoomLevel', level);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('app:getColorScheme', () => getColorScheme());
|
ipcMain.handle('app:getColorScheme', () => getColorScheme());
|
||||||
|
|
||||||
ipcMain.handle('app:setColorScheme', (_, colorScheme) =>
|
ipcMain.handle('app:setColorScheme', async (_, colorScheme) =>
|
||||||
setConfig('colorScheme', colorScheme)
|
setConfig('colorScheme', colorScheme)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -275,8 +275,8 @@ export function setupIPCHandlers() {
|
||||||
|
|
||||||
ipcMain.handle('app:openExternal', async (_, url) => openUrl(url));
|
ipcMain.handle('app:openExternal', async (_, url) => openUrl(url));
|
||||||
|
|
||||||
mainWindow.webContents.once('did-finish-load', async () => {
|
mainWindow.webContents.once('did-finish-load', () => {
|
||||||
const savedZoomLevel = await getConfig('zoomLevel');
|
const savedZoomLevel = getConfig('zoomLevel');
|
||||||
if (typeof savedZoomLevel === 'number') {
|
if (typeof savedZoomLevel === 'number') {
|
||||||
mainWindow.webContents.setZoomLevel(savedZoomLevel);
|
mainWindow.webContents.setZoomLevel(savedZoomLevel);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,10 @@ async function downloadFile(asset: GitHubAsset, tempPackedFilePath: string) {
|
||||||
await pump();
|
await pump();
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
writer.on('finish', async () => {
|
writer.on(
|
||||||
|
'finish',
|
||||||
|
() =>
|
||||||
|
void (async () => {
|
||||||
if (platform !== 'win32') {
|
if (platform !== 'win32') {
|
||||||
try {
|
try {
|
||||||
await chmod(tempPackedFilePath, 0o755);
|
await chmod(tempPackedFilePath, 0o755);
|
||||||
|
|
@ -93,7 +96,8 @@ async function downloadFile(asset: GitHubAsset, tempPackedFilePath: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
});
|
})()
|
||||||
|
);
|
||||||
writer.on('error', reject);
|
writer.on('error', reject);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ export async function launchKoboldCpp(
|
||||||
sendKoboldOutput(commandLine);
|
sendKoboldOutput(commandLine);
|
||||||
|
|
||||||
if (remotetunnel) {
|
if (remotetunnel) {
|
||||||
startTunnel(frontendPreference);
|
void startTunnel(frontendPreference);
|
||||||
}
|
}
|
||||||
|
|
||||||
let readyResolve:
|
let readyResolve:
|
||||||
|
|
@ -334,9 +334,9 @@ export async function launchKoboldCpp(
|
||||||
|
|
||||||
export async function stopKoboldCpp() {
|
export async function stopKoboldCpp() {
|
||||||
abortActiveDownloads();
|
abortActiveDownloads();
|
||||||
stopProxy();
|
void stopProxy();
|
||||||
stopTunnel();
|
void stopTunnel();
|
||||||
stopPreLaunchProcesses();
|
void stopPreLaunchProcesses();
|
||||||
isIntentionalStop = true;
|
isIntentionalStop = true;
|
||||||
return terminateProcess(koboldProcess);
|
return terminateProcess(koboldProcess);
|
||||||
}
|
}
|
||||||
|
|
@ -346,14 +346,10 @@ export const launchKoboldCppWithCustomFrontends = async (
|
||||||
preLaunchCommands: string[] = []
|
preLaunchCommands: string[] = []
|
||||||
) =>
|
) =>
|
||||||
safeExecute(async () => {
|
safeExecute(async () => {
|
||||||
const [frontendPreference, imageGenerationFrontendPreference] =
|
const frontendPreference = getConfig('frontendPreference');
|
||||||
(await Promise.all([
|
const imageGenerationFrontendPreference = getConfig(
|
||||||
getConfig('frontendPreference'),
|
'imageGenerationFrontendPreference'
|
||||||
getConfig('imageGenerationFrontendPreference'),
|
);
|
||||||
])) as [
|
|
||||||
FrontendPreference,
|
|
||||||
ImageGenerationFrontendPreference | undefined,
|
|
||||||
];
|
|
||||||
|
|
||||||
const { isTextMode } = parseKoboldConfig(args);
|
const { isTextMode } = parseKoboldConfig(args);
|
||||||
|
|
||||||
|
|
@ -372,9 +368,9 @@ export const launchKoboldCppWithCustomFrontends = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frontendPreference === 'sillytavern') {
|
if (frontendPreference === 'sillytavern') {
|
||||||
startSillyTavernFrontend(args);
|
void startSillyTavernFrontend(args);
|
||||||
} else if (frontendPreference === 'openwebui') {
|
} else if (frontendPreference === 'openwebui') {
|
||||||
startOpenWebUIFrontend(args);
|
void startOpenWebUIFrontend(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ async function downloadFile(
|
||||||
abort: () => {
|
abort: () => {
|
||||||
isAborted = true;
|
isAborted = true;
|
||||||
currentRequest?.destroy();
|
currentRequest?.destroy();
|
||||||
cleanup();
|
void cleanup();
|
||||||
reject(new Error('Download aborted by user'));
|
reject(new Error('Download aborted by user'));
|
||||||
},
|
},
|
||||||
tempPath,
|
tempPath,
|
||||||
|
|
@ -197,7 +197,7 @@ async function downloadFile(
|
||||||
|
|
||||||
if (totalBytes > 0 && downloadedBytes !== totalBytes) {
|
if (totalBytes > 0 && downloadedBytes !== totalBytes) {
|
||||||
activeDownloads.delete(abortController);
|
activeDownloads.delete(abortController);
|
||||||
cleanup();
|
void cleanup();
|
||||||
reject(
|
reject(
|
||||||
new Error(
|
new Error(
|
||||||
`Incomplete download: received ${downloadedBytes} bytes, expected ${totalBytes} bytes`
|
`Incomplete download: received ${downloadedBytes} bytes, expected ${totalBytes} bytes`
|
||||||
|
|
@ -207,7 +207,10 @@ async function downloadFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
fileStream.end();
|
fileStream.end();
|
||||||
fileStream.on('finish', async () => {
|
fileStream.on(
|
||||||
|
'finish',
|
||||||
|
() =>
|
||||||
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
await rename(tempPath, outputPath);
|
await rename(tempPath, outputPath);
|
||||||
sendKoboldOutput('\n');
|
sendKoboldOutput('\n');
|
||||||
|
|
@ -217,22 +220,31 @@ async function downloadFile(
|
||||||
activeDownloads.delete(abortController);
|
activeDownloads.delete(abortController);
|
||||||
reject(err instanceof Error ? err : new Error(String(err)));
|
reject(err instanceof Error ? err : new Error(String(err)));
|
||||||
}
|
}
|
||||||
});
|
})()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
response.on('error', async (err) => {
|
response.on(
|
||||||
|
'error',
|
||||||
|
(err) =>
|
||||||
|
void (async () => {
|
||||||
if (isAborted) return;
|
if (isAborted) return;
|
||||||
activeDownloads.delete(abortController);
|
activeDownloads.delete(abortController);
|
||||||
await cleanup();
|
await cleanup();
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
})()
|
||||||
|
);
|
||||||
|
|
||||||
fileStream.on('error', async (err) => {
|
fileStream.on(
|
||||||
|
'error',
|
||||||
|
(err) =>
|
||||||
|
void (async () => {
|
||||||
if (isAborted) return;
|
if (isAborted) return;
|
||||||
activeDownloads.delete(abortController);
|
activeDownloads.delete(abortController);
|
||||||
await cleanup();
|
await cleanup();
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
})()
|
||||||
|
);
|
||||||
}).on('error', (err) => {
|
}).on('error', (err) => {
|
||||||
if (isAborted) return;
|
if (isAborted) return;
|
||||||
activeDownloads.delete(abortController);
|
activeDownloads.delete(abortController);
|
||||||
|
|
|
||||||
|
|
@ -64,20 +64,20 @@ export function startMonitoring(window: BrowserWindow) {
|
||||||
mainWindow = window;
|
mainWindow = window;
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
|
|
||||||
collectAndSendCpuMetrics();
|
void collectAndSendCpuMetrics();
|
||||||
cpuInterval = setInterval(() => {
|
cpuInterval = setInterval(() => {
|
||||||
collectAndSendCpuMetrics();
|
void collectAndSendCpuMetrics();
|
||||||
}, updateFrequency);
|
}, updateFrequency);
|
||||||
|
|
||||||
collectAndSendMemoryMetrics();
|
void collectAndSendMemoryMetrics();
|
||||||
memoryInterval = setInterval(() => {
|
memoryInterval = setInterval(() => {
|
||||||
collectAndSendMemoryMetrics();
|
void collectAndSendMemoryMetrics();
|
||||||
}, updateFrequency);
|
}, updateFrequency);
|
||||||
|
|
||||||
if (platform === 'linux') {
|
if (platform === 'linux') {
|
||||||
collectAndSendGpuMetrics();
|
void collectAndSendGpuMetrics();
|
||||||
gpuInterval = setInterval(() => {
|
gpuInterval = setInterval(() => {
|
||||||
collectAndSendGpuMetrics();
|
void collectAndSendGpuMetrics();
|
||||||
}, updateFrequency);
|
}, updateFrequency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,10 +82,8 @@ export async function startFrontend(args: string[]) {
|
||||||
isImageMode,
|
isImageMode,
|
||||||
} = parseKoboldConfig(args);
|
} = parseKoboldConfig(args);
|
||||||
|
|
||||||
const [, appVersion] = await Promise.all([
|
await stopFrontend();
|
||||||
stopFrontend(),
|
const appVersion = app.getVersion();
|
||||||
app.getVersion(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
sendKoboldOutput(
|
sendKoboldOutput(
|
||||||
`Preparing Open WebUI to connect at ${koboldHost}:${koboldPort}${isImageMode ? ' (with image generation)' : ''}...`
|
`Preparing Open WebUI to connect at ${koboldHost}:${koboldPort}${isImageMode ? ' (with image generation)' : ''}...`
|
||||||
|
|
|
||||||
|
|
@ -187,17 +187,23 @@ async function ensureSillyTavernSettings() {
|
||||||
const output = data.toString();
|
const output = data.toString();
|
||||||
|
|
||||||
if (output.includes(SERVER_READY_SIGNALS.SILLYTAVERN)) {
|
if (output.includes(SERVER_READY_SIGNALS.SILLYTAVERN)) {
|
||||||
setTimeout(async () => {
|
setTimeout(
|
||||||
|
() =>
|
||||||
|
void (async () => {
|
||||||
if (!initProcess.killed && !hasResolved) {
|
if (!initProcess.killed && !hasResolved) {
|
||||||
hasResolved = true;
|
hasResolved = true;
|
||||||
|
|
||||||
await terminateProcess(initProcess);
|
await terminateProcess(initProcess);
|
||||||
|
|
||||||
sendKoboldOutput('SillyTavern settings should now be generated');
|
sendKoboldOutput(
|
||||||
|
'SillyTavern settings should now be generated'
|
||||||
|
);
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
}, 2000);
|
})(),
|
||||||
|
2000
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ let serverPort = 0;
|
||||||
|
|
||||||
export const startStaticServer = (distPath: string) =>
|
export const startStaticServer = (distPath: string) =>
|
||||||
new Promise<string>((resolve, reject) => {
|
new Promise<string>((resolve, reject) => {
|
||||||
server = createServer(async (req, res) => {
|
server = createServer(
|
||||||
|
(req, res) =>
|
||||||
|
void (async () => {
|
||||||
const requestPath = req.url === '/' ? 'index.html' : req.url!;
|
const requestPath = req.url === '/' ? 'index.html' : req.url!;
|
||||||
let filePath = join(distPath, requestPath);
|
let filePath = join(distPath, requestPath);
|
||||||
|
|
||||||
|
|
@ -27,7 +29,8 @@ export const startStaticServer = (distPath: string) =>
|
||||||
res.writeHead(404);
|
res.writeHead(404);
|
||||||
res.end('Not found');
|
res.end('Not found');
|
||||||
}
|
}
|
||||||
});
|
})()
|
||||||
|
);
|
||||||
|
|
||||||
server.listen(0, 'localhost', () => {
|
server.listen(0, 'localhost', () => {
|
||||||
const address = server!.address();
|
const address = server!.address();
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,7 @@ function setupContextMenu(mainWindow: BrowserWindow) {
|
||||||
const hasSelection = !!params.selectionText;
|
const hasSelection = !!params.selectionText;
|
||||||
const isEditable = params.isEditable;
|
const isEditable = params.isEditable;
|
||||||
const isDev = isDevelopment;
|
const isDev = isDevelopment;
|
||||||
|
const hasMisspelling = !!params.misspelledWord;
|
||||||
|
|
||||||
const canCut = hasSelection && isEditable;
|
const canCut = hasSelection && isEditable;
|
||||||
const canCopy = hasSelection;
|
const canCopy = hasSelection;
|
||||||
|
|
@ -192,11 +193,40 @@ function setupContextMenu(mainWindow: BrowserWindow) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasEditOperations) {
|
if (hasMisspelling && params.dictionarySuggestions.length > 0) {
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
menuItems.push({ type: 'separator' as const });
|
menuItems.push({ type: 'separator' as const });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params.dictionarySuggestions.forEach((suggestion) => {
|
||||||
|
menuItems.push({
|
||||||
|
label: suggestion,
|
||||||
|
click: () => {
|
||||||
|
mainWindow.webContents.replaceMisspelling(suggestion);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
menuItems.push({ type: 'separator' as const });
|
||||||
|
menuItems.push({
|
||||||
|
label: 'Add to Dictionary',
|
||||||
|
click: () => {
|
||||||
|
mainWindow.webContents.session.addWordToSpellCheckerDictionary(
|
||||||
|
params.misspelledWord
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasEditOperations) {
|
||||||
|
menuItems.push({ type: 'separator' as const });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasEditOperations) {
|
||||||
|
if (isDev && !hasMisspelling) {
|
||||||
|
menuItems.push({ type: 'separator' as const });
|
||||||
|
}
|
||||||
|
|
||||||
if (canUndo) {
|
if (canUndo) {
|
||||||
menuItems.push({ label: 'Undo', role: 'undo' as const });
|
menuItems.push({ label: 'Undo', role: 'undo' as const });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ const koboldAPI: KoboldAPI = {
|
||||||
flashAttention,
|
flashAttention,
|
||||||
acceleration
|
acceleration
|
||||||
),
|
),
|
||||||
stopKoboldCpp: () => ipcRenderer.invoke('kobold:stopKoboldCpp'),
|
stopKoboldCpp: () => void ipcRenderer.invoke('kobold:stopKoboldCpp'),
|
||||||
onDownloadProgress: (callback) => {
|
onDownloadProgress: (callback) => {
|
||||||
const handler = (_: IpcRendererEvent, progress: number) =>
|
const handler = (_: IpcRendererEvent, progress: number) =>
|
||||||
callback(progress);
|
callback(progress);
|
||||||
|
|
@ -141,14 +141,14 @@ const appAPI: AppAPI = {
|
||||||
openPath: (path) => ipcRenderer.invoke('app:openPath', path),
|
openPath: (path) => ipcRenderer.invoke('app:openPath', path),
|
||||||
getVersion: () => ipcRenderer.invoke('app:getVersion'),
|
getVersion: () => ipcRenderer.invoke('app:getVersion'),
|
||||||
getVersionInfo: () => ipcRenderer.invoke('app:getVersionInfo'),
|
getVersionInfo: () => ipcRenderer.invoke('app:getVersionInfo'),
|
||||||
minimizeWindow: () => ipcRenderer.invoke('app:minimizeWindow'),
|
minimizeWindow: async () => ipcRenderer.invoke('app:minimizeWindow'),
|
||||||
maximizeWindow: () => ipcRenderer.invoke('app:maximizeWindow'),
|
maximizeWindow: async () => ipcRenderer.invoke('app:maximizeWindow'),
|
||||||
closeWindow: () => ipcRenderer.invoke('app:closeWindow'),
|
closeWindow: async () => ipcRenderer.invoke('app:closeWindow'),
|
||||||
isMaximized: () => ipcRenderer.invoke('app:isMaximized'),
|
isMaximized: () => ipcRenderer.invoke('app:isMaximized'),
|
||||||
getZoomLevel: () => ipcRenderer.invoke('app:getZoomLevel'),
|
getZoomLevel: () => ipcRenderer.invoke('app:getZoomLevel'),
|
||||||
setZoomLevel: (level) => ipcRenderer.invoke('app:setZoomLevel', level),
|
setZoomLevel: async (level) => ipcRenderer.invoke('app:setZoomLevel', level),
|
||||||
getColorScheme: () => ipcRenderer.invoke('app:getColorScheme'),
|
getColorScheme: () => ipcRenderer.invoke('app:getColorScheme'),
|
||||||
setColorScheme: (colorScheme) =>
|
setColorScheme: async (colorScheme) =>
|
||||||
ipcRenderer.invoke('app:setColorScheme', colorScheme),
|
ipcRenderer.invoke('app:setColorScheme', colorScheme),
|
||||||
getEnableSystemTray: () => ipcRenderer.invoke('app:getEnableSystemTray'),
|
getEnableSystemTray: () => ipcRenderer.invoke('app:getEnableSystemTray'),
|
||||||
setEnableSystemTray: (enabled) =>
|
setEnableSystemTray: (enabled) =>
|
||||||
|
|
@ -252,7 +252,7 @@ const monitoringAPI: MonitoringAPI = {
|
||||||
const updaterAPI: UpdaterAPI = {
|
const updaterAPI: UpdaterAPI = {
|
||||||
checkForUpdates: () => ipcRenderer.invoke('app:checkForUpdates'),
|
checkForUpdates: () => ipcRenderer.invoke('app:checkForUpdates'),
|
||||||
downloadUpdate: () => ipcRenderer.invoke('app:downloadUpdate'),
|
downloadUpdate: () => ipcRenderer.invoke('app:downloadUpdate'),
|
||||||
quitAndInstall: () => ipcRenderer.invoke('app:quitAndInstall'),
|
quitAndInstall: async () => ipcRenderer.invoke('app:quitAndInstall'),
|
||||||
isUpdateDownloaded: () => ipcRenderer.invoke('app:isUpdateDownloaded'),
|
isUpdateDownloaded: () => ipcRenderer.invoke('app:isUpdateDownloaded'),
|
||||||
canAutoUpdate: () => ipcRenderer.invoke('app:canAutoUpdate'),
|
canAutoUpdate: () => ipcRenderer.invoke('app:canAutoUpdate'),
|
||||||
isAURInstallation: () => ipcRenderer.invoke('app:isAURInstallation'),
|
isAURInstallation: () => ipcRenderer.invoke('app:isAURInstallation'),
|
||||||
|
|
|
||||||
|
|
@ -203,4 +203,4 @@ export const useKoboldBackendsStore = create<KoboldBackendsState>(
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
useKoboldBackendsStore.getState().initialize();
|
void useKoboldBackendsStore.getState().initialize();
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,10 @@ export const useNotepadStore = create<NotepadStore>()(
|
||||||
t.title === title ? { ...t, content: DEFAULT_TAB_CONTENT } : t
|
t.title === title ? { ...t, content: DEFAULT_TAB_CONTENT } : t
|
||||||
),
|
),
|
||||||
}));
|
}));
|
||||||
window.electronAPI.notepad.saveTabContent(title, DEFAULT_TAB_CONTENT);
|
void window.electronAPI.notepad.saveTabContent(
|
||||||
|
title,
|
||||||
|
DEFAULT_TAB_CONTENT
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
@ -90,7 +93,7 @@ export const useNotepadStore = create<NotepadStore>()(
|
||||||
activeTabId: newActiveTabId,
|
activeTabId: newActiveTabId,
|
||||||
});
|
});
|
||||||
|
|
||||||
window.electronAPI.notepad.deleteTab(title);
|
void window.electronAPI.notepad.deleteTab(title);
|
||||||
},
|
},
|
||||||
|
|
||||||
reorderTabs: (fromIndex, toIndex) => {
|
reorderTabs: (fromIndex, toIndex) => {
|
||||||
|
|
@ -184,7 +187,7 @@ useNotepadStore.subscribe(
|
||||||
}),
|
}),
|
||||||
() => {
|
() => {
|
||||||
if (useNotepadStore.getState().isLoaded) {
|
if (useNotepadStore.getState().isLoaded) {
|
||||||
useNotepadStore.getState().saveState();
|
void useNotepadStore.getState().saveState();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -198,5 +201,5 @@ useNotepadStore.subscribe(
|
||||||
);
|
);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
useNotepadStore.getState().loadState();
|
void useNotepadStore.getState().loadState();
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
|
||||||
8
src/types/electron.d.ts
vendored
8
src/types/electron.d.ts
vendored
|
|
@ -208,9 +208,9 @@ export interface AppAPI {
|
||||||
openPath: (path: string) => Promise<void>;
|
openPath: (path: string) => Promise<void>;
|
||||||
getVersion: () => Promise<string>;
|
getVersion: () => Promise<string>;
|
||||||
getVersionInfo: () => Promise<SystemVersionInfo>;
|
getVersionInfo: () => Promise<SystemVersionInfo>;
|
||||||
minimizeWindow: () => void;
|
minimizeWindow: () => Promise<void>;
|
||||||
maximizeWindow: () => void;
|
maximizeWindow: () => Promise<void>;
|
||||||
closeWindow: () => void;
|
closeWindow: () => Promise<void>;
|
||||||
isMaximized: () => Promise<boolean>;
|
isMaximized: () => Promise<boolean>;
|
||||||
getZoomLevel: () => Promise<number>;
|
getZoomLevel: () => Promise<number>;
|
||||||
setZoomLevel: (level: number) => Promise<void>;
|
setZoomLevel: (level: number) => Promise<void>;
|
||||||
|
|
@ -267,7 +267,7 @@ export interface MonitoringAPI {
|
||||||
export interface UpdaterAPI {
|
export interface UpdaterAPI {
|
||||||
checkForUpdates: () => Promise<boolean>;
|
checkForUpdates: () => Promise<boolean>;
|
||||||
downloadUpdate: () => Promise<boolean>;
|
downloadUpdate: () => Promise<boolean>;
|
||||||
quitAndInstall: () => void;
|
quitAndInstall: () => Promise<void>;
|
||||||
isUpdateDownloaded: () => Promise<boolean>;
|
isUpdateDownloaded: () => Promise<boolean>;
|
||||||
canAutoUpdate: () => Promise<boolean>;
|
canAutoUpdate: () => Promise<boolean>;
|
||||||
isAURInstallation: () => Promise<boolean>;
|
isAURInstallation: () => Promise<boolean>;
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,13 @@ const createAppLogger = () => {
|
||||||
format.timestamp({
|
format.timestamp({
|
||||||
format: 'YYYY-MM-DD HH:mm:ss.SSS',
|
format: 'YYYY-MM-DD HH:mm:ss.SSS',
|
||||||
}),
|
}),
|
||||||
format.printf(({ timestamp, level, message, error }) => {
|
format.printf((info) => {
|
||||||
|
const { timestamp, level, message, error } = info as {
|
||||||
|
timestamp: string;
|
||||||
|
level: string;
|
||||||
|
message: string;
|
||||||
|
error?: Error;
|
||||||
|
};
|
||||||
let logEntry = `${timestamp} [MAIN] [${level.toUpperCase()}] ${message}`;
|
let logEntry = `${timestamp} [MAIN] [${level.toUpperCase()}] ${message}`;
|
||||||
|
|
||||||
if (error && error instanceof Error) {
|
if (error && error instanceof Error) {
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,11 @@ export const openPathHandler = async (path: string) =>
|
||||||
error: 'Failed to open path',
|
error: 'Failed to open path',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const openUrl = (url: string) =>
|
export const openUrl = async (url: string) =>
|
||||||
safeExecute(async () => {
|
(await safeExecute(async () => {
|
||||||
await shell.openExternal(url);
|
await shell.openExternal(url);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}, 'Failed to open external URL') || {
|
}, 'Failed to open external URL')) || {
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Failed to open external URL',
|
error: 'Failed to open external URL',
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue