mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 09:33:10 -07:00
custom frontend users will require to pre-install Node.js, welcome screen clarifications, don't package mac entitlements for other platforms
This commit is contained in:
parent
a559f8c86d
commit
1b392be5f0
29 changed files with 323 additions and 1231 deletions
|
|
@ -1,7 +1,7 @@
|
|||
# Friendly Kobold
|
||||
|
||||
A desktop app for running Large Language Models locally. <!-- markdownlint-disable MD033 -->
|
||||
<img src="assets/icon.png" alt="FriendlyKobold Icon" width="32" height="32">
|
||||
<img src="src/assets/icon.png" alt="FriendlyKobold Icon" width="32" height="32">
|
||||
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default defineConfig({
|
|||
},
|
||||
renderer: {
|
||||
root: '.',
|
||||
publicDir: 'assets',
|
||||
publicDir: 'src/assets',
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
rollupOptions: {
|
||||
|
|
|
|||
29
package.json
29
package.json
|
|
@ -85,7 +85,6 @@
|
|||
"execa": "^9.6.0",
|
||||
"got": "^14.4.7",
|
||||
"lucide-react": "^0.542.0",
|
||||
"npm": "^11.5.2",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"systeminformation": "^5.27.8",
|
||||
|
|
@ -97,7 +96,7 @@
|
|||
"appId": "com.friendly-kobold.app",
|
||||
"productName": "Friendly Kobold",
|
||||
"compression": "normal",
|
||||
"icon": "assets/icon.png",
|
||||
"icon": "src/assets/icon.png",
|
||||
"publish": null,
|
||||
"electronLanguages": [
|
||||
"en-US"
|
||||
|
|
@ -107,18 +106,18 @@
|
|||
},
|
||||
"files": [
|
||||
"out/**/*",
|
||||
"dist/**/*",
|
||||
"assets/**/*"
|
||||
"dist/**/*"
|
||||
],
|
||||
"extraFiles": [
|
||||
{
|
||||
"from": "assets/friendly-kobold.desktop",
|
||||
"to": "assets/friendly-kobold.desktop",
|
||||
"filter": [
|
||||
"**/*"
|
||||
]
|
||||
}
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "assets",
|
||||
"to": "assets",
|
||||
"filter": [
|
||||
"**/*",
|
||||
"!screenshots/**/*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"from": "src/main/templates",
|
||||
"to": "templates",
|
||||
|
|
@ -134,6 +133,12 @@
|
|||
"gatekeeperAssess": false,
|
||||
"entitlements": "assets/entitlements.mac.plist",
|
||||
"entitlementsInherit": "assets/entitlements.mac.plist",
|
||||
"extraFiles": [
|
||||
{
|
||||
"from": "assets/entitlements.mac.plist",
|
||||
"to": "assets/entitlements.mac.plist"
|
||||
}
|
||||
],
|
||||
"target": [
|
||||
{
|
||||
"target": "dmg",
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 235 KiB |
|
|
@ -11,7 +11,7 @@ import {
|
|||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { Settings, ArrowLeft } from 'lucide-react';
|
||||
import { soundAssets, playSound, initializeAudio } from '@/utils';
|
||||
import { soundAssets, playSound, initializeAudio } from '@/utils/sounds';
|
||||
import iconUrl from '/icon.png';
|
||||
import { FRONTENDS } from '@/constants';
|
||||
import type { InterfaceTab, FrontendPreference, Screen } from '@/types';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Group, TextInput, Button } from '@mantine/core';
|
||||
import { File, Search } from 'lucide-react';
|
||||
import { LabelWithTooltip } from '@/components/LabelWithTooltip';
|
||||
import { getInputValidationState } from '@/utils';
|
||||
import { getInputValidationState } from '@/utils/validation';
|
||||
import styles from '@/styles/layout.module.css';
|
||||
|
||||
interface ModelFileFieldProps {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import { Card, Text, Title, Loader, Stack, Container } from '@mantine/core';
|
||||
import { DownloadCard } from '@/components/DownloadCard';
|
||||
import { getPlatformDisplayName, formatDownloadSize } from '@/utils';
|
||||
import { getPlatformDisplayName } from '@/utils/platform';
|
||||
import { formatDownloadSize } from '@/utils/downloadUtils';
|
||||
import { getAssetDescription, sortDownloadsByType } from '@/utils/assets';
|
||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||
import type { DownloadItem } from '@/types/electron';
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import {
|
|||
import { ChevronDown } from 'lucide-react';
|
||||
import styles from '@/styles/layout.module.css';
|
||||
import { UI } from '@/constants';
|
||||
import { handleTerminalOutput, processTerminalContent } from '@/utils';
|
||||
import { handleTerminalOutput } from '@/utils/terminal';
|
||||
import { processTerminalContent } from '@/utils/linkifyTerminal';
|
||||
import { useLaunchConfigStore } from '@/stores/launchConfigStore';
|
||||
|
||||
interface TerminalTabProps {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Stack } from '@mantine/core';
|
|||
import { useState } from 'react';
|
||||
import { ModelFileField } from '@/components/ModelFileField';
|
||||
import { SelectWithTooltip } from '@/components/SelectWithTooltip';
|
||||
import { IMAGE_MODEL_PRESETS } from '@/utils';
|
||||
import { IMAGE_MODEL_PRESETS } from '@/constants/imageModelPresets';
|
||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||
|
||||
export const ImageGenerationTab = () => {
|
||||
|
|
|
|||
|
|
@ -73,7 +73,22 @@ export const WelcomeScreen = ({ onGetStarted }: WelcomeScreenProps) => (
|
|||
- Use powerful AI models for free once downloaded
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text>
|
||||
<Text component="span" fw={500}>
|
||||
Hardware acceleration
|
||||
</Text>{' '}
|
||||
- Supports CUDA, ROCm, Vulkan, and CLBlast backends for faster
|
||||
inference
|
||||
</Text>
|
||||
</List.Item>
|
||||
</List>
|
||||
|
||||
<Text size="xs" c="dimmed" ta="center" mt="sm">
|
||||
Note: Hardware acceleration requires appropriate drivers to be
|
||||
manually installed by the user (CUDA for NVIDIA GPUs, ROCm for AMD
|
||||
GPUs, etc.)
|
||||
</Text>
|
||||
</Stack>
|
||||
|
||||
<Button size="lg" onClick={onGetStarted}>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
Button,
|
||||
rem,
|
||||
Select,
|
||||
Anchor,
|
||||
} from '@mantine/core';
|
||||
import { Folder, FolderOpen, Monitor } from 'lucide-react';
|
||||
import styles from '@/styles/layout.module.css';
|
||||
|
|
@ -17,12 +18,50 @@ export const GeneralTab = () => {
|
|||
const [installDir, setInstallDir] = useState<string>('');
|
||||
const [FrontendPreference, setFrontendPreference] =
|
||||
useState<FrontendPreference>('koboldcpp');
|
||||
const [isNpxAvailable, setIsNpxAvailable] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
loadCurrentInstallDir();
|
||||
loadFrontendPreference();
|
||||
const initialize = async () => {
|
||||
await Promise.all([loadCurrentInstallDir(), loadFrontendPreference()]);
|
||||
};
|
||||
initialize();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const checkNpxAvailability = async () => {
|
||||
try {
|
||||
const available = await window.electronAPI.sillytavern.isNpxAvailable();
|
||||
setIsNpxAvailable(available);
|
||||
|
||||
if (!available && FrontendPreference === 'sillytavern') {
|
||||
await window.electronAPI.config.set(
|
||||
'frontendPreference',
|
||||
'koboldcpp'
|
||||
);
|
||||
setFrontendPreference('koboldcpp');
|
||||
}
|
||||
} catch (error) {
|
||||
window.electronAPI.logs.logError(
|
||||
'Failed to check npx availability:',
|
||||
error as Error
|
||||
);
|
||||
setIsNpxAvailable(false);
|
||||
|
||||
if (FrontendPreference === 'sillytavern') {
|
||||
await window.electronAPI.config.set(
|
||||
'frontendPreference',
|
||||
'koboldcpp'
|
||||
);
|
||||
setFrontendPreference('koboldcpp');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (FrontendPreference) {
|
||||
checkNpxAvailability();
|
||||
}
|
||||
}, [FrontendPreference]);
|
||||
|
||||
const loadCurrentInstallDir = async () => {
|
||||
try {
|
||||
const currentDir = await window.electronAPI.kobold.getCurrentInstallDir();
|
||||
|
|
@ -112,12 +151,33 @@ export const GeneralTab = () => {
|
|||
<Text fw={500} mb="sm">
|
||||
Frontend Interface
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" mb="md">
|
||||
Choose which frontend interface to use for interacting with models
|
||||
</Text>
|
||||
{isNpxAvailable && (
|
||||
<Text size="sm" c="dimmed" mb="md">
|
||||
Choose which frontend interface to use for interacting with models
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{!isNpxAvailable && (
|
||||
<Text size="sm" c="red" mb="md">
|
||||
Custom frontends require{' '}
|
||||
<Anchor
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.electronAPI.app.openExternal('https://nodejs.org/');
|
||||
}}
|
||||
c="red"
|
||||
td="underline"
|
||||
>
|
||||
Node.js
|
||||
</Anchor>{' '}
|
||||
to be installed on your system
|
||||
</Text>
|
||||
)}
|
||||
<Select
|
||||
value={FrontendPreference}
|
||||
onChange={handleFrontendPreferenceChange}
|
||||
disabled={!isNpxAvailable}
|
||||
data={[
|
||||
{ value: 'koboldcpp', label: 'KoboldCpp (Built-in)' },
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,10 +15,9 @@ import { DownloadCard } from '@/components/DownloadCard';
|
|||
import { getAssetDescription, sortDownloadsByType } from '@/utils/assets';
|
||||
import {
|
||||
getDisplayNameFromPath,
|
||||
formatDownloadSize,
|
||||
stripAssetExtensions,
|
||||
compareVersions,
|
||||
} from '@/utils';
|
||||
} from '@/utils/versionUtils';
|
||||
import { formatDownloadSize, compareVersions } from '@/utils/downloadUtils';
|
||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||
import type { InstalledVersion, ReleaseWithStatus } from '@/types/electron';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useLaunchConfigStore } from '@/stores/launchConfigStore';
|
||||
import {
|
||||
IMAGE_MODEL_PRESETS,
|
||||
type ImageModelPreset,
|
||||
} from '@/utils/imageModelPresets';
|
||||
IMAGE_MODEL_PRESETS,
|
||||
} from '@/constants/imageModelPresets';
|
||||
|
||||
export const useLaunchConfig = () => {
|
||||
const state = useLaunchConfigStore();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { getDisplayNameFromPath } from '@/utils/versionUtils';
|
||||
import { compareVersions } from '@/utils';
|
||||
import { compareVersions } from '@/utils/downloadUtils';
|
||||
import type { InstalledVersion, DownloadItem } from '@/types/electron';
|
||||
|
||||
interface UpdateInfo {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export class IPCHandlers {
|
|||
await this.sillyTavernManager.startFrontend(args);
|
||||
} catch (error) {
|
||||
this.logManager.logError(
|
||||
'Failed to setup SillyTavern text frontend:',
|
||||
'Failed to setup SillyTavern:',
|
||||
error as Error
|
||||
);
|
||||
}
|
||||
|
|
@ -247,5 +247,9 @@ export class IPCHandlers {
|
|||
this.logManager.logError(message, error);
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.handle('sillytavern:isNpxAvailable', () =>
|
||||
this.sillyTavernManager.isNpxAvailable()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import { LogManager } from '@/main/managers/LogManager';
|
|||
import { WindowManager } from '@/main/managers/WindowManager';
|
||||
import { ROCM, PRODUCT_NAME } from '@/constants';
|
||||
import { stripAssetExtensions } from '@/utils/versionUtils';
|
||||
import { compareVersions } from '@/utils';
|
||||
import { compareVersions } from '@/utils/downloadUtils';
|
||||
import type {
|
||||
DownloadItem,
|
||||
GitHubAsset,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { spawn } from 'child_process';
|
||||
import { createServer, request, type Server } from 'http';
|
||||
import { app } from 'electron';
|
||||
import { homedir } from 'os';
|
||||
import { join } from 'path';
|
||||
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
||||
|
|
@ -9,7 +8,6 @@ import type { ChildProcess } from 'child_process';
|
|||
import { LogManager } from './LogManager';
|
||||
import { WindowManager } from './WindowManager';
|
||||
import { SILLYTAVERN } from '@/constants';
|
||||
import { getPlatformPathFromWindowsPath } from '@/utils/server';
|
||||
|
||||
export interface SillyTavernConfig {
|
||||
name: string;
|
||||
|
|
@ -79,6 +77,31 @@ export class SillyTavernManager {
|
|||
}
|
||||
}
|
||||
|
||||
async isNpxAvailable(): Promise<boolean> {
|
||||
try {
|
||||
const testProcess = spawn('npx', ['--version'], { stdio: 'pipe' });
|
||||
|
||||
return new Promise<boolean>((resolve) => {
|
||||
const timeout = setTimeout(() => {
|
||||
testProcess.kill();
|
||||
resolve(false);
|
||||
}, 5000);
|
||||
|
||||
testProcess.on('exit', (code) => {
|
||||
clearTimeout(timeout);
|
||||
resolve(code === 0);
|
||||
});
|
||||
|
||||
testProcess.on('error', () => {
|
||||
clearTimeout(timeout);
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async ensureSillyTavernSettings(): Promise<void> {
|
||||
const settingsPath = this.getSillyTavernSettingsPath();
|
||||
|
||||
|
|
@ -91,22 +114,17 @@ export class SillyTavernManager {
|
|||
'SillyTavern settings not found, starting SillyTavern briefly to generate config...'
|
||||
);
|
||||
|
||||
const bundledNpxPath = getPlatformPathFromWindowsPath(
|
||||
app.getAppPath(),
|
||||
'npx.cmd'
|
||||
const spawnArgs = ['sillytavern', '--browserLaunchEnabled', 'false'];
|
||||
|
||||
this.windowManager.sendKoboldOutput(
|
||||
`Running command: npx ${spawnArgs.join(' ')}`
|
||||
);
|
||||
|
||||
this.windowManager.sendKoboldOutput(`Using bundled npx: ${bundledNpxPath}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const initProcess = spawn(
|
||||
bundledNpxPath,
|
||||
['sillytavern', '--browserLaunchEnabled', 'false'],
|
||||
{
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
detached: false,
|
||||
}
|
||||
);
|
||||
const initProcess = spawn('npx', spawnArgs, {
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
detached: false,
|
||||
});
|
||||
|
||||
let hasResolved = false;
|
||||
|
||||
|
|
@ -127,8 +145,12 @@ export class SillyTavernManager {
|
|||
cleanupAndResolve();
|
||||
}, 20000);
|
||||
|
||||
initProcess.on('exit', () => {
|
||||
initProcess.on('exit', (code: number | null, signal: string | null) => {
|
||||
clearTimeout(timeout);
|
||||
const exitMessage = signal
|
||||
? `SillyTavern init process terminated with signal ${signal}`
|
||||
: `SillyTavern init process exited with code ${code}`;
|
||||
this.windowManager.sendKoboldOutput(exitMessage);
|
||||
cleanupAndResolve();
|
||||
});
|
||||
|
||||
|
|
@ -140,6 +162,9 @@ export class SillyTavernManager {
|
|||
'Failed to initialize SillyTavern settings:',
|
||||
error
|
||||
);
|
||||
this.windowManager.sendKoboldOutput(
|
||||
`SillyTavern initialization error: ${error.message}`
|
||||
);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
|
|
@ -147,6 +172,9 @@ export class SillyTavernManager {
|
|||
if (initProcess.stdout) {
|
||||
initProcess.stdout.on('data', (data: Buffer) => {
|
||||
const output = data.toString();
|
||||
this.windowManager.sendKoboldOutput(
|
||||
`[SillyTavern stdout]: ${output.trim()}`
|
||||
);
|
||||
if (output.includes('SillyTavern is listening')) {
|
||||
setTimeout(() => {
|
||||
if (!initProcess.killed && !hasResolved) {
|
||||
|
|
@ -158,6 +186,15 @@ export class SillyTavernManager {
|
|||
});
|
||||
}
|
||||
|
||||
if (initProcess.stderr) {
|
||||
initProcess.stderr.on('data', (data: Buffer) => {
|
||||
const output = data.toString();
|
||||
this.windowManager.sendKoboldOutput(
|
||||
`[SillyTavern stderr]: ${output.trim()}`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (!initProcess.killed && !hasResolved) {
|
||||
this.windowManager.sendKoboldOutput(
|
||||
|
|
@ -166,7 +203,7 @@ export class SillyTavernManager {
|
|||
initProcess.kill('SIGTERM');
|
||||
cleanupAndResolve();
|
||||
}
|
||||
}, 8000);
|
||||
}, 10000);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -331,16 +368,7 @@ export class SillyTavernManager {
|
|||
'false',
|
||||
];
|
||||
|
||||
const bundledNpxPath = getPlatformPathFromWindowsPath(
|
||||
app.getAppPath(),
|
||||
'npx.cmd'
|
||||
);
|
||||
|
||||
this.windowManager.sendKoboldOutput(
|
||||
`Using bundled npx: ${bundledNpxPath}`
|
||||
);
|
||||
|
||||
this.sillyTavernProcess = spawn(bundledNpxPath, sillyTavernArgs, {
|
||||
this.sillyTavernProcess = spawn('npx', sillyTavernArgs, {
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
detached: false,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { GitHubRelease, DownloadItem } from '@/types/electron';
|
||||
import { LogManager } from '@/main/managers/LogManager';
|
||||
import { GITHUB_API } from '@/constants';
|
||||
import { filterAssetsByPlatform } from '@/utils';
|
||||
import { filterAssetsByPlatform } from '@/utils/platform';
|
||||
|
||||
export class GitHubService {
|
||||
private lastApiCall = 0;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable no-comments/disallowComments */
|
||||
import si from 'systeminformation';
|
||||
import { shortenDeviceName } from '@/utils/server';
|
||||
import { shortenDeviceName } from '@/utils/hardware';
|
||||
import { LogManager } from '@/main/managers/LogManager';
|
||||
import type {
|
||||
CPUCapabilities,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { contextBridge, ipcRenderer, type IpcRendererEvent } from 'electron';
|
||||
import type { KoboldAPI, AppAPI, ConfigAPI, LogsAPI } from '@/types/electron';
|
||||
import type {
|
||||
KoboldAPI,
|
||||
AppAPI,
|
||||
ConfigAPI,
|
||||
LogsAPI,
|
||||
SillyTavernAPI,
|
||||
} from '@/types/electron';
|
||||
|
||||
const koboldAPI: KoboldAPI = {
|
||||
getInstalledVersions: () => ipcRenderer.invoke('kobold:getInstalledVersions'),
|
||||
|
|
@ -116,9 +122,14 @@ const logsAPI: LogsAPI = {
|
|||
ipcRenderer.invoke('logs:logError', message, error),
|
||||
};
|
||||
|
||||
const sillyTavernAPI: SillyTavernAPI = {
|
||||
isNpxAvailable: () => ipcRenderer.invoke('sillytavern:isNpxAvailable'),
|
||||
};
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
kobold: koboldAPI,
|
||||
app: appAPI,
|
||||
config: configAPI,
|
||||
logs: logsAPI,
|
||||
sillytavern: sillyTavernAPI,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { create } from 'zustand';
|
||||
import type { ConfigFile, SdConvDirectMode } from '@/types';
|
||||
import type { ImageModelPreset } from '@/utils/imageModelPresets';
|
||||
import type { ImageModelPreset } from '@/constants/imageModelPresets';
|
||||
import { DEFAULT_CONTEXT_SIZE } from '@/constants';
|
||||
|
||||
interface LaunchConfigState {
|
||||
|
|
|
|||
5
src/types/electron.d.ts
vendored
5
src/types/electron.d.ts
vendored
|
|
@ -156,6 +156,10 @@ export interface LogsAPI {
|
|||
logError: (message: string, error?: Error) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface SillyTavernAPI {
|
||||
isNpxAvailable: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI: {
|
||||
|
|
@ -163,6 +167,7 @@ declare global {
|
|||
app: AppAPI;
|
||||
config: ConfigAPI;
|
||||
logs: LogsAPI;
|
||||
sillytavern: SillyTavernAPI;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
export * from './assets';
|
||||
export * from './downloadUtils';
|
||||
export * from './imageModelPresets';
|
||||
export * from './linkifyTerminal';
|
||||
export * from './platform';
|
||||
export * from './sounds';
|
||||
export * from './terminal';
|
||||
export * from './validation';
|
||||
export * from './versionUtils';
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
export * from './hardware';
|
||||
export * from './paths';
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import elephantSoundUrl from '@/assets/sounds/elephant-trunk.mp3';
|
||||
import mouseSqueak1Url from '@/assets/sounds/mouse-squeak1.mp3';
|
||||
import mouseSqueak2Url from '@/assets/sounds/mouse-squeak2.mp3';
|
||||
import mouseSqueak3Url from '@/assets/sounds/mouse-squeak3.mp3';
|
||||
import mouseSqueak4Url from '@/assets/sounds/mouse-squeak4.mp3';
|
||||
import mouseSqueak5Url from '@/assets/sounds/mouse-squeak5.mp3';
|
||||
import elephantSoundUrl from '/sounds/elephant-trunk.mp3';
|
||||
import mouseSqueak1Url from '/sounds/mouse-squeak1.mp3';
|
||||
import mouseSqueak2Url from '/sounds/mouse-squeak2.mp3';
|
||||
import mouseSqueak3Url from '/sounds/mouse-squeak3.mp3';
|
||||
import mouseSqueak4Url from '/sounds/mouse-squeak4.mp3';
|
||||
import mouseSqueak5Url from '/sounds/mouse-squeak5.mp3';
|
||||
|
||||
export const soundAssets = {
|
||||
elephant: elephantSoundUrl,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue