mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 09:33:10 -07:00
open all links in electron windows, minor refactors
This commit is contained in:
parent
25971a6127
commit
fe2dfc02c4
18 changed files with 52 additions and 160 deletions
|
|
@ -3,7 +3,6 @@ import globals from 'globals';
|
|||
import tseslint from '@typescript-eslint/eslint-plugin';
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||
import react from 'eslint-plugin-react';
|
||||
import importPlugin from 'eslint-plugin-import';
|
||||
import sonarjs from 'eslint-plugin-sonarjs';
|
||||
|
|
@ -50,7 +49,6 @@ const config = [
|
|||
plugins: {
|
||||
'@typescript-eslint': tseslint,
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
react: react,
|
||||
import: importPlugin,
|
||||
sonarjs: sonarjs,
|
||||
|
|
@ -80,10 +78,6 @@ const config = [
|
|||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
'react/function-component-definition': [
|
||||
'error',
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "gerbil",
|
||||
"productName": "Gerbil",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"description": "Run Large Language Models locally",
|
||||
"main": "out/main/index.js",
|
||||
"homepage": "./",
|
||||
|
|
@ -54,7 +54,6 @@
|
|||
"eslint-plugin-no-comments": "^1.1.10",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
"eslint-plugin-sonarjs": "^3.0.5",
|
||||
"globals": "^16.3.0",
|
||||
"jiti": "^2.5.1",
|
||||
|
|
|
|||
|
|
@ -57,9 +57,10 @@ export const ModelFileField = ({
|
|||
</Button>
|
||||
{showSearchHF && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.electronAPI.app.openExternal(searchUrl);
|
||||
}}
|
||||
component="a"
|
||||
href={searchUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
variant="outline"
|
||||
leftSection={<Search size={16} />}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export const TitleBar = ({
|
|||
const computedColorScheme = useComputedColorScheme('light', {
|
||||
getInitialValueInEffect: false,
|
||||
});
|
||||
const { hasUpdate, openReleasePage } = useAppUpdateChecker();
|
||||
const { hasUpdate, releaseUrl } = useAppUpdateChecker();
|
||||
const { isImageGenerationMode } = useLaunchConfigStore();
|
||||
const [logoClickCount, setLogoClickCount] = useState(0);
|
||||
const [isElephantMode, setIsElephantMode] = useState(false);
|
||||
|
|
@ -214,12 +214,15 @@ export const TitleBar = ({
|
|||
</Box>
|
||||
|
||||
<Group gap="0" style={{ WebkitAppRegion: 'no-drag' }}>
|
||||
{hasUpdate && (
|
||||
{hasUpdate && releaseUrl && (
|
||||
<ActionIcon
|
||||
component="a"
|
||||
href={releaseUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
variant="subtle"
|
||||
color="orange"
|
||||
size={TITLEBAR_HEIGHT}
|
||||
onClick={openReleasePage}
|
||||
aria-label="New release available"
|
||||
tabIndex={-1}
|
||||
style={{
|
||||
|
|
@ -240,6 +243,7 @@ export const TitleBar = ({
|
|||
style={{
|
||||
borderRadius: '0.25rem',
|
||||
margin: 0,
|
||||
outline: 'none',
|
||||
}}
|
||||
>
|
||||
<Settings size="1.25rem" />
|
||||
|
|
|
|||
|
|
@ -101,11 +101,6 @@ export const UpdateAvailableModal = ({
|
|||
href={`https://github.com/${GITHUB_API.KOBOLDCPP_REPO}/releases/tag/v${availableUpdate?.version}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={() =>
|
||||
window.electronAPI.app.openExternal(
|
||||
`https://github.com/${GITHUB_API.KOBOLDCPP_REPO}/releases/tag/v${availableUpdate?.version}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<Group gap={4} align="center">
|
||||
<span>v{availableUpdate?.version}</span>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Card, Text, Title, Loader, Stack, Container } from '@mantine/core';
|
|||
import { DownloadCard } from '@/components/DownloadCard';
|
||||
import { getPlatformDisplayName } from '@/utils/platform';
|
||||
import { formatDownloadSize } from '@/utils/download';
|
||||
import { getAssetDescription, sortDownloadsByType } from '@/utils/assets';
|
||||
import { getAssetDescription } from '@/utils/assets';
|
||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
import type { DownloadItem } from '@/types/electron';
|
||||
|
|
@ -28,8 +28,6 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
|||
|
||||
const loading = loadingPlatform || loadingRemote;
|
||||
|
||||
const sortedDownloads = sortDownloadsByType(availableDownloads);
|
||||
|
||||
const handleDownload = useCallback(
|
||||
async (download: DownloadItem) => {
|
||||
setDownloadingAsset(download.name);
|
||||
|
|
@ -80,7 +78,7 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
|||
<>
|
||||
{availableDownloads.length > 0 ? (
|
||||
<Stack gap="sm">
|
||||
{sortedDownloads.map((download) => {
|
||||
{availableDownloads.map((download) => {
|
||||
const isDownloading =
|
||||
Boolean(downloading) &&
|
||||
downloadingAsset === download.name;
|
||||
|
|
|
|||
|
|
@ -100,12 +100,7 @@ export const AboutTab = () => {
|
|||
<Anchor
|
||||
href="https://github.com/lone-cloud/gerbil"
|
||||
target="_blank"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.electronAPI.app.openExternal(
|
||||
'https://github.com/lone-cloud/gerbil'
|
||||
);
|
||||
}}
|
||||
rel="noopener noreferrer"
|
||||
style={{ textDecoration: 'none' }}
|
||||
>
|
||||
<Group gap="xs" align="center">
|
||||
|
|
|
|||
|
|
@ -227,11 +227,9 @@ export const GeneralTab = () => {
|
|||
{getUnmetRequirements().map((req, index) => (
|
||||
<span key={req.id}>
|
||||
<Anchor
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.electronAPI.app.openExternal(req.url);
|
||||
}}
|
||||
href={req.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
c="red"
|
||||
td="underline"
|
||||
>
|
||||
|
|
@ -266,11 +264,9 @@ export const GeneralTab = () => {
|
|||
{unmetReqs.map((req, index) => (
|
||||
<span key={req.id}>
|
||||
<Anchor
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.electronAPI.app.openExternal(req.url);
|
||||
}}
|
||||
href={req.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
c="orange"
|
||||
td="underline"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -81,9 +81,6 @@ export const SettingsModal = ({
|
|||
position: 'relative',
|
||||
},
|
||||
}}
|
||||
transitionProps={{
|
||||
duration: 200,
|
||||
}}
|
||||
>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,14 @@ import {
|
|||
Stack,
|
||||
Text,
|
||||
Group,
|
||||
Button,
|
||||
Card,
|
||||
Loader,
|
||||
rem,
|
||||
Center,
|
||||
Anchor,
|
||||
} from '@mantine/core';
|
||||
import { RotateCcw, ExternalLink } from 'lucide-react';
|
||||
import { ExternalLink } from 'lucide-react';
|
||||
import { DownloadCard } from '@/components/DownloadCard';
|
||||
import { getAssetDescription, sortDownloadsByType } from '@/utils/assets';
|
||||
import { getAssetDescription } from '@/utils/assets';
|
||||
import {
|
||||
getDisplayNameFromPath,
|
||||
stripAssetExtensions,
|
||||
|
|
@ -43,7 +41,6 @@ export const VersionsTab = () => {
|
|||
loadingRemote,
|
||||
downloading,
|
||||
downloadProgress,
|
||||
loadRemoteVersions,
|
||||
handleDownload: sharedHandleDownload,
|
||||
getLatestReleaseWithDownloadStatus,
|
||||
} = useKoboldVersions();
|
||||
|
|
@ -95,9 +92,7 @@ export const VersionsTab = () => {
|
|||
const versions: VersionInfo[] = [];
|
||||
const processedInstalled = new Set<string>();
|
||||
|
||||
const sortedDownloads = sortDownloadsByType(availableDownloads);
|
||||
|
||||
sortedDownloads.forEach((download) => {
|
||||
availableDownloads.forEach((download) => {
|
||||
const downloadBaseName = stripAssetExtensions(download.name);
|
||||
|
||||
const installedVersion = installedVersions.find((v) => {
|
||||
|
|
@ -230,9 +225,7 @@ export const VersionsTab = () => {
|
|||
}, 'Failed to set current version:');
|
||||
};
|
||||
|
||||
const isLoading = loadingInstalled || loadingPlatform || loadingRemote;
|
||||
|
||||
if (isLoading) {
|
||||
if (loadingInstalled || loadingPlatform || loadingRemote) {
|
||||
return (
|
||||
<Center h="100%">
|
||||
<Stack align="center" gap="md">
|
||||
|
|
@ -252,45 +245,25 @@ export const VersionsTab = () => {
|
|||
return (
|
||||
<>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<div>
|
||||
{latestRelease && (
|
||||
<Group gap="xs">
|
||||
<Text size="sm" c="dimmed">
|
||||
Latest release: {latestRelease.release.tag_name}
|
||||
</Text>
|
||||
<Anchor
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.electronAPI.app.openExternal(
|
||||
latestRelease.release.html_url
|
||||
);
|
||||
}}
|
||||
size="sm"
|
||||
c="blue"
|
||||
>
|
||||
<Group gap={4} align="center">
|
||||
<span>Release notes</span>
|
||||
<ExternalLink size={12} />
|
||||
</Group>
|
||||
</Anchor>
|
||||
</Group>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
variant="subtle"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
loadInstalledVersions();
|
||||
loadRemoteVersions();
|
||||
loadLatestRelease();
|
||||
}}
|
||||
leftSection={
|
||||
<RotateCcw style={{ width: rem(14), height: rem(14) }} />
|
||||
}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
{latestRelease && (
|
||||
<Group gap="xs">
|
||||
<Text size="sm" c="dimmed">
|
||||
Latest release: {latestRelease.release.tag_name}
|
||||
</Text>
|
||||
<Anchor
|
||||
href={latestRelease.release.html_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
size="sm"
|
||||
c="blue"
|
||||
>
|
||||
<Group gap={4} align="center">
|
||||
<span>Release notes</span>
|
||||
<ExternalLink size={12} />
|
||||
</Group>
|
||||
</Anchor>
|
||||
</Group>
|
||||
)}
|
||||
</Group>
|
||||
|
||||
{allVersions.map((version, index) => {
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@ export const KLITE_AUTOSCROLL_PATCHES = `
|
|||
const currentHeight = el.scrollHeight;
|
||||
const lastHeight = lastScrollHeights[id] || currentHeight;
|
||||
|
||||
// Calculate dynamic threshold based on recent growth
|
||||
const heightGrowth = Math.max(0, currentHeight - lastHeight);
|
||||
const dynamicThreshold = Math.min(Math.max(heightGrowth * 1.2, 30), 200);
|
||||
|
||||
|
|
|
|||
|
|
@ -57,12 +57,6 @@ export const useAppUpdateChecker = () => {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const openReleasePage = useCallback(() => {
|
||||
if (updateInfo?.releaseUrl) {
|
||||
window.electronAPI.app.openExternal(updateInfo.releaseUrl);
|
||||
}
|
||||
}, [updateInfo]);
|
||||
|
||||
useEffect(() => {
|
||||
checkForAppUpdates();
|
||||
}, [checkForAppUpdates]);
|
||||
|
|
@ -72,7 +66,7 @@ export const useAppUpdateChecker = () => {
|
|||
isChecking,
|
||||
lastChecked,
|
||||
checkForAppUpdates,
|
||||
openReleasePage,
|
||||
releaseUrl: updateInfo?.releaseUrl,
|
||||
hasUpdate: updateInfo?.hasUpdate || false,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import type {
|
|||
GitHubAsset,
|
||||
InstalledVersion,
|
||||
} from '@/types/electron';
|
||||
import { sortDownloadsByType } from '@/utils/assets';
|
||||
|
||||
interface CachedReleaseData {
|
||||
releases: DownloadItem[];
|
||||
|
|
@ -137,15 +138,7 @@ interface UseKoboldVersionsReturn {
|
|||
loadingRemote: boolean;
|
||||
downloading: string | null;
|
||||
downloadProgress: Record<string, number>;
|
||||
loadRemoteVersions: () => Promise<void>;
|
||||
refresh: () => Promise<void>;
|
||||
handleDownload: (params: HandleDownloadParams) => Promise<boolean>;
|
||||
setDownloading: (value: string | null) => void;
|
||||
setDownloadProgress: (
|
||||
value:
|
||||
| Record<string, number>
|
||||
| ((prev: Record<string, number>) => Record<string, number>)
|
||||
) => void;
|
||||
getLatestReleaseWithDownloadStatus: () => Promise<ReleaseWithStatus | null>;
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +179,7 @@ export const useKoboldVersions = (): UseKoboldVersionsReturn => {
|
|||
if (rocm) {
|
||||
allDownloads.push(rocm);
|
||||
}
|
||||
setAvailableDownloads(allDownloads);
|
||||
setAvailableDownloads(sortDownloadsByType(allDownloads));
|
||||
setLoadingRemote(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -203,7 +196,7 @@ export const useKoboldVersions = (): UseKoboldVersionsReturn => {
|
|||
allDownloads.push(rocm);
|
||||
}
|
||||
|
||||
setAvailableDownloads(allDownloads);
|
||||
setAvailableDownloads(sortDownloadsByType(allDownloads));
|
||||
} catch (err) {
|
||||
error('Failed to load remote versions:', err as Error);
|
||||
const cached = loadFromCache();
|
||||
|
|
@ -213,7 +206,7 @@ export const useKoboldVersions = (): UseKoboldVersionsReturn => {
|
|||
if (rocm) {
|
||||
allDownloads.push(rocm);
|
||||
}
|
||||
setAvailableDownloads(allDownloads);
|
||||
setAvailableDownloads(sortDownloadsByType(allDownloads));
|
||||
}
|
||||
} finally {
|
||||
setLoadingRemote(false);
|
||||
|
|
@ -257,11 +250,6 @@ export const useKoboldVersions = (): UseKoboldVersionsReturn => {
|
|||
[]
|
||||
);
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
localStorage.removeItem(CACHE_KEY);
|
||||
await loadRemoteVersions();
|
||||
}, [loadRemoteVersions]);
|
||||
|
||||
useEffect(() => {
|
||||
loadPlatform();
|
||||
}, [loadPlatform]);
|
||||
|
|
@ -296,11 +284,7 @@ export const useKoboldVersions = (): UseKoboldVersionsReturn => {
|
|||
loadingRemote,
|
||||
downloading,
|
||||
downloadProgress,
|
||||
loadRemoteVersions,
|
||||
refresh,
|
||||
handleDownload,
|
||||
setDownloading,
|
||||
setDownloadProgress,
|
||||
getLatestReleaseWithDownloadStatus,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -169,20 +169,6 @@ export class IPCHandlers {
|
|||
arch: process.arch,
|
||||
}));
|
||||
|
||||
ipcMain.handle('app:openExternal', async (_, url) => {
|
||||
try {
|
||||
await shell.openExternal(url);
|
||||
} catch (error) {
|
||||
this.logManager.logError(
|
||||
'Failed to open external URL:',
|
||||
error as Error
|
||||
);
|
||||
throw new Error(
|
||||
`Failed to open external URL: ${(error as Error).message}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('app:showLogsFolder', async () => {
|
||||
try {
|
||||
const logsDir = this.logManager.getLogsDirectory();
|
||||
|
|
|
|||
|
|
@ -87,20 +87,9 @@ export class WindowManager {
|
|||
}
|
||||
});
|
||||
|
||||
this.mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
const parsedUrl = new URL(url);
|
||||
|
||||
if (
|
||||
parsedUrl.hostname === 'localhost' ||
|
||||
parsedUrl.hostname === '127.0.0.1'
|
||||
) {
|
||||
return { action: 'allow' };
|
||||
}
|
||||
|
||||
shell.openExternal(url);
|
||||
|
||||
return { action: 'deny' };
|
||||
});
|
||||
this.mainWindow.webContents.setWindowOpenHandler(() => ({
|
||||
action: 'allow',
|
||||
}));
|
||||
|
||||
this.mainWindow.on('close', () => {
|
||||
app.quit();
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ const koboldAPI: KoboldAPI = {
|
|||
};
|
||||
|
||||
const appAPI: AppAPI = {
|
||||
openExternal: (url) => ipcRenderer.invoke('app:openExternal', url),
|
||||
showLogsFolder: () => ipcRenderer.invoke('app:showLogsFolder'),
|
||||
getVersion: () => ipcRenderer.invoke('app:getVersion'),
|
||||
getVersionInfo: () => ipcRenderer.invoke('app:getVersionInfo'),
|
||||
|
|
|
|||
1
src/types/electron.d.ts
vendored
1
src/types/electron.d.ts
vendored
|
|
@ -142,7 +142,6 @@ export interface VersionInfo {
|
|||
}
|
||||
|
||||
export interface AppAPI {
|
||||
openExternal: (url: string) => Promise<void>;
|
||||
showLogsFolder: () => Promise<void>;
|
||||
getVersion: () => Promise<string>;
|
||||
getVersionInfo: () => Promise<VersionInfo>;
|
||||
|
|
|
|||
10
yarn.lock
10
yarn.lock
|
|
@ -3083,15 +3083,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-plugin-react-refresh@npm:^0.4.20":
|
||||
version: 0.4.20
|
||||
resolution: "eslint-plugin-react-refresh@npm:0.4.20"
|
||||
peerDependencies:
|
||||
eslint: ">=8.40"
|
||||
checksum: 10c0/2ccf4ba28f1dcbcb9e773e46eae1e61e568bba69281a700eb26fd762152e4e90a78c991f9c8173342a7cd2a82f3f52fedb40a1e81360cef9c40ea5b814fa3613
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-plugin-react@npm:^7.37.5":
|
||||
version: 7.37.5
|
||||
resolution: "eslint-plugin-react@npm:7.37.5"
|
||||
|
|
@ -3654,7 +3645,6 @@ __metadata:
|
|||
eslint-plugin-no-comments: "npm:^1.1.10"
|
||||
eslint-plugin-react: "npm:^7.37.5"
|
||||
eslint-plugin-react-hooks: "npm:^5.2.0"
|
||||
eslint-plugin-react-refresh: "npm:^0.4.20"
|
||||
eslint-plugin-sonarjs: "npm:^3.0.5"
|
||||
execa: "npm:^9.6.0"
|
||||
globals: "npm:^16.3.0"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue