fix CUDA devices not being listed under system hardware, calcuate our own auto VRAM layers instead of relying on kcpp

This commit is contained in:
Egor 2025-11-03 18:49:42 -08:00
parent 537bd4d710
commit 59675ea59a
11 changed files with 351 additions and 93 deletions

View file

@ -1,7 +1,7 @@
{
"name": "gerbil",
"productName": "Gerbil",
"version": "1.8.3",
"version": "1.8.4",
"description": "Run Large Language Models locally",
"main": "out/main/index.js",
"homepage": "./",
@ -38,19 +38,19 @@
},
"license": "AGPL-3.0-or-later",
"devDependencies": {
"@eslint/js": "^9.39.0",
"@eslint/js": "^9.39.1",
"@types/node": "^24.10.0",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@types/yauzl": "^2.10.3",
"@typescript-eslint/eslint-plugin": "^8.46.2",
"@typescript-eslint/parser": "^8.46.2",
"@typescript-eslint/eslint-plugin": "^8.46.3",
"@typescript-eslint/parser": "^8.46.3",
"@vitejs/plugin-react": "^5.1.0",
"cross-env": "^10.1.0",
"electron": "^38.5.0",
"electron-builder": "^26.0.12",
"electron-vite": "^4.0.1",
"eslint": "^9.39.0",
"eslint": "^9.39.1",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-no-comments": "^1.1.10",
"eslint-plugin-promise": "^7.2.1",

View file

@ -610,7 +610,7 @@ const COMMAND_LINE_ARGUMENTS = [
{
flag: '--sdvaeauto',
description:
'Uses a built-in VAE via TAE SD, which is very fast, and fixed bad VAEs.',
'Uses a built-in VAE via TAE SD, which is very fast and fixed bad VAEs.',
type: 'boolean',
category: 'Image Generation',
},

View file

@ -12,6 +12,10 @@ export const BackendSelector = () => {
backend,
gpuLayers,
autoGpuLayers,
model,
contextSize,
gpuDeviceSelection,
flashattention,
handleBackendChange,
handleGpuLayersChange,
handleAutoGpuLayersChange,
@ -21,6 +25,7 @@ export const BackendSelector = () => {
[]
);
const [isLoadingBackends, setIsLoadingBackends] = useState(false);
const [isCalculatingLayers, setIsCalculatingLayers] = useState(false);
const hasInitialized = useRef(false);
useEffect(() => {
@ -62,6 +67,76 @@ export const BackendSelector = () => {
}
}, [availableBackends, backend, handleBackendChange]);
useEffect(() => {
const calculateLayers = async () => {
if (
!autoGpuLayers ||
!model ||
!contextSize ||
backend === 'cpu' ||
isLoadingBackends
) {
return;
}
try {
setIsCalculatingLayers(true);
const gpuMemory = await window.electronAPI.kobold.detectGPUMemory();
if (!gpuMemory || gpuMemory.length === 0) {
return;
}
const selectedDeviceIndices = gpuDeviceSelection
.split(',')
.map((d) => parseInt(d.trim(), 10))
.filter((d) => !isNaN(d));
const availableVramGB = selectedDeviceIndices.reduce(
(total, deviceIndex) => {
const device = gpuMemory[deviceIndex];
const vramGB = device?.totalMemoryGB
? parseFloat(device.totalMemoryGB)
: 0;
return total + vramGB;
},
0
);
if (availableVramGB === 0) {
return;
}
const result = await window.electronAPI.kobold.calculateOptimalLayers(
model,
contextSize,
availableVramGB,
flashattention
);
handleGpuLayersChange(result.recommendedLayers);
} catch (error) {
window.electronAPI.logs.logError(
'Failed to calculate optimal GPU layers',
error as Error
);
} finally {
setIsCalculatingLayers(false);
}
};
calculateLayers();
}, [
autoGpuLayers,
model,
contextSize,
backend,
gpuDeviceSelection,
flashattention,
isLoadingBackends,
handleGpuLayersChange,
]);
return (
<div>
<Group justify="space-between" align="flex-start" mb="xs">
@ -70,7 +145,7 @@ export const BackendSelector = () => {
<Text size="sm" fw={500}>
Backend
</Text>
<InfoTooltip label="Select a backend to use to run LLMs. CUDA runs on NVIDIA GPUs, and is much faster. ROCm is the AMD equivalent. Vulkan and CLBlast work on all GPUs." />
<InfoTooltip label="Select a backend to use to run LLMs. CUDA runs on NVIDIA GPUs and is much faster. ROCm is the AMD equivalent. Vulkan and CLBlast work on all GPUs." />
</Group>
<Select
placeholder={
@ -113,11 +188,18 @@ export const BackendSelector = () => {
<Text size="sm" fw={500}>
GPU Layers
</Text>
<InfoTooltip label="The number of layers to offload to your GPU's VRAM. Ideally the entire LLM should fit inside the VRAM for optimal performance." />
<InfoTooltip label="The number of layers to offload to your GPU's VRAM. When Auto is enabled, this is calculated based on your model size, context size, available VRAM and flash attention settings." />
</Group>
<Group gap="lg" align="center">
<TextInput
value={gpuLayers.toString()}
value={autoGpuLayers ? '' : gpuLayers.toString()}
placeholder={
autoGpuLayers
? isCalculatingLayers
? 'Calculating...'
: gpuLayers.toString()
: undefined
}
onChange={(event) =>
handleGpuLayersChange(Number(event.target.value) || 0)
}
@ -139,7 +221,7 @@ export const BackendSelector = () => {
size="sm"
disabled={backend === 'cpu'}
/>
<InfoTooltip label="Automatically try to allocate the GPU layers based on available VRAM." />
<InfoTooltip label="Automatically calculate optimal GPU layers based on available VRAM. The calculation accounts for model size, context size and flash attention." />
</Group>
</Group>
</div>

View file

@ -45,7 +45,7 @@ export const GeneralTab = () => {
System Tray
</Text>
<Text size="sm" c="dimmed" mb="md">
Add a system tray icon with quick access to launch, eject, and monitor
Add a system tray icon with quick access to launch, eject and monitor
system metrics
</Text>
<Switch

View file

@ -98,9 +98,9 @@ const buildConfigArgs = (isImageMode: boolean, launchArgs: LaunchArgs) => {
const isGpuBackend = launchArgs.backend && launchArgs.backend !== 'cpu';
if (isGpuBackend) {
if (launchArgs.autoGpuLayers) {
args.push('--gpulayers', '-1');
} else if (launchArgs.gpuLayers > 0) {
if (launchArgs.autoGpuLayers && launchArgs.gpuLayers > 0) {
args.push('--gpulayers', launchArgs.gpuLayers.toString());
} else if (!launchArgs.autoGpuLayers && launchArgs.gpuLayers > 0) {
args.push('--gpulayers', launchArgs.gpuLayers.toString());
}
}

View file

@ -76,6 +76,7 @@ import {
isUpdateDownloaded,
canAutoUpdate,
} from '@/main/modules/autoUpdater';
import { calculateOptimalGpuLayers } from '@/utils/node/vram';
export function setupIPCHandlers() {
const mainWindow = getMainWindow();
@ -161,6 +162,23 @@ export function setupIPCHandlers() {
analyzeGGUFModel(filePath)
);
ipcMain.handle(
'kobold:calculateOptimalLayers',
async (
_,
modelPath: string,
contextSize: number,
availableVramGB: number,
flashAttention: boolean
) =>
calculateOptimalGpuLayers({
modelPath,
contextSize,
availableVramGB,
flashAttention,
})
);
ipcMain.handle('config:get', (_, key) => getConfig(key));
ipcMain.on('config:set', (_, key, value) => setConfig(key, value));

View file

@ -53,6 +53,19 @@ const koboldAPI: KoboldAPI = {
ipcRenderer.invoke('kobold:selectModelFile', title),
analyzeModel: (filePath) =>
ipcRenderer.invoke('kobold:analyzeModel', filePath),
calculateOptimalLayers: (
modelPath,
contextSize,
availableVramGB,
flashAttention
) =>
ipcRenderer.invoke(
'kobold:calculateOptimalLayers',
modelPath,
contextSize,
availableVramGB,
flashAttention
),
stopKoboldCpp: () => ipcRenderer.invoke('kobold:stopKoboldCpp'),
onDownloadProgress: (callback) => {
const handler = (_: IpcRendererEvent, progress: number) =>

View file

@ -71,6 +71,15 @@ export interface DownloadItem {
version?: string;
}
export interface OptimalLayersResult {
recommendedLayers: number;
totalLayers: number;
estimatedVramUsageGB: number;
modelVramGB: number;
contextVramGB: number;
headroomGB: number;
}
export interface KoboldConfig {
gpulayers?: number;
contextsize?: number;
@ -148,6 +157,12 @@ export interface KoboldAPI {
parseConfigFile: (filePath: string) => Promise<KoboldConfig | null>;
selectModelFile: (title?: string) => Promise<string | null>;
analyzeModel: (filePath: string) => Promise<ModelAnalysis>;
calculateOptimalLayers: (
modelPath: string,
contextSize: number,
availableVramGB: number,
flashAttention: boolean
) => Promise<OptimalLayersResult>;
stopKoboldCpp: () => void;
onDownloadProgress: (callback: (progress: number) => void) => () => void;
onInstallDirChanged: (callback: (newPath: string) => void) => () => void;

122
src/utils/node/vram.ts Normal file
View file

@ -0,0 +1,122 @@
import { gguf } from '@huggingface/gguf';
import { stat } from 'fs/promises';
interface VramCalculationParams {
modelPath: string;
contextSize: number;
availableVramGB: number;
flashAttention?: boolean;
}
function estimateContextVram(
contextSize: number,
layers: number,
embeddingLength: number,
flashAttention: boolean
) {
const bytesPerElement = 2;
let kvCacheSizeBytes =
2 * contextSize * layers * embeddingLength * bytesPerElement;
if (flashAttention) {
kvCacheSizeBytes *= 0.5;
}
const kvCacheSizeGB = kvCacheSizeBytes / 1024 ** 3;
return kvCacheSizeGB;
}
export async function calculateOptimalGpuLayers({
modelPath,
contextSize,
availableVramGB,
flashAttention = false,
}: VramCalculationParams) {
const isUrl =
modelPath.startsWith('http://') || modelPath.startsWith('https://');
let fileSize: number;
if (isUrl) {
const response = await fetch(modelPath, { method: 'HEAD' });
const contentLength = response.headers.get('content-length');
fileSize = contentLength ? parseInt(contentLength, 10) : 0;
} else {
const stats = await stat(modelPath);
fileSize = stats.size;
}
const multiPartMatch = modelPath.match(/-(\d{5})-of-(\d{5})\./);
if (multiPartMatch) {
const totalParts = parseInt(multiPartMatch[2], 10);
if (totalParts > 1 && totalParts <= 999) {
fileSize *= totalParts;
}
}
const { metadata } = await gguf(modelPath, {
allowLocalFile: !isUrl,
});
const metadataRecord = metadata as Record<string, unknown>;
const architecture =
(metadataRecord['general.architecture'] as string) || 'llama';
const totalLayers =
(metadataRecord[`${architecture}.block_count`] as number) || 32;
const embeddingLength =
(metadataRecord[`${architecture}.embedding_length`] as number) || 4096;
const headCount =
(metadataRecord[`${architecture}.attention.head_count`] as number) || 32;
const headCountKv =
(metadataRecord[`${architecture}.attention.head_count_kv`] as number) ||
headCount;
const headDim = embeddingLength / headCount;
const kvDim = headCountKv * headDim;
const modelSizeGB = fileSize / 1024 ** 3;
const vramPerLayerGB = modelSizeGB / totalLayers;
const headroomGB = 0.1;
const availableForModel = availableVramGB - headroomGB;
let recommendedLayers = 0;
let modelVramGB = 0;
let contextVramGB = 0;
for (let layers = 1; layers <= totalLayers; layers++) {
modelVramGB = layers * vramPerLayerGB;
contextVramGB = estimateContextVram(
contextSize,
layers,
kvDim,
flashAttention
);
const totalVram = modelVramGB + contextVramGB;
if (totalVram <= availableForModel) {
recommendedLayers = layers;
} else {
break;
}
}
const finalContextVram = estimateContextVram(
contextSize,
recommendedLayers,
kvDim,
flashAttention
);
const estimatedVramUsageGB =
recommendedLayers * vramPerLayerGB + finalContextVram;
return {
recommendedLayers,
totalLayers,
estimatedVramUsageGB,
modelVramGB: recommendedLayers * vramPerLayerGB,
contextVramGB: finalContextVram,
headroomGB,
};
}

View file

@ -1,7 +1,7 @@
import type { SystemVersionInfo } from '@/types/electron';
import { PRODUCT_NAME } from '@/constants';
import type { InfoItem } from '@/components/InfoCard';
import type { HardwareInfo } from '@/types/hardware';
import type { HardwareInfo, GPUDevice } from '@/types/hardware';
export const createSoftwareItems = (
versionInfo: SystemVersionInfo,
@ -138,8 +138,16 @@ export const createHardwareItems = (hardwareInfo: HardwareInfo) => [
: 'Detecting...',
},
...(() => {
const discreteGPUs = [];
const discreteGPUs: GPUDevice[] = [];
if (hardwareInfo.gpuCapabilities.cuda.devices.length > 0) {
discreteGPUs.push(
...hardwareInfo.gpuCapabilities.cuda.devices.map((name) => ({
name,
isIntegrated: false,
}))
);
}
if (hardwareInfo.gpuCapabilities.vulkan.devices.length > 0) {
discreteGPUs.push(
...hardwareInfo.gpuCapabilities.vulkan.devices.filter(

154
yarn.lock
View file

@ -720,10 +720,10 @@ __metadata:
languageName: node
linkType: hard
"@eslint/js@npm:9.39.0, @eslint/js@npm:^9.39.0":
version: 9.39.0
resolution: "@eslint/js@npm:9.39.0"
checksum: 10c0/f0ac65784932f1a5d3b9c0db12eb1ff9dcb480dbd03da1045e5da820bd97a35875fb7790f1fbe652763270b1327b770c79a9ba0396e2ad91fbd97822493e67eb
"@eslint/js@npm:9.39.1, @eslint/js@npm:^9.39.1":
version: 9.39.1
resolution: "@eslint/js@npm:9.39.1"
checksum: 10c0/6f7f26f8cdb7ad6327bbf9741973b6278eb946f18f70e35406e88194b0d5c522d0547a34a02f2a208eec95c5d1388cdf7ccb20039efd2e4cb6655615247a50f1
languageName: node
linkType: hard
@ -1472,106 +1472,106 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:^8.46.2":
version: 8.46.2
resolution: "@typescript-eslint/eslint-plugin@npm:8.46.2"
"@typescript-eslint/eslint-plugin@npm:^8.46.3":
version: 8.46.3
resolution: "@typescript-eslint/eslint-plugin@npm:8.46.3"
dependencies:
"@eslint-community/regexpp": "npm:^4.10.0"
"@typescript-eslint/scope-manager": "npm:8.46.2"
"@typescript-eslint/type-utils": "npm:8.46.2"
"@typescript-eslint/utils": "npm:8.46.2"
"@typescript-eslint/visitor-keys": "npm:8.46.2"
"@typescript-eslint/scope-manager": "npm:8.46.3"
"@typescript-eslint/type-utils": "npm:8.46.3"
"@typescript-eslint/utils": "npm:8.46.3"
"@typescript-eslint/visitor-keys": "npm:8.46.3"
graphemer: "npm:^1.4.0"
ignore: "npm:^7.0.0"
natural-compare: "npm:^1.4.0"
ts-api-utils: "npm:^2.1.0"
peerDependencies:
"@typescript-eslint/parser": ^8.46.2
"@typescript-eslint/parser": ^8.46.3
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/24d1257bd023525754dc130e99bad1404c46f997729a060e3764b7f80dd43edcc43767b60fd89244cba82157918609e3922e408d0f7be4223e2056c1447fb387
checksum: 10c0/9c8a5efd9779418d2096634a072a9e2b108e146d0fc541572db56ff28ff37469f03dd404fdb3b0c3161be4cc4857ce14259f30eba1a93d4771de5d1562624e45
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:^8.46.2":
version: 8.46.2
resolution: "@typescript-eslint/parser@npm:8.46.2"
"@typescript-eslint/parser@npm:^8.46.3":
version: 8.46.3
resolution: "@typescript-eslint/parser@npm:8.46.3"
dependencies:
"@typescript-eslint/scope-manager": "npm:8.46.2"
"@typescript-eslint/types": "npm:8.46.2"
"@typescript-eslint/typescript-estree": "npm:8.46.2"
"@typescript-eslint/visitor-keys": "npm:8.46.2"
"@typescript-eslint/scope-manager": "npm:8.46.3"
"@typescript-eslint/types": "npm:8.46.3"
"@typescript-eslint/typescript-estree": "npm:8.46.3"
"@typescript-eslint/visitor-keys": "npm:8.46.3"
debug: "npm:^4.3.4"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/9556bf8ec039c6d1751a37cf76cf70912e80dc45337731a304509309e67472c3f5b5abe6ac5021a7ae9361ea65b2e1f66b626603cecca6506a4533152a77b28f
checksum: 10c0/8a8b47abbbc8bbc68f423df23189afefd296305d50a31c6bec9bdde563adc9ddf99b89a6b8466965fda4aee9118263bae36422dd1c25d7595dd82f8897b5df61
languageName: node
linkType: hard
"@typescript-eslint/project-service@npm:8.46.2":
version: 8.46.2
resolution: "@typescript-eslint/project-service@npm:8.46.2"
"@typescript-eslint/project-service@npm:8.46.3":
version: 8.46.3
resolution: "@typescript-eslint/project-service@npm:8.46.3"
dependencies:
"@typescript-eslint/tsconfig-utils": "npm:^8.46.2"
"@typescript-eslint/types": "npm:^8.46.2"
"@typescript-eslint/tsconfig-utils": "npm:^8.46.3"
"@typescript-eslint/types": "npm:^8.46.3"
debug: "npm:^4.3.4"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/03e87bcbca6af3f95bf54d4047a8b4d12434126c27d7312e804499a9459e1c847fe045f83fe8e3b22c3dc3925baad0aa2a1a5476d0d51f73a493dc5909a53dbf
checksum: 10c0/24ef305bbb550a8e27a7d6377663c1f2773b39b7a9f12c8b95c66c0d15f8150787b036bbff9ae4c2a0a18ab68c62435b0e03889df294bef00b3ae8846cd20659
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.46.2":
version: 8.46.2
resolution: "@typescript-eslint/scope-manager@npm:8.46.2"
"@typescript-eslint/scope-manager@npm:8.46.3":
version: 8.46.3
resolution: "@typescript-eslint/scope-manager@npm:8.46.3"
dependencies:
"@typescript-eslint/types": "npm:8.46.2"
"@typescript-eslint/visitor-keys": "npm:8.46.2"
checksum: 10c0/42f52ee621a3a0ef2233e7d3384d9dbd76218f5c906a9cce3152a1f55c060a3d3614c7b8fff5270bdf48e8fcc003e732d3f003f283ea6fb204d64a2f6bb3ea9c
"@typescript-eslint/types": "npm:8.46.3"
"@typescript-eslint/visitor-keys": "npm:8.46.3"
checksum: 10c0/de8c116477e2a05a895ecd848a8289974a76cab884e07683c8085b3a2ce53895871d9bcd9de94723d6b2a437a6c526c77afcc75d6030cc4f1dccb9b47f4fc069
languageName: node
linkType: hard
"@typescript-eslint/tsconfig-utils@npm:8.46.2, @typescript-eslint/tsconfig-utils@npm:^8.46.2":
version: 8.46.2
resolution: "@typescript-eslint/tsconfig-utils@npm:8.46.2"
"@typescript-eslint/tsconfig-utils@npm:8.46.3, @typescript-eslint/tsconfig-utils@npm:^8.46.3":
version: 8.46.3
resolution: "@typescript-eslint/tsconfig-utils@npm:8.46.3"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/23e34ad296347417e42234945138022fb045d180fde69941483884a38e85fa55d5449420d2a660c0ebf1794a445add2f13e171c8dd64e4e83f594e2c4e35bf4d
checksum: 10c0/a9686141204a96591ee51814a79fa676a8da845638eabb2363f9d82902660fd48ea47f7ec15a618129e45021ad154e1d193127248915752546d60d475d6a566e
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.46.2":
version: 8.46.2
resolution: "@typescript-eslint/type-utils@npm:8.46.2"
"@typescript-eslint/type-utils@npm:8.46.3":
version: 8.46.3
resolution: "@typescript-eslint/type-utils@npm:8.46.3"
dependencies:
"@typescript-eslint/types": "npm:8.46.2"
"@typescript-eslint/typescript-estree": "npm:8.46.2"
"@typescript-eslint/utils": "npm:8.46.2"
"@typescript-eslint/types": "npm:8.46.3"
"@typescript-eslint/typescript-estree": "npm:8.46.3"
"@typescript-eslint/utils": "npm:8.46.3"
debug: "npm:^4.3.4"
ts-api-utils: "npm:^2.1.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/e12fc65e4b58c1ab6fe65f5486265b7afe9a9a6730e3529aca927ddfc22e5913eb28999fc83e68ea1b49097e1edbbae1f61dd724b0bb0e7586fb24ecda1d4938
checksum: 10c0/06e20dff5a22feb6581703e8d35159ad6694d9e1df8fbb75869fcd89893826ca533b7b30b795a16d532e9d8ea6720462b1361d1e7a11d431a4cd11b3f47a22b5
languageName: node
linkType: hard
"@typescript-eslint/types@npm:8.46.2, @typescript-eslint/types@npm:^8.46.2":
version: 8.46.2
resolution: "@typescript-eslint/types@npm:8.46.2"
checksum: 10c0/611716bae2369a1b8001c7f6cc03c5ecadfb956643cbbe27269defd28a61d43fe52eda008d7a09568b0be50c502e8292bf767b246366004283476e9a971b6fbc
"@typescript-eslint/types@npm:8.46.3, @typescript-eslint/types@npm:^8.46.3":
version: 8.46.3
resolution: "@typescript-eslint/types@npm:8.46.3"
checksum: 10c0/6a6ccefbd086e6c38172fe14d04ba27c1c34755af7c25e752547c42d978b91bf6b97da56a5e63d098fbd679b4a5076c4dd4be6c947fd39b4c5feea5fed6deeb6
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:8.46.2":
version: 8.46.2
resolution: "@typescript-eslint/typescript-estree@npm:8.46.2"
"@typescript-eslint/typescript-estree@npm:8.46.3":
version: 8.46.3
resolution: "@typescript-eslint/typescript-estree@npm:8.46.3"
dependencies:
"@typescript-eslint/project-service": "npm:8.46.2"
"@typescript-eslint/tsconfig-utils": "npm:8.46.2"
"@typescript-eslint/types": "npm:8.46.2"
"@typescript-eslint/visitor-keys": "npm:8.46.2"
"@typescript-eslint/project-service": "npm:8.46.3"
"@typescript-eslint/tsconfig-utils": "npm:8.46.3"
"@typescript-eslint/types": "npm:8.46.3"
"@typescript-eslint/visitor-keys": "npm:8.46.3"
debug: "npm:^4.3.4"
fast-glob: "npm:^3.3.2"
is-glob: "npm:^4.0.3"
@ -1580,32 +1580,32 @@ __metadata:
ts-api-utils: "npm:^2.1.0"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/ad7dbf352982bc6e16473ef19fc7d209fffeb147a732db8a2464e0ec33e7fbbc24ce3f23d01bdf99d503626c582a476debf4c90c527d755eeb99b863476d9f5f
checksum: 10c0/3a2bb879a3b42eda478015beee42729efdc78c0cfc70fa009442706626813114f8f9a1e918638ab957df385681ab073cf2076c508973ff9a72e2425e4e521b4f
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.46.2":
version: 8.46.2
resolution: "@typescript-eslint/utils@npm:8.46.2"
"@typescript-eslint/utils@npm:8.46.3":
version: 8.46.3
resolution: "@typescript-eslint/utils@npm:8.46.3"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.7.0"
"@typescript-eslint/scope-manager": "npm:8.46.2"
"@typescript-eslint/types": "npm:8.46.2"
"@typescript-eslint/typescript-estree": "npm:8.46.2"
"@typescript-eslint/scope-manager": "npm:8.46.3"
"@typescript-eslint/types": "npm:8.46.3"
"@typescript-eslint/typescript-estree": "npm:8.46.3"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10c0/600b70730077ed85a6e278e06771f3933cdafce242f979e4af1c1b41290bf1efb14d20823c25c38a3a792def69b18eb9410af28bb228fe86027ad7859753c62d
checksum: 10c0/cf85b166f75c2fd248004fb59643315347489d9ab589738cda1b4c36c25e7947c197a1c21e46cb25959be7d0f310b352c4436f8d3e0a91d64e4fafb3ef4b4e3d
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:8.46.2":
version: 8.46.2
resolution: "@typescript-eslint/visitor-keys@npm:8.46.2"
"@typescript-eslint/visitor-keys@npm:8.46.3":
version: 8.46.3
resolution: "@typescript-eslint/visitor-keys@npm:8.46.3"
dependencies:
"@typescript-eslint/types": "npm:8.46.2"
"@typescript-eslint/types": "npm:8.46.3"
eslint-visitor-keys: "npm:^4.2.1"
checksum: 10c0/2067cd9a3c90b3817242cc49b5fa77428e1b92b28e16a12f45c2b399acbba7bd17e503553e5e68924e40078477a5c247dfa12e7709c24fe11c0b17a0c8486c33
checksum: 10c0/c5f96840e0c31541e1a2390712a6cb290eff59fc97a3ffa7ecab353d3bb3cf0d8c6f62d68db271bf194aa8c4582be735b6121fcc5b30449e01799642be77de6e
languageName: node
linkType: hard
@ -3296,9 +3296,9 @@ __metadata:
languageName: node
linkType: hard
"eslint@npm:^9.39.0":
version: 9.39.0
resolution: "eslint@npm:9.39.0"
"eslint@npm:^9.39.1":
version: 9.39.1
resolution: "eslint@npm:9.39.1"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.8.0"
"@eslint-community/regexpp": "npm:^4.12.1"
@ -3306,7 +3306,7 @@ __metadata:
"@eslint/config-helpers": "npm:^0.4.2"
"@eslint/core": "npm:^0.17.0"
"@eslint/eslintrc": "npm:^3.3.1"
"@eslint/js": "npm:9.39.0"
"@eslint/js": "npm:9.39.1"
"@eslint/plugin-kit": "npm:^0.4.1"
"@humanfs/node": "npm:^0.16.6"
"@humanwhocodes/module-importer": "npm:^1.0.1"
@ -3341,7 +3341,7 @@ __metadata:
optional: true
bin:
eslint: bin/eslint.js
checksum: 10c0/4035f7a3d01b3a6026d0ade899713286ed9514e5b65afe5eb671b66834a4c211769de18d1329dadb4e0bb487ea61e6a5ba0905c2b92144b8b9a2216c1ccca3e8
checksum: 10c0/59b2480639404ba24578ca480f973683b87b7aac8aa7e349240474a39067804fd13cd8b9cb22fee074170b8c7c563b57bab703ec0f0d3f81ea017e5d2cad299d
languageName: node
linkType: hard
@ -3766,7 +3766,7 @@ __metadata:
"@codemirror/search": "npm:^6.5.11"
"@codemirror/theme-one-dark": "npm:^6.1.3"
"@codemirror/view": "npm:^6.38.6"
"@eslint/js": "npm:^9.39.0"
"@eslint/js": "npm:^9.39.1"
"@fontsource/inter": "npm:^5.2.8"
"@huggingface/gguf": "npm:^0.3.2"
"@mantine/core": "npm:^8.3.6"
@ -3775,8 +3775,8 @@ __metadata:
"@types/react": "npm:^19.2.2"
"@types/react-dom": "npm:^19.2.2"
"@types/yauzl": "npm:^2.10.3"
"@typescript-eslint/eslint-plugin": "npm:^8.46.2"
"@typescript-eslint/parser": "npm:^8.46.2"
"@typescript-eslint/eslint-plugin": "npm:^8.46.3"
"@typescript-eslint/parser": "npm:^8.46.3"
"@uiw/react-codemirror": "npm:^4.25.2"
"@vitejs/plugin-react": "npm:^5.1.0"
cross-env: "npm:^10.1.0"
@ -3784,7 +3784,7 @@ __metadata:
electron-builder: "npm:^26.0.12"
electron-updater: "npm:^6.6.2"
electron-vite: "npm:^4.0.1"
eslint: "npm:^9.39.0"
eslint: "npm:^9.39.1"
eslint-plugin-import: "npm:^2.32.0"
eslint-plugin-no-comments: "npm:^1.1.10"
eslint-plugin-promise: "npm:^7.2.1"