dep upgrades, code refactors for the new eslint-plugin-react-hooks rules, maybe better DownloadCard progress

This commit is contained in:
Egor 2025-10-09 13:59:53 -07:00
parent 4a314177c7
commit 576cfdbc02
20 changed files with 175 additions and 174 deletions

View file

@ -39,14 +39,14 @@
"license": "AGPL-3.0-or-later",
"devDependencies": {
"@eslint/js": "^9.37.0",
"@types/node": "^24.7.0",
"@types/react": "^19.2.1",
"@types/react-dom": "^19.2.0",
"@types/node": "^24.7.1",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.1",
"@typescript-eslint/eslint-plugin": "^8.46.0",
"@typescript-eslint/parser": "^8.46.0",
"@vitejs/plugin-react": "^5.0.4",
"cross-env": "^10.1.0",
"electron": "^38.2.1",
"electron": "^38.2.2",
"electron-builder": "^26.0.12",
"electron-vite": "^4.0.1",
"eslint": "^9.37.0",
@ -54,7 +54,7 @@
"eslint-plugin-no-comments": "^1.1.10",
"eslint-plugin-promise": "^7.2.1",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^6.1.1",
"eslint-plugin-react-hooks": "^7.0.0",
"eslint-plugin-sonarjs": "^3.0.5",
"globals": "^16.4.0",
"jiti": "^2.6.1",
@ -66,10 +66,10 @@
"dependencies": {
"@codemirror/search": "^6.5.11",
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.38.4",
"@codemirror/view": "^6.38.5",
"@fontsource/inter": "^5.2.8",
"@mantine/core": "^8.3.3",
"@mantine/hooks": "^8.3.3",
"@mantine/core": "^8.3.4",
"@mantine/hooks": "^8.3.4",
"@types/yauzl": "^2.10.3",
"@uiw/react-codemirror": "^4.25.2",
"electron-updater": "^6.6.2",

View file

@ -25,39 +25,35 @@ export const StatusBar = ({ maxDataPoints = 60 }: StatusBarProps) => {
const { isVisible, setVisible } = useNotepadStore();
useEffect(() => {
let isMounted = true;
let cleanupCpu: (() => void) | undefined;
let cleanupMemory: (() => void) | undefined;
let cleanupGpu: (() => void) | undefined;
let stopMonitoring: (() => void) | undefined;
if (systemMonitoringEnabled) {
const handleCpuMetrics = (metrics: CpuMetrics) => {
if (!isMounted) return;
setCpuMetrics(metrics);
};
const handleMemoryMetrics = (metrics: MemoryMetrics) => {
if (!isMounted) return;
setMemoryMetrics(metrics);
};
const handleGpuMetrics = (metrics: GpuMetrics) => {
if (!isMounted) return;
setGpuMetrics(metrics);
};
cleanupCpu = window.electronAPI.monitoring.onCpuMetrics(handleCpuMetrics);
cleanupMemory =
window.electronAPI.monitoring.onMemoryMetrics(handleMemoryMetrics);
cleanupGpu = window.electronAPI.monitoring.onGpuMetrics(handleGpuMetrics);
stopMonitoring = window.electronAPI.monitoring.start();
} else {
setCpuMetrics(null);
setMemoryMetrics(null);
setGpuMetrics(null);
if (!systemMonitoringEnabled) {
return;
}
let isMounted = true;
const handleCpuMetrics = (metrics: CpuMetrics) => {
if (!isMounted) return;
setCpuMetrics(metrics);
};
const handleMemoryMetrics = (metrics: MemoryMetrics) => {
if (!isMounted) return;
setMemoryMetrics(metrics);
};
const handleGpuMetrics = (metrics: GpuMetrics) => {
if (!isMounted) return;
setGpuMetrics(metrics);
};
const cleanupCpu =
window.electronAPI.monitoring.onCpuMetrics(handleCpuMetrics);
const cleanupMemory =
window.electronAPI.monitoring.onMemoryMetrics(handleMemoryMetrics);
const cleanupGpu =
window.electronAPI.monitoring.onGpuMetrics(handleGpuMetrics);
const stopMonitoring = window.electronAPI.monitoring.start();
return () => {
isMounted = false;
cleanupCpu?.();
@ -67,6 +63,10 @@ export const StatusBar = ({ maxDataPoints = 60 }: StatusBarProps) => {
};
}, [maxDataPoints, systemMonitoringEnabled]);
const displayCpuMetrics = systemMonitoringEnabled ? cpuMetrics : null;
const displayMemoryMetrics = systemMonitoringEnabled ? memoryMetrics : null;
const displayGpuMetrics = systemMonitoringEnabled ? gpuMetrics : null;
return (
<AppShell.Footer
style={{
@ -95,32 +95,32 @@ export const StatusBar = ({ maxDataPoints = 60 }: StatusBarProps) => {
<Group gap="xs">
{systemMonitoringEnabled ? (
<>
{cpuMetrics && memoryMetrics && (
{displayCpuMetrics && displayMemoryMetrics && (
<>
<PerformanceBadge
label="CPU"
value={`${cpuMetrics.usage}%`}
tooltipLabel={`${cpuMetrics.usage}%${cpuMetrics.temperature ? `${cpuMetrics.temperature}°C` : ''}`}
value={`${displayCpuMetrics.usage}%`}
tooltipLabel={`${displayCpuMetrics.usage}%${displayCpuMetrics.temperature ? `${displayCpuMetrics.temperature}°C` : ''}`}
/>
<PerformanceBadge
label="RAM"
value={`${memoryMetrics.usage}%`}
tooltipLabel={`${memoryMetrics.used.toFixed(2)} GB / ${memoryMetrics.total.toFixed(2)} GB (${memoryMetrics.usage}%)`}
value={`${displayMemoryMetrics.usage}%`}
tooltipLabel={`${displayMemoryMetrics.used.toFixed(2)} GB / ${displayMemoryMetrics.total.toFixed(2)} GB (${displayMemoryMetrics.usage}%)`}
/>
</>
)}
{gpuMetrics?.gpus.map((gpu, index) => (
{displayGpuMetrics?.gpus.map((gpu, index) => (
<Group gap="xs" key={`gpu-${index}`}>
<PerformanceBadge
label={`GPU${gpuMetrics.gpus.length > 1 ? ` ${index + 1}` : ''}`}
label={`GPU${displayGpuMetrics.gpus.length > 1 ? ` ${index + 1}` : ''}`}
value={`${gpu.usage}%`}
tooltipLabel={`${gpu.usage}%${gpu.temperature ? `${gpu.temperature}°C` : ''}`}
/>
<PerformanceBadge
label={`VRAM${gpuMetrics.gpus.length > 1 ? ` ${index + 1}` : ''}`}
label={`VRAM${displayGpuMetrics.gpus.length > 1 ? ` ${index + 1}` : ''}`}
value={`${gpu.memoryUsage}%`}
tooltipLabel={`${gpu.memoryUsed.toFixed(2)} GB / ${gpu.memoryTotal.toFixed(2)} GB (${gpu.memoryUsage}%)`}
/>

View file

@ -220,6 +220,7 @@ export const TitleBar = ({
</Box>
<SettingsModal
key={settingsModalOpen ? 'open' : 'closed'}
isOnInterfaceScreen={currentScreen === 'interface'}
opened={settingsModalOpen}
onClose={() => setSettingsModalOpen(false)}

View file

@ -58,6 +58,11 @@ export const App = () => {
void updateTray();
}, [currentScreen, model, sdmodel, systemMonitoringEnabled]);
const performEject = () => {
window.electronAPI.kobold.stopKoboldCpp();
setCurrentScreen('launch');
};
useEffect(() => {
const ejectCleanup = window.electronAPI.app.onTrayEject(() => {
performEject();
@ -78,6 +83,19 @@ export const App = () => {
const { handleDownload, loadingRemote } = useKoboldVersionsStore();
const determineScreen = (
currentVersion: unknown,
hasSeenWelcome: boolean
) => {
if (!hasSeenWelcome) {
setCurrentScreen('welcome');
} else if (currentVersion) {
setCurrentScreen('launch');
} else {
setCurrentScreen('download');
}
};
useEffect(() => {
const checkInstallation = async () => {
const [currentVersion, hasSeenWelcome] = await Promise.all([
@ -108,19 +126,6 @@ export const App = () => {
runUpdateCheck();
}, [loadingRemote, hasInitialized, checkForUpdates]);
const determineScreen = (
currentVersion: unknown,
hasSeenWelcome: boolean
) => {
if (!hasSeenWelcome) {
setCurrentScreen('welcome');
} else if (currentVersion) {
setCurrentScreen('launch');
} else {
setCurrentScreen('download');
}
};
const handleBinaryUpdate = async (download: DownloadItem) => {
const currentVersion = await window.electronAPI.kobold.getCurrentVersion();
@ -146,11 +151,6 @@ export const App = () => {
}
};
const performEject = () => {
window.electronAPI.kobold.stopKoboldCpp();
setCurrentScreen('launch');
};
const handleEjectConfirm = (skipConfirmation: boolean) => {
if (skipConfirmation) {
window.electronAPI.config.set('skipEjectConfirmation', true);

View file

@ -13,14 +13,13 @@ import { Download, Trash2 } from 'lucide-react';
import { MouseEvent } from 'react';
import { pretifyBinName, isWindowsROCmBuild } from '@/utils/assets';
import { usePreferencesStore } from '@/stores/preferences';
import { useKoboldVersionsStore } from '@/stores/koboldVersions';
import type { VersionInfo } from '@/types';
interface DownloadCardProps {
version: VersionInfo;
size: string;
description?: string;
isLoading?: boolean;
downloadProgress?: number;
disabled?: boolean;
onDownload: (e: MouseEvent<HTMLButtonElement>) => void;
onMakeCurrent?: () => void;
@ -33,8 +32,6 @@ export const DownloadCard = ({
version: versionInfo,
size,
description,
isLoading = false,
downloadProgress = 0,
disabled = false,
onDownload,
onMakeCurrent,
@ -43,6 +40,12 @@ export const DownloadCard = ({
onDelete,
}: DownloadCardProps) => {
const { resolvedColorScheme: colorScheme } = usePreferencesStore();
const { downloading, downloadProgress } = useKoboldVersionsStore();
const isLoading = downloading === versionInfo.name;
const currentProgress = isLoading
? downloadProgress[versionInfo.name] || 0
: 0;
const hasVersionMismatch = Boolean(
versionInfo.version &&
versionInfo.actualVersion &&
@ -226,18 +229,17 @@ export const DownloadCard = ({
{renderActionButtons()}
</Group>
{isLoading && downloadProgress !== undefined && (
{isLoading && currentProgress !== undefined && (
<Stack gap="xs" mt="sm">
<Progress
value={Math.min(downloadProgress, 100)}
value={Math.min(currentProgress, 100)}
color="blue"
radius="xl"
/>
<Text size="xs" c="dimmed" ta="center">
{Math.min(downloadProgress, 100) === 100
? '100%'
: `${Math.min(downloadProgress, 100).toFixed(1)}%`}{' '}
complete
{Math.min(currentProgress, 100) === 100
? '100.0% complete'
: `${Math.min(currentProgress, 100).toFixed(1).padStart(5, ' ')}% complete`}
</Text>
</Stack>
)}

View file

@ -228,7 +228,7 @@ export const NotepadContainer = () => {
</Box>
<Box style={{ flex: 1, position: 'relative' }}>
{activeTab && <NotepadEditor tab={activeTab} />}
{activeTab && <NotepadEditor key={activeTab.title} tab={activeTab} />}
</Box>
</Box>

View file

@ -23,7 +23,7 @@ export const NotepadEditor = ({ tab }: NotepadEditorProps) => {
const { saveTabContent, showLineNumbers, setShowLineNumbers } =
useNotepadStore();
const { resolvedColorScheme } = usePreferencesStore();
const [content, setContent] = useState(tab.content);
const [content, setContent] = useState(() => tab.content);
const [saveTimeout, setSaveTimeout] = useState<ReturnType<
typeof setTimeout
> | null>(null);
@ -60,10 +60,6 @@ export const NotepadEditor = ({ tab }: NotepadEditorProps) => {
}
}, []);
useEffect(() => {
setContent(tab.content);
}, [tab.content, tab.title]);
useEffect(
() => () => {
if (saveTimeout) {

View file

@ -18,7 +18,6 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
loadingPlatform,
loadingRemote,
downloading,
downloadProgress,
handleDownload: handleDownloadFromStore,
} = useKoboldVersionsStore();
@ -96,12 +95,6 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
download.url
)}
description={getAssetDescription(download.name)}
isLoading={isDownloading}
downloadProgress={
isDownloading
? downloadProgress[download.name] || 0
: 0
}
disabled={
Boolean(downloading) &&
downloadingAsset !== download.name

View file

@ -147,6 +147,7 @@ export const ConfigFileManager = ({
</Stack>
<CreateConfigModal
key={configModalOpened ? 'open' : 'closed'}
opened={configModalOpened}
onClose={handleCloseConfigModal}
onCreateConfig={onCreateNewConfig}

View file

@ -1,6 +1,6 @@
import { TextInput, Group, Button, Stack } from '@mantine/core';
import { Modal } from '@/components/Modal';
import { useState, useEffect } from 'react';
import { useState } from 'react';
interface CreateConfigModalProps {
opened: boolean;
@ -34,12 +34,6 @@ export const CreateConfigModal = ({
onClose();
};
useEffect(() => {
if (opened) {
setNewConfigName('');
}
}, [opened]);
return (
<Modal
opened={opened}

View file

@ -1,5 +1,5 @@
import { Stack, Text, TextInput, Group } from '@mantine/core';
import { useState, useEffect } from 'react';
import { useState } from 'react';
import { InfoTooltip } from '@/components/InfoTooltip';
import { CheckboxWithTooltip } from '@/components/CheckboxWithTooltip';
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
@ -21,11 +21,7 @@ export const NetworkTab = () => {
handleNocertifyChange,
handleWebsearchChange,
} = useLaunchConfig();
const [portInput, setPortInput] = useState(port?.toString() ?? '');
useEffect(() => {
setPortInput(port?.toString() ?? '');
}, [port]);
const [portInput, setPortInput] = useState('');
return (
<Stack gap="md">
@ -53,7 +49,7 @@ export const NetworkTab = () => {
</Group>
<TextInput
placeholder="5001"
value={portInput}
value={portInput || port?.toString() || ''}
onChange={(event) => {
const value = event.currentTarget.value;
setPortInput(value);

View file

@ -107,7 +107,7 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
void setInitialDefaults(model, sdmodel);
}
}
}, [configLoaded, setHappyDefaults]);
}, [configLoaded, setHappyDefaults, model, sdmodel, setInitialDefaults]);
const loadConfigFiles = useCallback(async () => {
const [files, currentDir, savedConfig] = await Promise.all([
@ -131,7 +131,14 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
}
setConfigLoaded(true);
}, [selectedFile, loadConfigFromFile]);
}, [
selectedFile,
loadConfigFromFile,
setConfigFiles,
setInstallDir,
setSelectedFile,
setConfigLoaded,
]);
const handleFileSelection = async (fileName: string) => {
setSelectedFile(fileName);
@ -247,6 +254,7 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
};
useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
void loadConfigFiles();
const handleInstallDirChange = () => {

View file

@ -215,6 +215,7 @@ export const FrontendInterfaceSelector = ({
useEffect(() => {
if (frontendPreference) {
// eslint-disable-next-line react-hooks/set-state-in-effect
checkAllFrontendRequirements();
}
}, [frontendPreference, checkAllFrontendRequirements]);

View file

@ -9,14 +9,14 @@ export const GeneralTab = () => {
usePreferencesStore();
useEffect(() => {
const loadSystemTrayPreference = async () => {
const enabled = await window.electronAPI.app.getEnableSystemTray();
setEnableSystemTray(enabled);
};
loadSystemTrayPreference();
}, []);
const loadSystemTrayPreference = async () => {
const enabled = await window.electronAPI.app.getEnableSystemTray();
setEnableSystemTray(enabled);
};
const handleSystemTrayToggle = async (checked: boolean) => {
setEnableSystemTray(checked);
await window.electronAPI.app.setEnableSystemTray(checked);

View file

@ -36,16 +36,11 @@ export const SettingsModal = ({
const showVersionsTab =
currentScreen !== 'download' && currentScreen !== 'welcome';
useEffect(() => {
if (!showVersionsTab && activeTab === 'versions') {
setActiveTab('general');
}
}, [showVersionsTab, activeTab]);
const effectiveActiveTab =
!showVersionsTab && activeTab === 'versions' ? 'general' : activeTab;
useEffect(() => {
if (opened) {
setActiveTab('general');
const originalOverflow = document.body.style.overflow;
const scrollbarWidth =
window.innerWidth - document.documentElement.clientWidth;
@ -83,7 +78,7 @@ export const SettingsModal = ({
}}
>
<Tabs
value={activeTab}
value={effectiveActiveTab}
onChange={(value) => value && setActiveTab(value)}
orientation="vertical"
variant="pills"

View file

@ -6,16 +6,16 @@ export const TroubleshootingTab = () => {
const [installDir, setInstallDir] = useState('');
useEffect(() => {
const loadCurrentInstallDir = async () => {
const currentDir = await window.electronAPI.kobold.getCurrentInstallDir();
if (currentDir) {
setInstallDir(currentDir);
}
};
loadCurrentInstallDir();
}, []);
const loadCurrentInstallDir = async () => {
const currentDir = await window.electronAPI.kobold.getCurrentInstallDir();
if (currentDir) {
setInstallDir(currentDir);
}
};
const handleSelectInstallDir = async () => {
const selectedDir =
await window.electronAPI.kobold.selectInstallDirectory();

View file

@ -28,7 +28,6 @@ export const VersionsTab = () => {
loadingPlatform,
loadingRemote,
downloading,
downloadProgress,
handleDownload: handleDownloadFromStore,
getLatestReleaseWithDownloadStatus,
} = useKoboldVersionsStore();
@ -67,8 +66,10 @@ export const VersionsTab = () => {
}, [getLatestReleaseWithDownloadStatus]);
useEffect(() => {
/* eslint-disable react-hooks/set-state-in-effect */
loadInstalledVersions();
loadLatestRelease();
/* eslint-enable react-hooks/set-state-in-effect */
}, [loadInstalledVersions, loadLatestRelease]);
const allVersions = useMemo((): VersionInfo[] => {
@ -281,8 +282,6 @@ export const VersionsTab = () => {
: ''
}
description={getAssetDescription(version.name)}
isLoading={isDownloading}
downloadProgress={downloadProgress[version.name]}
disabled={downloading !== null}
onDownload={(e) => {
e.stopPropagation();

View file

@ -229,6 +229,7 @@ export const useWarnings = ({
}, [backend]);
useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
updateBackendWarnings();
}, [updateBackendWarnings]);

View file

@ -28,7 +28,6 @@ let currentMetrics: {
interface TrayAppState {
currentScreen: Screen | null;
isLaunched: boolean;
currentModel: string | null;
currentConfig: string | null;
monitoringEnabled: boolean;
}
@ -36,7 +35,6 @@ interface TrayAppState {
const appState: TrayAppState = {
currentScreen: null,
isLaunched: false,
currentModel: null,
currentConfig: null,
monitoringEnabled: false,
};
@ -51,12 +49,11 @@ export function updateTrayState(state: {
appState.currentScreen = state.screen;
appState.isLaunched = state.screen === 'interface';
}
if (state.model !== undefined) {
appState.currentModel = state.model;
}
if (state.config !== undefined) {
appState.currentConfig = state.config;
}
if (state.monitoringEnabled !== undefined) {
appState.monitoringEnabled = state.monitoringEnabled;
}
@ -149,7 +146,7 @@ function buildContextMenu() {
menuTemplate.push({ type: 'separator' });
if (appState.isLaunched && appState.currentScreen === 'interface') {
if (appState.currentModel) {
if (appState.currentConfig) {
menuTemplate.push({
label: `Running: ${stripFileExtension(appState.currentConfig || '')}`,
enabled: false,

View file

@ -315,15 +315,15 @@ __metadata:
languageName: node
linkType: hard
"@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0, @codemirror/view@npm:^6.35.0, @codemirror/view@npm:^6.38.4":
version: 6.38.4
resolution: "@codemirror/view@npm:6.38.4"
"@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0, @codemirror/view@npm:^6.35.0, @codemirror/view@npm:^6.38.5":
version: 6.38.5
resolution: "@codemirror/view@npm:6.38.5"
dependencies:
"@codemirror/state": "npm:^6.5.0"
crelt: "npm:^1.0.6"
style-mod: "npm:^4.1.0"
w3c-keyname: "npm:^2.2.4"
checksum: 10c0/8133e7d723f5ca82449e866cffdd267f72c5f924b5a88bdbc1ae29ca3e3274c8c5695342c82da8eb5b0509688ef8a0cfc6d517352a5329cfc3fed2cbbd504ce4
checksum: 10c0/ad2b2459d40154b24ac665197f0b41edab781f82cd99950c5b1285dc19b14c33788a03df6fcdaf843a99de4bb5f1c1c113b59f7d7e72daf6dcb50e706a62959d
languageName: node
linkType: hard
@ -991,9 +991,9 @@ __metadata:
languageName: node
linkType: hard
"@mantine/core@npm:^8.3.3":
version: 8.3.3
resolution: "@mantine/core@npm:8.3.3"
"@mantine/core@npm:^8.3.4":
version: 8.3.4
resolution: "@mantine/core@npm:8.3.4"
dependencies:
"@floating-ui/react": "npm:^0.27.16"
clsx: "npm:^2.1.1"
@ -1002,19 +1002,19 @@ __metadata:
react-textarea-autosize: "npm:8.5.9"
type-fest: "npm:^4.41.0"
peerDependencies:
"@mantine/hooks": 8.3.3
"@mantine/hooks": 8.3.4
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
checksum: 10c0/5ce82c59a3199e7364997aef3e578b06c4dabfba3c030970097d19874a0628f540d7752d00f275f4d3fbc09067b01f9277846ce2e376c7aa1ecadf534b187a0e
checksum: 10c0/4a3fb1509d59ebb05b26659c349c871a8507377bf317c15d4694edd2c33b11ba4ff3548dfabd9b3c0327b3269dc1e8cdb045fd03e83ddbe3c7fdb3032d7fd9a3
languageName: node
linkType: hard
"@mantine/hooks@npm:^8.3.3":
version: 8.3.3
resolution: "@mantine/hooks@npm:8.3.3"
"@mantine/hooks@npm:^8.3.4":
version: 8.3.4
resolution: "@mantine/hooks@npm:8.3.4"
peerDependencies:
react: ^18.x || ^19.x
checksum: 10c0/9bc5f9bc7fad06be530a32a1450f7dfbcb61676080e364966a3fdf5d45de2e53330efa88000b019596818b7f3b4d18c0ed04e3964bdfe9b8dec20128cf2a69bb
checksum: 10c0/5ec0c45a615a6673aa2597702809cc9d23885b986cf383f07f15f76831f2ac5f34933a44aada2b11361c5ede0f5c9496b550269258a3e3d12395541cd4d27aea
languageName: node
linkType: hard
@ -1431,12 +1431,12 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:*, @types/node@npm:^24.7.0":
version: 24.7.0
resolution: "@types/node@npm:24.7.0"
"@types/node@npm:*, @types/node@npm:^24.7.1":
version: 24.7.1
resolution: "@types/node@npm:24.7.1"
dependencies:
undici-types: "npm:~7.14.0"
checksum: 10c0/f036c78062cb3a0d5c6586bf2dac347ed3f4af121cef4ab92c85c44e32be1c50aab5ba96955842b7f8165f0e0f1c8066d1813ae259372df6c0fa9fadb1116a3e
checksum: 10c0/2525f2aa865d78b1c75faaf8c6bf2af51c930962d8078306620d9a76eafcbbea035142cf2cdc2fcf1b4010cd3958a1c6c59b67aba1ac205dc1e5f895ef6af673
languageName: node
linkType: hard
@ -1459,21 +1459,21 @@ __metadata:
languageName: node
linkType: hard
"@types/react-dom@npm:^19.2.0":
version: 19.2.0
resolution: "@types/react-dom@npm:19.2.0"
"@types/react-dom@npm:^19.2.1":
version: 19.2.1
resolution: "@types/react-dom@npm:19.2.1"
peerDependencies:
"@types/react": ^19.2.0
checksum: 10c0/73ba326c8bc53e7bb597aa8e66ce4aabd79e501f744e1386278f0c63f1be6d78cca71a8269af3565206f296675116109a3ccbed4038409614fabf8405e54c6ef
checksum: 10c0/0dbbc5b7ecd74681bfac95a413133b26118a70b8840748277abafa47e5c7a037beae6a660e6a21fb53f5cbdb0b2d33e117ea7bbd976a888c298392a8a96bc68f
languageName: node
linkType: hard
"@types/react@npm:^19.2.1":
version: 19.2.1
resolution: "@types/react@npm:19.2.1"
"@types/react@npm:^19.2.2":
version: 19.2.2
resolution: "@types/react@npm:19.2.2"
dependencies:
csstype: "npm:^3.0.2"
checksum: 10c0/c44881c275da91156ce02986ab1f59c9724db256f4850d3937c9acea561a6ab1fe1028f7a1fc4da3a2c1bcb00de29e238922e8c6d42a727ef2e6e0cd40b3db9f
checksum: 10c0/f830b1204aca4634ce3c6cb3477b5d3d066b80a4dd832a4ee0069acb504b6debd2416548a43a11c1407c12bc60e2dc6cf362934a18fe75fe06a69c0a98cba8ab
languageName: node
linkType: hard
@ -2920,16 +2920,16 @@ __metadata:
languageName: node
linkType: hard
"electron@npm:^38.2.1":
version: 38.2.1
resolution: "electron@npm:38.2.1"
"electron@npm:^38.2.2":
version: 38.2.2
resolution: "electron@npm:38.2.2"
dependencies:
"@electron/get": "npm:^2.0.0"
"@types/node": "npm:^22.7.7"
extract-zip: "npm:^2.0.1"
bin:
electron: cli.js
checksum: 10c0/f9be3bda71dfa4e04e28e4e23cb02a1103d822763d62d2b0bf9a553e8593a298fdfb47dbc0ed4a252a77ea05a890b4612370d8aa339a2b67dacbcd28c3713798
checksum: 10c0/35b9e70745b65c9b35ebb481864323fcbc99be49aed74b67859de2aca4c2a40290661962c5ce631f83a7571952c69eb57979ef938999caebcabc9fe1de5a75c4
languageName: node
linkType: hard
@ -3307,17 +3307,18 @@ __metadata:
languageName: node
linkType: hard
"eslint-plugin-react-hooks@npm:^6.1.1":
version: 6.1.1
resolution: "eslint-plugin-react-hooks@npm:6.1.1"
"eslint-plugin-react-hooks@npm:^7.0.0":
version: 7.0.0
resolution: "eslint-plugin-react-hooks@npm:7.0.0"
dependencies:
"@babel/core": "npm:^7.24.4"
"@babel/parser": "npm:^7.24.4"
hermes-parser: "npm:^0.25.1"
zod: "npm:^3.22.4 || ^4.0.0"
zod-validation-error: "npm:^3.0.3 || ^4.0.0"
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
checksum: 10c0/579be053bc89c995a6c03996f9ee3f6bac88946b4b1c8b891b42f981e7c05a9c5de46324bbd2a33199855c0a602820c0e3eeb7f840730301b77a9ba3dc7a0ae2
checksum: 10c0/911c9efdd9b102ce2eabac247dff8c217ecb8d6972aaf3b7eecfb1cfc293d4d902766355993ff7a37a33c0abde3e76971f43bc1c8ff36d6c123310e5680d0423
languageName: node
linkType: hard
@ -3856,21 +3857,21 @@ __metadata:
dependencies:
"@codemirror/search": "npm:^6.5.11"
"@codemirror/theme-one-dark": "npm:^6.1.3"
"@codemirror/view": "npm:^6.38.4"
"@codemirror/view": "npm:^6.38.5"
"@eslint/js": "npm:^9.37.0"
"@fontsource/inter": "npm:^5.2.8"
"@mantine/core": "npm:^8.3.3"
"@mantine/hooks": "npm:^8.3.3"
"@types/node": "npm:^24.7.0"
"@types/react": "npm:^19.2.1"
"@types/react-dom": "npm:^19.2.0"
"@mantine/core": "npm:^8.3.4"
"@mantine/hooks": "npm:^8.3.4"
"@types/node": "npm:^24.7.1"
"@types/react": "npm:^19.2.2"
"@types/react-dom": "npm:^19.2.1"
"@types/yauzl": "npm:^2.10.3"
"@typescript-eslint/eslint-plugin": "npm:^8.46.0"
"@typescript-eslint/parser": "npm:^8.46.0"
"@uiw/react-codemirror": "npm:^4.25.2"
"@vitejs/plugin-react": "npm:^5.0.4"
cross-env: "npm:^10.1.0"
electron: "npm:^38.2.1"
electron: "npm:^38.2.2"
electron-builder: "npm:^26.0.12"
electron-updater: "npm:^6.6.2"
electron-vite: "npm:^4.0.1"
@ -3879,7 +3880,7 @@ __metadata:
eslint-plugin-no-comments: "npm:^1.1.10"
eslint-plugin-promise: "npm:^7.2.1"
eslint-plugin-react: "npm:^7.37.5"
eslint-plugin-react-hooks: "npm:^6.1.1"
eslint-plugin-react-hooks: "npm:^7.0.0"
eslint-plugin-sonarjs: "npm:^3.0.5"
execa: "npm:^9.6.0"
globals: "npm:^16.4.0"
@ -4168,6 +4169,22 @@ __metadata:
languageName: node
linkType: hard
"hermes-estree@npm:0.25.1":
version: 0.25.1
resolution: "hermes-estree@npm:0.25.1"
checksum: 10c0/48be3b2fa37a0cbc77a112a89096fa212f25d06de92781b163d67853d210a8a5c3784fac23d7d48335058f7ed283115c87b4332c2a2abaaccc76d0ead1a282ac
languageName: node
linkType: hard
"hermes-parser@npm:^0.25.1":
version: 0.25.1
resolution: "hermes-parser@npm:0.25.1"
dependencies:
hermes-estree: "npm:0.25.1"
checksum: 10c0/3abaa4c6f1bcc25273f267297a89a4904963ea29af19b8e4f6eabe04f1c2c7e9abd7bfc4730ddb1d58f2ea04b6fee74053d8bddb5656ec6ebf6c79cc8d14202c
languageName: node
linkType: hard
"hosted-git-info@npm:^4.1.0":
version: 4.1.0
resolution: "hosted-git-info@npm:4.1.0"