diff --git a/core/src/browser/extension.ts b/core/src/browser/extension.ts index 1d641980b6..d768473c93 100644 --- a/core/src/browser/extension.ts +++ b/core/src/browser/extension.ts @@ -12,6 +12,7 @@ export enum ExtensionTypeEnum { SystemMonitoring = 'systemMonitoring', HuggingFace = 'huggingFace', Engine = 'engine', + Hardware = 'hardware', } export interface ExtensionType { diff --git a/core/src/browser/extensions/hardwareManagement.ts b/core/src/browser/extensions/hardwareManagement.ts new file mode 100644 index 0000000000..1f7c362875 --- /dev/null +++ b/core/src/browser/extensions/hardwareManagement.ts @@ -0,0 +1,26 @@ +import { HardwareInformation } from '../../types' +import { BaseExtension, ExtensionTypeEnum } from '../extension' + +/** + * Engine management extension. Persists and retrieves engine management. + * @abstract + * @extends BaseExtension + */ +export abstract class HardwareManagementExtension extends BaseExtension { + type(): ExtensionTypeEnum | undefined { + return ExtensionTypeEnum.Hardware + } + + /** + * @returns A Promise that resolves to an object of list hardware. + */ + abstract getHardware(): Promise + + /** + * @returns A Promise that resolves to an object of set active gpus. + */ + abstract setAvtiveGpu(data: { gpus: number[] }): Promise<{ + message: string + activated_gpus: number[] + }> +} diff --git a/core/src/browser/extensions/index.ts b/core/src/browser/extensions/index.ts index 9dbfe1afe2..2fd447f402 100644 --- a/core/src/browser/extensions/index.ts +++ b/core/src/browser/extensions/index.ts @@ -33,3 +33,8 @@ export * from './engines' * Engines Management */ export * from './enginesManagement' + +/** + * Hardware Management + */ +export * from './hardwareManagement' diff --git a/core/src/types/hardware/index.ts b/core/src/types/hardware/index.ts new file mode 100644 index 0000000000..f729ba4e53 --- /dev/null +++ b/core/src/types/hardware/index.ts @@ -0,0 +1,55 @@ +export type Cpu = { + arch: string + cores: number + instructions: string[] + model: string + usage: number +} + +export type GpuAdditionalInformation = { + compute_cap: string + driver_version: string +} + +export type Gpu = { + activated: boolean + additional_information: GpuAdditionalInformation + free_vram: number + id: string + name: string + total_vram: number + uuid: string + version: string +} + +export type Os = { + name: string + version: string +} + +export type Power = { + battery_life: number + charging_status: string + is_power_saving: boolean +} + +export type Ram = { + available: number + total: number + type: string +} + +export type Storage = { + available: number + total: number + type: string +} + +export type HardwareInformation = { + cpu: Cpu + gpus: Gpu[] + os: Os + power: Power + ram: Ram + storage: Storage +} diff --git a/core/src/types/index.ts b/core/src/types/index.ts index e30dd18c38..d18c1d6ced 100644 --- a/core/src/types/index.ts +++ b/core/src/types/index.ts @@ -11,3 +11,4 @@ export * from './miscellaneous' export * from './api' export * from './setting' export * from './engine' +export * from './hardware' diff --git a/extensions/hardware-management-extension/jest.config.js b/extensions/hardware-management-extension/jest.config.js new file mode 100644 index 0000000000..8bb37208d7 --- /dev/null +++ b/extensions/hardware-management-extension/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +} diff --git a/extensions/hardware-management-extension/package.json b/extensions/hardware-management-extension/package.json new file mode 100644 index 0000000000..ec98c74409 --- /dev/null +++ b/extensions/hardware-management-extension/package.json @@ -0,0 +1,48 @@ +{ + "name": "@janhq/hardware-management-extension", + "productName": "Hardware Management", + "version": "1.0.0", + "description": "Manages Better Hardware settings.", + "main": "dist/index.js", + "node": "dist/node/index.cjs.js", + "author": "Jan ", + "license": "MIT", + "scripts": { + "test": "jest", + "build": "rolldown -c rolldown.config.mjs", + "codesign:darwin": "../../.github/scripts/auto-sign.sh", + "codesign:win32:linux": "echo 'No codesigning required'", + "codesign": "run-script-os", + "build:publish": "rimraf *.tgz --glob || true && yarn build && yarn codesign && npm pack && cpx *.tgz ../../pre-install" + }, + "exports": { + ".": "./dist/index.js", + "./main": "./dist/module.js" + }, + "devDependencies": { + "cpx": "^1.5.0", + "rimraf": "^3.0.2", + "rolldown": "^1.0.0-beta.1", + "run-script-os": "^1.1.6", + "ts-loader": "^9.5.0", + "typescript": "^5.3.3" + }, + "dependencies": { + "@janhq/core": "../../core/package.tgz", + "cpu-instructions": "^0.0.13", + "ky": "^1.7.2", + "p-queue": "^8.0.1" + }, + "bundledDependencies": [ + "cpu-instructions", + "@janhq/core" + ], + "hardwares": { + "node": ">=18.0.0" + }, + "files": [ + "dist/*", + "package.json", + "README.md" + ] +} diff --git a/extensions/hardware-management-extension/rolldown.config.mjs b/extensions/hardware-management-extension/rolldown.config.mjs new file mode 100644 index 0000000000..7982ca5552 --- /dev/null +++ b/extensions/hardware-management-extension/rolldown.config.mjs @@ -0,0 +1,17 @@ +import { defineConfig } from 'rolldown' +import pkgJson from './package.json' with { type: 'json' } + +export default defineConfig([ + { + input: 'src/index.ts', + output: { + format: 'esm', + file: 'dist/index.js', + }, + define: { + NODE: JSON.stringify(`${pkgJson.name}/${pkgJson.node}`), + API_URL: JSON.stringify('http://127.0.0.1:39291'), + SOCKET_URL: JSON.stringify('ws://127.0.0.1:39291'), + }, + }, +]) diff --git a/extensions/hardware-management-extension/src/@types/global.d.ts b/extensions/hardware-management-extension/src/@types/global.d.ts new file mode 100644 index 0000000000..6639b9cbb6 --- /dev/null +++ b/extensions/hardware-management-extension/src/@types/global.d.ts @@ -0,0 +1,12 @@ +declare const API_URL: string +declare const SOCKET_URL: string +declare const NODE: string + +interface Core { + api: APIFunctions + events: EventEmitter +} +interface Window { + core?: Core | undefined + electronAPI?: any | undefined +} diff --git a/extensions/hardware-management-extension/src/index.ts b/extensions/hardware-management-extension/src/index.ts new file mode 100644 index 0000000000..c2edc61597 --- /dev/null +++ b/extensions/hardware-management-extension/src/index.ts @@ -0,0 +1,67 @@ +import { + executeOnMain, + HardwareManagementExtension, + HardwareInformation, +} from '@janhq/core' +import ky from 'ky' +import PQueue from 'p-queue' + +/** + * JSONHardwareManagementExtension is a HardwareManagementExtension implementation that provides + * functionality for managing engines. + */ +export default class JSONHardwareManagementExtension extends HardwareManagementExtension { + queue = new PQueue({ concurrency: 1 }) + + /** + * Called when the extension is loaded. + */ + async onLoad() { + // Run Healthcheck + this.queue.add(() => this.healthz()) + } + + /** + * Called when the extension is unloaded. + */ + onUnload() {} + + /** + * Do health check on cortex.cpp + * @returns + */ + async healthz(): Promise { + return ky + .get(`${API_URL}/healthz`, { + retry: { limit: 20, delay: () => 500, methods: ['get'] }, + }) + .then(() => {}) + } + + /** + * @returns A Promise that resolves to an object of hardware. + */ + async getHardware(): Promise { + return this.queue.add(() => + ky + .get(`${API_URL}/v1/hardware`) + .json() + .then((e) => e) + ) as Promise + } + + /** + * @returns A Promise that resolves to an object of set gpu activate. + */ + async setAvtiveGpu(data: { gpus: number[] }): Promise<{ + message: string + activated_gpus: number[] + }> { + return this.queue.add(() => + ky.post(`${API_URL}/v1/hardware/activate`, { json: data }).then((e) => e) + ) as Promise<{ + message: string + activated_gpus: number[] + }> + } +} diff --git a/extensions/hardware-management-extension/tsconfig.json b/extensions/hardware-management-extension/tsconfig.json new file mode 100644 index 0000000000..72e1e1895a --- /dev/null +++ b/extensions/hardware-management-extension/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "es2016", + "module": "ES6", + "moduleResolution": "node", + "outDir": "./dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": false, + "skipLibCheck": true, + "rootDir": "./src", + "resolveJsonModule": true + }, + "include": ["./src"], + "exclude": ["src/**/*.test.ts", "rolldown.config.mjs"] +} diff --git a/extensions/yarn.lock b/extensions/yarn.lock index acf2965ca9..71fe009332 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -509,61 +509,71 @@ __metadata: "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=7dd866&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=07e12e&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/da0eed6e552ce2ff6f52a087e6e221101c3d0c03d92820840ee80c3ca1a17317a66525cb5bf59b6c1e8bd2e36e54763008f97e13000ae339dac49f5682fcfa65 + checksum: 10c0/47917b03405393c8d2738b93d3adf14a8270c7709403130eb7544cb3284f52bfde4bf09ef3c6b3e2685fa1512179cf5173b70aca3bb3c1a752a0e8158c5c2ed2 languageName: node linkType: hard "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=7dd866&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=07e12e&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/da0eed6e552ce2ff6f52a087e6e221101c3d0c03d92820840ee80c3ca1a17317a66525cb5bf59b6c1e8bd2e36e54763008f97e13000ae339dac49f5682fcfa65 + checksum: 10c0/47917b03405393c8d2738b93d3adf14a8270c7709403130eb7544cb3284f52bfde4bf09ef3c6b3e2685fa1512179cf5173b70aca3bb3c1a752a0e8158c5c2ed2 languageName: node linkType: hard "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=7dd866&locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=07e12e&locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/da0eed6e552ce2ff6f52a087e6e221101c3d0c03d92820840ee80c3ca1a17317a66525cb5bf59b6c1e8bd2e36e54763008f97e13000ae339dac49f5682fcfa65 + checksum: 10c0/47917b03405393c8d2738b93d3adf14a8270c7709403130eb7544cb3284f52bfde4bf09ef3c6b3e2685fa1512179cf5173b70aca3bb3c1a752a0e8158c5c2ed2 + languageName: node + linkType: hard + +"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fhardware-management-extension%40workspace%3Ahardware-management-extension": + version: 0.1.10 + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=07e12e&locator=%40janhq%2Fhardware-management-extension%40workspace%3Ahardware-management-extension" + dependencies: + rxjs: "npm:^7.8.1" + ulidx: "npm:^2.3.0" + checksum: 10c0/47917b03405393c8d2738b93d3adf14a8270c7709403130eb7544cb3284f52bfde4bf09ef3c6b3e2685fa1512179cf5173b70aca3bb3c1a752a0e8158c5c2ed2 languageName: node linkType: hard "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=7dd866&locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=07e12e&locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/da0eed6e552ce2ff6f52a087e6e221101c3d0c03d92820840ee80c3ca1a17317a66525cb5bf59b6c1e8bd2e36e54763008f97e13000ae339dac49f5682fcfa65 + checksum: 10c0/47917b03405393c8d2738b93d3adf14a8270c7709403130eb7544cb3284f52bfde4bf09ef3c6b3e2685fa1512179cf5173b70aca3bb3c1a752a0e8158c5c2ed2 languageName: node linkType: hard "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=7dd866&locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=07e12e&locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/da0eed6e552ce2ff6f52a087e6e221101c3d0c03d92820840ee80c3ca1a17317a66525cb5bf59b6c1e8bd2e36e54763008f97e13000ae339dac49f5682fcfa65 + checksum: 10c0/47917b03405393c8d2738b93d3adf14a8270c7709403130eb7544cb3284f52bfde4bf09ef3c6b3e2685fa1512179cf5173b70aca3bb3c1a752a0e8158c5c2ed2 languageName: node linkType: hard "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fmonitoring-extension%40workspace%3Amonitoring-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=7dd866&locator=%40janhq%2Fmonitoring-extension%40workspace%3Amonitoring-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=07e12e&locator=%40janhq%2Fmonitoring-extension%40workspace%3Amonitoring-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/da0eed6e552ce2ff6f52a087e6e221101c3d0c03d92820840ee80c3ca1a17317a66525cb5bf59b6c1e8bd2e36e54763008f97e13000ae339dac49f5682fcfa65 + checksum: 10c0/47917b03405393c8d2738b93d3adf14a8270c7709403130eb7544cb3284f52bfde4bf09ef3c6b3e2685fa1512179cf5173b70aca3bb3c1a752a0e8158c5c2ed2 languageName: node linkType: hard @@ -584,6 +594,23 @@ __metadata: languageName: unknown linkType: soft +"@janhq/hardware-management-extension@workspace:hardware-management-extension": + version: 0.0.0-use.local + resolution: "@janhq/hardware-management-extension@workspace:hardware-management-extension" + dependencies: + "@janhq/core": ../../core/package.tgz + cpu-instructions: "npm:^0.0.13" + cpx: "npm:^1.5.0" + ky: "npm:^1.7.2" + p-queue: "npm:^8.0.1" + rimraf: "npm:^3.0.2" + rolldown: "npm:^1.0.0-beta.1" + run-script-os: "npm:^1.1.6" + ts-loader: "npm:^9.5.0" + typescript: "npm:^5.3.3" + languageName: unknown + linkType: soft + "@janhq/inference-cortex-extension@workspace:inference-cortex-extension": version: 0.0.0-use.local resolution: "@janhq/inference-cortex-extension@workspace:inference-cortex-extension" diff --git a/web/hooks/useHardwareManagement.ts b/web/hooks/useHardwareManagement.ts new file mode 100644 index 0000000000..bc168819e8 --- /dev/null +++ b/web/hooks/useHardwareManagement.ts @@ -0,0 +1,73 @@ +import { useMemo } from 'react' + +import { ExtensionTypeEnum, HardwareManagementExtension } from '@janhq/core' + +import useSWR from 'swr' + +import { extensionManager } from '@/extension/ExtensionManager' + +// fetcher function +async function fetchExtensionData( + extension: HardwareManagementExtension | null, + method: (extension: HardwareManagementExtension) => Promise +): Promise { + if (!extension) { + throw new Error('Extension not found') + } + return method(extension) +} + +const getExtension = () => + extensionManager.get( + ExtensionTypeEnum.Hardware + ) ?? null + +/** + * @returns A Promise that resolves to an object of list engines. + */ +export function useGetHardwareInfo() { + const extension = useMemo( + () => + extensionManager.get( + ExtensionTypeEnum.Hardware + ) ?? null, + [] + ) + + const { + data: hardware, + error, + mutate, + } = useSWR( + extension ? 'hardware' : null, + () => fetchExtensionData(extension, (ext) => ext.getHardware()), + { + revalidateOnFocus: false, + revalidateOnReconnect: true, + refreshInterval: 5000, // Poll every 5 seconds + refreshWhenHidden: false, // Optional: Prevent polling when the tab is not visible + } + ) + + return { hardware, error, mutate } +} + +/** + * set gpus activate + * @returns A Promise that resolves set gpus activate. + */ +export const setActiveGpus = async (data: { gpus: number[] }) => { + const extension = getExtension() + + if (!extension) { + throw new Error('Extension is not available') + } + + try { + const response = await extension.setAvtiveGpu(data) + return response + } catch (error) { + console.error('Failed to install engine variant:', error) + throw error + } +} diff --git a/web/package.json b/web/package.json index c562b6aa66..aba9edec17 100644 --- a/web/package.json +++ b/web/package.json @@ -14,6 +14,7 @@ "test": "jest" }, "dependencies": { + "@hello-pangea/dnd": "17.0.0", "@hookform/resolvers": "^3.9.1", "@janhq/core": "link:../core", "@janhq/joi": "link:../joi", diff --git a/web/screens/Settings/Hardware/index.tsx b/web/screens/Settings/Hardware/index.tsx new file mode 100644 index 0000000000..01ef509a70 --- /dev/null +++ b/web/screens/Settings/Hardware/index.tsx @@ -0,0 +1,354 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import * as React from 'react' + +import { useState } from 'react' + +import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd' +import { Gpu } from '@janhq/core' +import { Progress, ScrollArea, Switch } from '@janhq/joi' +import { useAtom } from 'jotai' +import { atomWithStorage } from 'jotai/utils' + +import { ChevronDownIcon, GripVerticalIcon } from 'lucide-react' + +import { twMerge } from 'tailwind-merge' + +import { + useGetHardwareInfo, + setActiveGpus, +} from '@/hooks/useHardwareManagement' + +const gpusAtom = atomWithStorage('gpus', []) + +export const showRightPanelAtom = atomWithStorage( + 'gpus', + [], + undefined, + { getOnInit: true } +) + +const Hardware = () => { + const { hardware } = useGetHardwareInfo() + const [openPanels, setOpenPanels] = useState>({}) + + const [gpus, setGpus] = useAtom(gpusAtom) + + const togglePanel = (index: number) => { + setOpenPanels((prev) => ({ + ...prev, + [index]: !prev[index], // Toggle the specific panel + })) + } + + // Handle switch toggle for GPU activation + const handleSwitchChange = async (index: number, isActive: boolean) => { + const updatedGpus = gpus.map((gpu, i) => + i === index ? { ...gpu, activated: isActive } : gpu + ) + setGpus(updatedGpus) + // Call the API to update the active GPUs + try { + const activeGpuIds = updatedGpus + .filter((gpu) => gpu.activated) + .map((gpu) => Number(gpu.id)) + await setActiveGpus({ gpus: activeGpuIds }) + } catch (error) { + console.error('Failed to update active GPUs:', error) + } + } + + const handleDragEnd = (result: any) => { + if (!result.destination) return + const reorderedGpus = Array.from(gpus) + const [movedGpu] = reorderedGpus.splice(result.source.index, 1) + reorderedGpus.splice(result.destination.index, 0, movedGpu) + setGpus(reorderedGpus) // Update the atom, which persists to localStorage + } + + React.useEffect(() => { + if (hardware?.gpus) { + setGpus((prevGpus) => { + // Create a map of existing GPUs by UUID for quick lookup + const gpuMap = new Map(prevGpus.map((gpu) => [gpu.uuid, gpu])) + + // Update existing GPUs or add new ones + const updatedGpus = hardware.gpus.map((newGpu) => { + const existingGpu = gpuMap.get(newGpu.uuid) + + if (existingGpu) { + // Update the GPU properties while keeping the original order + return { + ...existingGpu, + free_vram: newGpu.free_vram, + total_vram: newGpu.total_vram, + } + } + + // Return the new GPU if not already in the state + return newGpu + }) + + // Append GPUs from the previous state that are not in the hardware.gpus + // This preserves user-reordered GPUs that aren't present in the new data + const remainingGpus = prevGpus.filter( + (prevGpu) => !hardware.gpus.some((gpu) => gpu.uuid === prevGpu.uuid) + ) + + return [...updatedGpus, ...remainingGpus] + }) + } + }, [hardware?.gpus, setGpus]) + + return ( + +
+ {/* CPU */} +
+
+
+
CPU
+
+
+
+
+
+ {hardware?.cpu.model} + | + Cores: {hardware?.cpu.cores} + | + Architecture: {hardware?.cpu.arch} +
+
+ + {hardware?.cpu.usage}% +
+
+
+
+ {/* RAM */} +
+
+
+
RAM
+
+
+
+
+
+ + {( + (Number(hardware?.ram.total) - + Number(hardware?.ram.available)) / + 1024 + ).toFixed(2)} + GB / {(Number(hardware?.ram.total) / 1024).toFixed(2)}GB + + {hardware?.ram.type && ( + <> + | + Type: {hardware?.ram.type} + + )} +
+
+ + + {Math.round( + ((Number(hardware?.ram.total) - + Number(hardware?.ram.available)) / + Number(hardware?.ram.total)) * + 100 + ).toFixed()} + % + +
+
+
+
+ {/* OS */} +
+
+
+
OS
+
+
+
+
+
+ {hardware?.os.name} + | + {hardware?.os.version} +
+
+
+
+ {/* GPUs */} + {!isMac && gpus.length > 0 && ( +
+
+
+
GPUs
+
+

+ {`Enhance model performance by utilizing your device's GPU for + acceleration.`} +

+ + + {(provided) => ( +
+ {gpus.map((item, i) => ( + + {(provided, snapshot) => ( +
1 && 'last:rounded-t-none', + snapshot.isDragging + ? 'border-b' + : 'border-b-0 last:border-b' + )} + onClick={() => togglePanel(i)} + > +
+
+
+ +
+
{item.name}
+
+
+ {item.activated && ( +
+ + + {Math.round( + ((Number(item.total_vram) - + Number(item.free_vram)) / + Number(item.total_vram)) * + 100 + ).toFixed()} + % + +
+ )} + +
+ {item.activated && ( + + {( + (Number(item.total_vram) - + Number(item.free_vram)) / + 1024 + ).toFixed(2)} + GB /{' '} + + )} + + {( + Number(item.total_vram) / 1024 + ).toFixed(2)} + GB + +
+ + + handleSwitchChange(i, e.target.checked) + } + /> + + +
+
+
+ + {openPanels[i] && ( +
+
+
+ Driver Version +
+ + { + item.additional_information + .driver_version + } + +
+
+
+ Compute Capability +
+ + {item.additional_information.compute_cap} + +
+
+ )} +
+ )} + + ))} + {provided.placeholder} +
+ )} + + +
+
+ )} +
+ + ) +} + +export default Hardware diff --git a/web/screens/Settings/SettingDetail/index.tsx b/web/screens/Settings/SettingDetail/index.tsx index 1e4b79282d..03d01c217a 100644 --- a/web/screens/Settings/SettingDetail/index.tsx +++ b/web/screens/Settings/SettingDetail/index.tsx @@ -8,6 +8,7 @@ import Engines from '@/screens/Settings/Engines' import LocalEngineSettings from '@/screens/Settings/Engines/LocalEngineSettings' import RemoteEngineSettings from '@/screens/Settings/Engines/RemoteEngineSettings' import ExtensionSetting from '@/screens/Settings/ExtensionSetting' +import Hardware from '@/screens/Settings/Hardware' import Hotkeys from '@/screens/Settings/Hotkeys' import MyModels from '@/screens/Settings/MyModels' import Privacy from '@/screens/Settings/Privacy' @@ -34,6 +35,9 @@ const SettingDetail = () => { case 'Keyboard Shortcuts': return + case 'Hardware': + return + case 'Privacy': return diff --git a/web/screens/Settings/index.tsx b/web/screens/Settings/index.tsx index 66e11d07ed..d126f0d0ed 100644 --- a/web/screens/Settings/index.tsx +++ b/web/screens/Settings/index.tsx @@ -15,6 +15,7 @@ export const SettingScreenList = [ 'My Models', 'Preferences', 'Keyboard Shortcuts', + 'Hardware', 'Privacy', 'Advanced Settings', 'Engines', diff --git a/yarn.lock b/yarn.lock index fcd00767c5..64474317f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -369,7 +369,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.12.5": +"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.25.6": version: 7.26.0 resolution: "@babel/runtime@npm:7.26.0" dependencies: @@ -844,6 +844,24 @@ __metadata: languageName: node linkType: hard +"@hello-pangea/dnd@npm:17.0.0": + version: 17.0.0 + resolution: "@hello-pangea/dnd@npm:17.0.0" + dependencies: + "@babel/runtime": "npm:^7.25.6" + css-box-model: "npm:^1.2.1" + memoize-one: "npm:^6.0.0" + raf-schd: "npm:^4.0.3" + react-redux: "npm:^9.1.2" + redux: "npm:^5.0.1" + use-memo-one: "npm:^1.1.3" + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 10c0/93417c055267f6f12a37a1cdb08d9db85ab021b102315e1e5a70a79d7de6c2ffaeff211e3ec40441c110f39e60688cfcea85ab86c21820041d974415c1ca715e + languageName: node + linkType: hard + "@hookform/resolvers@npm:^3.9.1": version: 3.9.1 resolution: "@hookform/resolvers@npm:3.9.1" @@ -1067,6 +1085,7 @@ __metadata: version: 0.0.0-use.local resolution: "@janhq/web@workspace:web" dependencies: + "@hello-pangea/dnd": "npm:17.0.0" "@hookform/resolvers": "npm:^3.9.1" "@janhq/core": "link:../core" "@janhq/joi": "link:../joi" @@ -4408,6 +4427,13 @@ __metadata: languageName: node linkType: hard +"@types/use-sync-external-store@npm:^0.0.6": + version: 0.0.6 + resolution: "@types/use-sync-external-store@npm:0.0.6" + checksum: 10c0/77c045a98f57488201f678b181cccd042279aff3da34540ad242f893acc52b358bd0a8207a321b8ac09adbcef36e3236944390e2df4fcedb556ce7bb2a88f2a8 + languageName: node + linkType: hard + "@types/uuid@npm:^9.0.6": version: 9.0.8 resolution: "@types/uuid@npm:9.0.8" @@ -6681,6 +6707,15 @@ __metadata: languageName: node linkType: hard +"css-box-model@npm:^1.2.1": + version: 1.2.1 + resolution: "css-box-model@npm:1.2.1" + dependencies: + tiny-invariant: "npm:^1.0.6" + checksum: 10c0/611e56d76b16e4e21956ed9fa53f1936fbbfaccd378659587e9c929f342037fc6c062f8af9447226e11fe7c95e31e6c007a37e592f9bff4c2d40e6915553104a + languageName: node + linkType: hard + "css-declaration-sorter@npm:^6.3.1": version: 6.4.1 resolution: "css-declaration-sorter@npm:6.4.1" @@ -12482,6 +12517,13 @@ __metadata: languageName: node linkType: hard +"memoize-one@npm:^6.0.0": + version: 6.0.0 + resolution: "memoize-one@npm:6.0.0" + checksum: 10c0/45c88e064fd715166619af72e8cf8a7a17224d6edf61f7a8633d740ed8c8c0558a4373876c9b8ffc5518c2b65a960266adf403cc215cb1e90f7e262b58991f54 + languageName: node + linkType: hard + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -15331,6 +15373,13 @@ __metadata: languageName: node linkType: hard +"raf-schd@npm:^4.0.3": + version: 4.0.3 + resolution: "raf-schd@npm:4.0.3" + checksum: 10c0/ecabf0957c05fad059779bddcd992f1a9d3a35dfea439a6f0935c382fcf4f7f7fa60489e467b4c2db357a3665167d2a379782586b59712bb36c766e02824709b + languageName: node + linkType: hard + "randomatic@npm:^3.0.0": version: 3.1.1 resolution: "randomatic@npm:3.1.1" @@ -15477,6 +15526,25 @@ __metadata: languageName: node linkType: hard +"react-redux@npm:^9.1.2": + version: 9.2.0 + resolution: "react-redux@npm:9.2.0" + dependencies: + "@types/use-sync-external-store": "npm:^0.0.6" + use-sync-external-store: "npm:^1.4.0" + peerDependencies: + "@types/react": ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + redux: + optional: true + checksum: 10c0/00d485f9d9219ca1507b4d30dde5f6ff8fb68ba642458f742e0ec83af052f89e65cd668249b99299e1053cc6ad3d2d8ac6cb89e2f70d2ac5585ae0d7fa0ef259 + languageName: node + linkType: hard + "react-remove-scroll-bar@npm:^2.3.7": version: 2.3.8 resolution: "react-remove-scroll-bar@npm:2.3.8" @@ -15684,6 +15752,13 @@ __metadata: languageName: node linkType: hard +"redux@npm:^5.0.1": + version: 5.0.1 + resolution: "redux@npm:5.0.1" + checksum: 10c0/b10c28357194f38e7d53b760ed5e64faa317cc63de1fb95bc5d9e127fab956392344368c357b8e7a9bedb0c35b111e7efa522210cfdc3b3c75e5074718e9069c + languageName: node + linkType: hard + "reflect.getprototypeof@npm:^1.0.6, reflect.getprototypeof@npm:^1.0.8, reflect.getprototypeof@npm:^1.0.9": version: 1.0.9 resolution: "reflect.getprototypeof@npm:1.0.9" @@ -17775,6 +17850,13 @@ __metadata: languageName: node linkType: hard +"tiny-invariant@npm:^1.0.6": + version: 1.3.3 + resolution: "tiny-invariant@npm:1.3.3" + checksum: 10c0/65af4a07324b591a059b35269cd696aba21bef2107f29b9f5894d83cc143159a204b299553435b03874ebb5b94d019afa8b8eff241c8a4cfee95872c2e1c1c4a + languageName: node + linkType: hard + "tiny-typed-emitter@npm:^2.1.0": version: 2.1.0 resolution: "tiny-typed-emitter@npm:2.1.0" @@ -18498,6 +18580,15 @@ __metadata: languageName: node linkType: hard +"use-memo-one@npm:^1.1.3": + version: 1.1.3 + resolution: "use-memo-one@npm:1.1.3" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 10c0/3d596e65a6b47b2f1818061599738e00daad1f9a9bb4e5ce1f014b20a35b297e50fe4bf1d8c1699ab43ea97f01f84649a736c15ceff96de83bfa696925f6cc6b + languageName: node + linkType: hard + "use-sidecar@npm:^1.1.2": version: 1.1.3 resolution: "use-sidecar@npm:1.1.3"