mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-04 12:13:28 -07:00
windows fixes, reformat everything
This commit is contained in:
parent
d04545f5ba
commit
cd54275354
14 changed files with 268 additions and 171 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -10,4 +10,5 @@ Thumbs.db
|
|||
|
||||
# Yarn
|
||||
.yarn/
|
||||
.pnp.*
|
||||
.pnp.*
|
||||
package-lock.json
|
||||
|
|
|
|||
|
|
@ -39,9 +39,11 @@ A koboldcpp manager. <!-- markdownlint-disable MD033 -->
|
|||
yarn dev
|
||||
```
|
||||
|
||||
### Linux Wayland support
|
||||
### Windows ROCm Support
|
||||
|
||||
Additional configurations have been written to help with ideal Wayland support, but as per current Electron guidelines, the user should set `ELECTRON_OZONE_PLATFORM_HINT` to `wayland` in their environment variable according to the [Electron Environment Variables documentation](https://www.electronjs.org/docs/latest/api/environment-variables#electron_ozone_platform_hint-linux).
|
||||
There is ROCm Windows support maintained by YellowRoseCx in a separate fork.
|
||||
Unfortunately it does not properly support unpacking, which would greatly diminish its performance and provide a poor UX when used alongside this app.
|
||||
For Friendly Kobold to work with this fork, this issue must be fixed first: https://github.com/YellowRoseCx/koboldcpp-rocm/issues/129
|
||||
|
||||
### Future features
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
"hipblas",
|
||||
"kcpps",
|
||||
"kcppt",
|
||||
"koboldai",
|
||||
"KoboldAI",
|
||||
"KOBOLDAI",
|
||||
"koboldcpp",
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
"ai",
|
||||
"llm"
|
||||
],
|
||||
"author": "Egor Philippov",
|
||||
"author": "lone-cloud",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
"@cspell/eslint-plugin": "^9.2.0",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import {
|
||||
Card,
|
||||
Text,
|
||||
|
|
@ -6,11 +6,10 @@ import {
|
|||
Loader,
|
||||
Stack,
|
||||
Container,
|
||||
Badge,
|
||||
Anchor,
|
||||
} from '@mantine/core';
|
||||
import { DownloadCard } from '@/components/DownloadCard';
|
||||
import { StyledTooltip } from '@/components/StyledTooltip';
|
||||
import { getPlatformDisplayName, formatFileSizeInMB } from '@/utils';
|
||||
import { getPlatformDisplayName, formatDownloadSize } from '@/utils';
|
||||
import {
|
||||
isAssetRecommended,
|
||||
sortAssetsByRecommendation,
|
||||
|
|
@ -38,6 +37,7 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
|||
'asset' | 'rocm' | null
|
||||
>(null);
|
||||
const [downloadingAsset, setDownloadingAsset] = useState<string | null>(null);
|
||||
const downloadingItemRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const loading = loadingPlatform || loadingRemote;
|
||||
|
||||
|
|
@ -80,31 +80,44 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
|||
[sharedHandleDownload, onDownloadComplete]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (downloading && downloadingItemRef.current) {
|
||||
downloadingItemRef.current.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
});
|
||||
}
|
||||
}, [downloading]);
|
||||
|
||||
const renderROCmCard = () => {
|
||||
if (!rocmDownload) return null;
|
||||
|
||||
const isDownloading = Boolean(downloading) && downloadingType === 'rocm';
|
||||
|
||||
return (
|
||||
<DownloadCard
|
||||
name={rocmDownload.name}
|
||||
size={`~${formatFileSizeInMB(rocmDownload.size)}`}
|
||||
description={getAssetDescription(rocmDownload.name)}
|
||||
version={rocmDownload.version}
|
||||
isRecommended={isAssetRecommended(
|
||||
rocmDownload.name,
|
||||
platformInfo.hasAMDGPU
|
||||
)}
|
||||
isDownloading={Boolean(downloading) && downloadingType === 'rocm'}
|
||||
downloadProgress={
|
||||
downloadingType === 'rocm'
|
||||
? downloadProgress[rocmDownload.name] || 0
|
||||
: 0
|
||||
}
|
||||
disabled={Boolean(downloading) && downloadingType !== 'rocm'}
|
||||
onDownload={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDownload('rocm', rocmDownload);
|
||||
}}
|
||||
/>
|
||||
<div ref={isDownloading ? downloadingItemRef : null}>
|
||||
<DownloadCard
|
||||
name={rocmDownload.name}
|
||||
size={formatDownloadSize(rocmDownload.size, rocmDownload.url, true)}
|
||||
description={getAssetDescription(rocmDownload.name)}
|
||||
version={rocmDownload.version}
|
||||
isRecommended={isAssetRecommended(
|
||||
rocmDownload.name,
|
||||
platformInfo.hasAMDGPU
|
||||
)}
|
||||
isDownloading={isDownloading}
|
||||
downloadProgress={
|
||||
downloadingType === 'rocm'
|
||||
? downloadProgress[rocmDownload.name] || 0
|
||||
: 0
|
||||
}
|
||||
disabled={Boolean(downloading) && downloadingType !== 'rocm'}
|
||||
onDownload={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDownload('rocm', rocmDownload);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -126,40 +139,42 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
|||
<>
|
||||
{availableDownloads.length > 0 ? (
|
||||
<Stack gap="sm">
|
||||
{platformInfo.hasAMDGPU && !platformInfo.hasROCm && (
|
||||
<Card withBorder p="md" bg="orange.0">
|
||||
<Stack gap="xs">
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
}}
|
||||
>
|
||||
<Text fw={600} c="orange.9">
|
||||
AMD GPU Detected
|
||||
</Text>
|
||||
<StyledTooltip
|
||||
label="ROCm is not installed. Install ROCm for optimal AMD GPU performance with KoboldCpp."
|
||||
multiline
|
||||
w={220}
|
||||
{platformInfo.hasAMDGPU &&
|
||||
!platformInfo.hasROCm &&
|
||||
platformInfo.platform === 'linux' && (
|
||||
<Card withBorder p="md" bg="orange.0">
|
||||
<Stack gap="xs">
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
}}
|
||||
>
|
||||
<Badge
|
||||
<Text fw={600} c="orange.9">
|
||||
AMD GPU Detected
|
||||
</Text>
|
||||
</div>
|
||||
<Text size="sm" c="orange.8">
|
||||
For best performance with your AMD GPU,
|
||||
consider installing ROCm support.{' '}
|
||||
<Anchor
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.electronAPI.app.openExternal(
|
||||
'https://rocm.docs.amd.com/projects/install-on-linux/en/latest/reference/system-requirements.html'
|
||||
);
|
||||
}}
|
||||
size="sm"
|
||||
color="orange"
|
||||
variant="filled"
|
||||
c="orange.8"
|
||||
>
|
||||
ROCm Not Found
|
||||
</Badge>
|
||||
</StyledTooltip>
|
||||
</div>
|
||||
<Text size="sm" c="orange.8">
|
||||
For best performance with your AMD GPU, consider
|
||||
installing ROCm support.
|
||||
</Text>
|
||||
</Stack>
|
||||
</Card>
|
||||
)}
|
||||
Learn more
|
||||
</Anchor>
|
||||
</Text>
|
||||
</Stack>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{rocmDownload &&
|
||||
platformInfo.hasAMDGPU &&
|
||||
|
|
@ -168,39 +183,47 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
|||
{sortAssetsByRecommendation(
|
||||
regularDownloads,
|
||||
platformInfo.hasAMDGPU
|
||||
).map((download) => (
|
||||
<DownloadCard
|
||||
key={download.name}
|
||||
name={download.name}
|
||||
size={formatFileSizeInMB(download.size)}
|
||||
version={download.version}
|
||||
description={getAssetDescription(download.name)}
|
||||
isRecommended={isAssetRecommended(
|
||||
download.name,
|
||||
platformInfo.hasAMDGPU
|
||||
)}
|
||||
isDownloading={
|
||||
Boolean(downloading) &&
|
||||
downloadingType === 'asset' &&
|
||||
downloadingAsset === download.name
|
||||
}
|
||||
downloadProgress={
|
||||
Boolean(downloading) &&
|
||||
downloadingType === 'asset' &&
|
||||
downloadingAsset === download.name
|
||||
? downloadProgress[download.name] || 0
|
||||
: 0
|
||||
}
|
||||
disabled={
|
||||
Boolean(downloading) &&
|
||||
downloadingAsset !== download.name
|
||||
}
|
||||
onDownload={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDownload('asset', download);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
).map((download) => {
|
||||
const isDownloading =
|
||||
Boolean(downloading) &&
|
||||
downloadingType === 'asset' &&
|
||||
downloadingAsset === download.name;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={download.name}
|
||||
ref={isDownloading ? downloadingItemRef : null}
|
||||
>
|
||||
<DownloadCard
|
||||
name={download.name}
|
||||
size={formatDownloadSize(
|
||||
download.size,
|
||||
download.url
|
||||
)}
|
||||
version={download.version}
|
||||
description={getAssetDescription(download.name)}
|
||||
isRecommended={isAssetRecommended(
|
||||
download.name,
|
||||
platformInfo.hasAMDGPU
|
||||
)}
|
||||
isDownloading={isDownloading}
|
||||
downloadProgress={
|
||||
isDownloading
|
||||
? downloadProgress[download.name] || 0
|
||||
: 0
|
||||
}
|
||||
disabled={
|
||||
Boolean(downloading) &&
|
||||
downloadingAsset !== download.name
|
||||
}
|
||||
onDownload={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDownload('asset', download);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{rocmDownload &&
|
||||
!platformInfo.hasAMDGPU &&
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||
import {
|
||||
Stack,
|
||||
Text,
|
||||
|
|
@ -17,35 +17,15 @@ import {
|
|||
sortAssetsByRecommendation,
|
||||
isAssetRecommended,
|
||||
} from '@/utils/assets';
|
||||
import { getDisplayNameFromPath, formatFileSizeInMB } from '@/utils';
|
||||
import {
|
||||
getDisplayNameFromPath,
|
||||
formatDownloadSize,
|
||||
stripAssetExtensions,
|
||||
compareVersions,
|
||||
} from '@/utils';
|
||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||
import type { InstalledVersion, ReleaseWithStatus } from '@/types/electron';
|
||||
|
||||
const compareVersions = (versionA: string, versionB: string): number => {
|
||||
const cleanVersion = (version: string): string =>
|
||||
version.replace(/^v/, '').replace(/[^0-9.]/g, '');
|
||||
|
||||
const parseVersion = (version: string): number[] =>
|
||||
cleanVersion(version)
|
||||
.split('.')
|
||||
.map((num) => parseInt(num, 10) || 0);
|
||||
|
||||
const a = parseVersion(versionA);
|
||||
const b = parseVersion(versionB);
|
||||
const maxLength = Math.max(a.length, b.length);
|
||||
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const aVal = a[i] || 0;
|
||||
const bVal = b[i] || 0;
|
||||
|
||||
if (aVal !== bVal) {
|
||||
return aVal - bVal;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
interface VersionInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
|
|
@ -81,6 +61,7 @@ export const VersionsTab = () => {
|
|||
const [latestRelease, setLatestRelease] = useState<ReleaseWithStatus | null>(
|
||||
null
|
||||
);
|
||||
const downloadingItemRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const loadInstalledVersions = useCallback(async () => {
|
||||
setLoadingInstalled(true);
|
||||
|
|
@ -146,14 +127,12 @@ export const VersionsTab = () => {
|
|||
loadLatestRelease();
|
||||
}, [loadInstalledVersions, loadLatestRelease]);
|
||||
|
||||
const getAllVersions = (): VersionInfo[] => {
|
||||
const allVersions = useMemo((): VersionInfo[] => {
|
||||
const versions: VersionInfo[] = [];
|
||||
const processedInstalled = new Set<string>();
|
||||
|
||||
availableDownloads.forEach((download) => {
|
||||
const downloadBaseName = download.name
|
||||
.replace(/\.(tar\.gz|zip|exe)$/i, '')
|
||||
.replace(/\.packed$/, '');
|
||||
const downloadBaseName = stripAssetExtensions(download.name);
|
||||
|
||||
const installedVersion = installedVersions.find((v) => {
|
||||
const displayName = getDisplayNameFromPath(v);
|
||||
|
|
@ -220,7 +199,21 @@ export const VersionsTab = () => {
|
|||
});
|
||||
|
||||
return sortAssetsByRecommendation(versions, platformInfo.hasAMDGPU);
|
||||
};
|
||||
}, [
|
||||
availableDownloads,
|
||||
installedVersions,
|
||||
currentVersion,
|
||||
platformInfo.hasAMDGPU,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (downloading && downloadingItemRef.current) {
|
||||
downloadingItemRef.current.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
});
|
||||
}
|
||||
}, [downloading]);
|
||||
|
||||
const handleDownload = async (version: VersionInfo) => {
|
||||
try {
|
||||
|
|
@ -362,21 +355,24 @@ export const VersionsTab = () => {
|
|||
</Button>
|
||||
</Group>
|
||||
|
||||
{getAllVersions().map((version, index) => {
|
||||
{allVersions.map((version, index) => {
|
||||
const isDownloading = downloading === version.name;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`${version.name}-${version.version}-${index}`}
|
||||
style={{ paddingBottom: '8px' }}
|
||||
ref={isDownloading ? downloadingItemRef : null}
|
||||
>
|
||||
<DownloadCard
|
||||
name={version.name}
|
||||
size={
|
||||
version.size
|
||||
? version.isROCm
|
||||
? `~${formatFileSizeInMB(version.size)}`
|
||||
: formatFileSizeInMB(version.size)
|
||||
? formatDownloadSize(
|
||||
version.size,
|
||||
version.downloadUrl,
|
||||
version.isROCm
|
||||
)
|
||||
: ''
|
||||
}
|
||||
version={version.version}
|
||||
|
|
@ -418,7 +414,7 @@ export const VersionsTab = () => {
|
|||
);
|
||||
})}
|
||||
|
||||
{getAllVersions().length === 0 && (
|
||||
{allVersions.length === 0 && (
|
||||
<Card withBorder radius="md" padding="md">
|
||||
<Text size="sm" c="dimmed" ta="center">
|
||||
No versions found
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ export const ASSET_SUFFIXES = {
|
|||
export const KOBOLDAI_URLS = {
|
||||
STANDARD_DOWNLOAD: 'https://koboldai.org/cpp',
|
||||
ROCM_DOWNLOAD: 'https://koboldai.org/cpplinuxrocm',
|
||||
DOMAIN: 'koboldai.org',
|
||||
} as const;
|
||||
|
||||
export const GITHUB_URLS = {
|
||||
DOMAIN: 'github.com',
|
||||
} as const;
|
||||
|
||||
export const ROCM = {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable no-comments/disallowComments */
|
||||
import { spawn, ChildProcess } from 'child_process';
|
||||
import { join } from 'path';
|
||||
import {
|
||||
|
|
@ -16,7 +17,8 @@ import { GitHubService } from '@/main/services/GitHubService';
|
|||
import { ConfigManager } from '@/main/managers/ConfigManager';
|
||||
import { LogManager } from '@/main/managers/LogManager';
|
||||
import { WindowManager } from '@/main/managers/WindowManager';
|
||||
import { ROCM, GITHUB_API } from '@/constants';
|
||||
import { ROCM } from '@/constants';
|
||||
import { stripAssetExtensions } from '@/utils/versionUtils';
|
||||
import type { DownloadItem } from '@/types/electron';
|
||||
|
||||
interface GitHubAsset {
|
||||
|
|
@ -85,7 +87,7 @@ export class KoboldCppManager {
|
|||
onProgress?: (progress: number) => void
|
||||
): Promise<string> {
|
||||
const tempPackedFilePath = join(this.installDir, `${asset.name}.packed`);
|
||||
const baseFilename = asset.name.replace(/\.exe$/, '');
|
||||
const baseFilename = stripAssetExtensions(asset.name);
|
||||
const unpackedDirPath = join(this.installDir, baseFilename);
|
||||
|
||||
if (asset.isUpdate && existsSync(unpackedDirPath)) {
|
||||
|
|
@ -666,32 +668,35 @@ export class KoboldCppManager {
|
|||
type: 'rocm',
|
||||
};
|
||||
} else if (platform === 'win32') {
|
||||
try {
|
||||
const response = await fetch(GITHUB_API.ROCM_LATEST_RELEASE_URL);
|
||||
if (!response.ok) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
// The launcher doesn't exist in unpacked state yet.
|
||||
// Enable when it's ready.
|
||||
// try {
|
||||
// const response = await fetch(GITHUB_API.ROCM_LATEST_RELEASE_URL);
|
||||
// if (!response.ok) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
const release = await response.json();
|
||||
const rocmAsset = release.assets?.find((asset: GitHubAsset) =>
|
||||
asset.name.endsWith('rocm.exe')
|
||||
);
|
||||
// const release = await response.json();
|
||||
// const rocmAsset = release.assets?.find((asset: GitHubAsset) =>
|
||||
// asset.name.endsWith('rocm.exe')
|
||||
// );
|
||||
|
||||
if (rocmAsset) {
|
||||
return {
|
||||
name: rocmAsset.name,
|
||||
url: rocmAsset.browser_download_url,
|
||||
size: rocmAsset.size,
|
||||
version: release.tag_name?.replace(/^v/, '') || 'unknown',
|
||||
type: 'rocm',
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
this.logManager.logError(
|
||||
'Failed to fetch Windows ROCm release:',
|
||||
error as Error
|
||||
);
|
||||
}
|
||||
// if (rocmAsset) {
|
||||
// return {
|
||||
// name: rocmAsset.name,
|
||||
// url: rocmAsset.browser_download_url,
|
||||
// size: rocmAsset.size,
|
||||
// version: release.tag_name?.replace(/^v/, '') || 'unknown',
|
||||
// type: 'rocm',
|
||||
// };
|
||||
// }
|
||||
// } catch (error) {
|
||||
// this.logManager.logError(
|
||||
// 'Failed to fetch Windows ROCm release:',
|
||||
// error as Error
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -715,7 +720,7 @@ export class KoboldCppManager {
|
|||
this.installDir,
|
||||
`${rocmInfo.name}.packed`
|
||||
);
|
||||
const baseFilename = rocmInfo.name.replace(/\.exe$/, '');
|
||||
const baseFilename = stripAssetExtensions(rocmInfo.name);
|
||||
const unpackedDirPath = join(this.installDir, baseFilename);
|
||||
|
||||
const response = await fetch(rocmInfo.url);
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ export class WindowManager {
|
|||
},
|
||||
},
|
||||
{
|
||||
label: 'Open Logs Directory',
|
||||
label: 'View Error Logs',
|
||||
click: () => {
|
||||
const logsDir = join(app.getPath('userData'), 'logs');
|
||||
shell.openPath(logsDir);
|
||||
|
|
|
|||
|
|
@ -37,21 +37,18 @@ export class BinaryService {
|
|||
const binaryDir = dirname(koboldBinaryPath);
|
||||
const internalDir = join(binaryDir, '_internal');
|
||||
|
||||
if (!existsSync(internalDir)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
'_internal directory not found, cannot detect backend support'
|
||||
);
|
||||
this.backendSupportCache.set(koboldBinaryPath, support);
|
||||
return support;
|
||||
}
|
||||
|
||||
const platform = process.platform;
|
||||
const isDynamicLib = (name: string) => {
|
||||
if (platform === 'win32') {
|
||||
return existsSync(join(internalDir, `${name}.dll`));
|
||||
return (
|
||||
existsSync(join(internalDir, `${name}.dll`)) ||
|
||||
existsSync(join(binaryDir, `${name}.dll`))
|
||||
);
|
||||
} else {
|
||||
return existsSync(join(internalDir, `${name}.so`));
|
||||
return (
|
||||
existsSync(join(internalDir, `${name}.so`)) ||
|
||||
existsSync(join(binaryDir, `${name}.so`))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { ASSET_SUFFIXES } from '@/constants';
|
||||
import { stripAssetExtensions } from '@/utils/versionUtils';
|
||||
|
||||
export const getAssetDescription = (assetName: string): string => {
|
||||
const name = assetName.toLowerCase();
|
||||
const name = stripAssetExtensions(assetName).toLowerCase();
|
||||
|
||||
if (name.includes(ASSET_SUFFIXES.ROCM)) {
|
||||
return 'Optimized for AMD GPUs with ROCm support.';
|
||||
|
|
@ -22,7 +23,7 @@ export const isAssetRecommended = (
|
|||
assetName: string,
|
||||
hasAMDGPU: boolean
|
||||
): boolean => {
|
||||
const name = assetName.toLowerCase();
|
||||
const name = stripAssetExtensions(assetName).toLowerCase();
|
||||
|
||||
if (hasAMDGPU && name.includes(ASSET_SUFFIXES.ROCM)) {
|
||||
return true;
|
||||
|
|
@ -36,6 +37,16 @@ export const isAssetRecommended = (
|
|||
);
|
||||
};
|
||||
|
||||
export const isAssetStandard = (assetName: string): boolean => {
|
||||
const name = stripAssetExtensions(assetName).toLowerCase();
|
||||
|
||||
return (
|
||||
!name.includes(ASSET_SUFFIXES.ROCM) &&
|
||||
!name.endsWith(ASSET_SUFFIXES.OLDPC) &&
|
||||
!name.endsWith(ASSET_SUFFIXES.NOCUDA)
|
||||
);
|
||||
};
|
||||
|
||||
export const sortAssetsByRecommendation = <T extends { name: string }>(
|
||||
assets: T[],
|
||||
hasAMDGPU: boolean
|
||||
|
|
@ -43,9 +54,16 @@ export const sortAssetsByRecommendation = <T extends { name: string }>(
|
|||
[...assets].sort((a, b) => {
|
||||
const aRecommended = isAssetRecommended(a.name, hasAMDGPU);
|
||||
const bRecommended = isAssetRecommended(b.name, hasAMDGPU);
|
||||
const aStandard = isAssetStandard(a.name);
|
||||
const bStandard = isAssetStandard(b.name);
|
||||
|
||||
if (aRecommended && !bRecommended) return -1;
|
||||
if (!aRecommended && bRecommended) return 1;
|
||||
|
||||
if (aRecommended === bRecommended) {
|
||||
if (aStandard && !bStandard) return -1;
|
||||
if (!aStandard && bStandard) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
|
|
|||
43
src/utils/downloadUtils.ts
Normal file
43
src/utils/downloadUtils.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { formatFileSizeInMB } from '@/utils/fileSize';
|
||||
import { KOBOLDAI_URLS, GITHUB_URLS } from '@/constants';
|
||||
|
||||
export const formatDownloadSize = (
|
||||
size: number,
|
||||
url?: string,
|
||||
isROCm?: boolean
|
||||
): string => {
|
||||
if (!size) return '';
|
||||
|
||||
const isApproximateSize =
|
||||
url?.includes(KOBOLDAI_URLS.DOMAIN) ||
|
||||
(isROCm && !url?.includes(GITHUB_URLS.DOMAIN));
|
||||
|
||||
return isApproximateSize
|
||||
? `~${formatFileSizeInMB(size)}`
|
||||
: formatFileSizeInMB(size);
|
||||
};
|
||||
|
||||
export const compareVersions = (versionA: string, versionB: string): number => {
|
||||
const cleanVersion = (version: string): string =>
|
||||
version.replace(/^v/, '').replace(/[^0-9.]/g, '');
|
||||
|
||||
const parseVersion = (version: string): number[] =>
|
||||
cleanVersion(version)
|
||||
.split('.')
|
||||
.map((num) => parseInt(num, 10) || 0);
|
||||
|
||||
const a = parseVersion(versionA);
|
||||
const b = parseVersion(versionB);
|
||||
const maxLength = Math.max(a.length, b.length);
|
||||
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const aVal = a[i] || 0;
|
||||
const bVal = b[i] || 0;
|
||||
|
||||
if (aVal !== bVal) {
|
||||
return aVal - bVal;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
export * from './assets';
|
||||
export * from './downloadUtils';
|
||||
export * from './fileSize';
|
||||
export * from './imageModelPresets';
|
||||
export * from './nullish';
|
||||
|
|
|
|||
|
|
@ -14,3 +14,8 @@ export const getDisplayNameFromPath = (
|
|||
|
||||
return installedVersion.filename;
|
||||
};
|
||||
|
||||
export const stripAssetExtensions = (assetName: string): string =>
|
||||
assetName
|
||||
.replace(/\.(tar\.gz|zip|exe|dmg|AppImage)$/i, '')
|
||||
.replace(/\.packed$/, '');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue