chore: add relocate jan data folder function to new FE (#5043)
* chore: typo * fix: linter issues * chore: fix linter * chore: fix linter * chore: add relocate data folder
This commit is contained in:
parent
e4168a4c17
commit
6676e0ced8
@ -19,7 +19,7 @@ export abstract class HardwareManagementExtension extends BaseExtension {
|
||||
/**
|
||||
* @returns A Promise that resolves to an object of set active gpus.
|
||||
*/
|
||||
abstract setAvtiveGpu(data: { gpus: number[] }): Promise<{
|
||||
abstract setActiveGpu(data: { gpus: number[] }): Promise<{
|
||||
message: string
|
||||
activated_gpus: number[]
|
||||
}>
|
||||
|
||||
@ -51,7 +51,7 @@ export default class JSONHardwareManagementExtension extends HardwareManagementE
|
||||
/**
|
||||
* @returns A Promise that resolves to an object of set gpu activate.
|
||||
*/
|
||||
async setAvtiveGpu(data: { gpus: number[] }): Promise<{
|
||||
async setActiveGpu(data: { gpus: number[] }): Promise<{
|
||||
message: string
|
||||
activated_gpus: number[]
|
||||
}> {
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "yarn workspace jan lint && yarn workspace @janhq/web lint",
|
||||
"lint": "yarn workspace @janhq/web-app lint",
|
||||
"test:unit": "jest",
|
||||
"test:coverage": "yarn workspace @janhq/web-app test",
|
||||
"test": "yarn workspace @janhq/web-app test",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs, path::PathBuf};
|
||||
use std::{fs, path::PathBuf, io};
|
||||
use tauri::{AppHandle, Manager, Runtime, State};
|
||||
|
||||
use super::{server, setup, state::AppState};
|
||||
@ -267,6 +267,65 @@ pub fn get_user_home_path(app: AppHandle) -> String {
|
||||
return get_app_configurations(app.clone()).data_folder;
|
||||
}
|
||||
|
||||
/// Recursively copy a directory from src to dst
|
||||
fn copy_dir_recursive(src: &PathBuf, dst: &PathBuf) -> Result<(), io::Error> {
|
||||
if !dst.exists() {
|
||||
fs::create_dir_all(dst)?;
|
||||
}
|
||||
|
||||
for entry in fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let file_type = entry.file_type()?;
|
||||
let src_path = entry.path();
|
||||
let dst_path = dst.join(entry.file_name());
|
||||
|
||||
if file_type.is_dir() {
|
||||
copy_dir_recursive(&src_path, &dst_path)?;
|
||||
} else {
|
||||
fs::copy(&src_path, &dst_path)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn change_app_data_folder(
|
||||
app_handle: tauri::AppHandle,
|
||||
new_data_folder: String,
|
||||
) -> Result<(), String> {
|
||||
// Get current data folder path
|
||||
let current_data_folder = get_jan_data_folder_path(app_handle.clone());
|
||||
let new_data_folder_path = PathBuf::from(&new_data_folder);
|
||||
|
||||
// Create the new data folder if it doesn't exist
|
||||
if !new_data_folder_path.exists() {
|
||||
fs::create_dir_all(&new_data_folder_path)
|
||||
.map_err(|e| format!("Failed to create new data folder: {}", e))?;
|
||||
}
|
||||
|
||||
// Copy all files from the old folder to the new one
|
||||
if current_data_folder.exists() {
|
||||
log::info!(
|
||||
"Copying data from {:?} to {:?}",
|
||||
current_data_folder,
|
||||
new_data_folder_path
|
||||
);
|
||||
|
||||
copy_dir_recursive(¤t_data_folder, &new_data_folder_path)
|
||||
.map_err(|e| format!("Failed to copy data to new folder: {}", e))?;
|
||||
} else {
|
||||
log::info!("Current data folder does not exist, nothing to copy");
|
||||
}
|
||||
|
||||
// Update the configuration to point to the new folder
|
||||
let mut configuration = get_app_configurations(app_handle.clone());
|
||||
configuration.data_folder = new_data_folder;
|
||||
|
||||
// Save the updated configuration
|
||||
update_app_configuration(app_handle, configuration)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn app_token(state: State<'_, AppState>) -> Option<String> {
|
||||
state.app_token.clone()
|
||||
|
||||
@ -46,6 +46,7 @@ pub fn run() {
|
||||
core::cmd::start_server,
|
||||
core::cmd::stop_server,
|
||||
core::cmd::read_logs,
|
||||
core::cmd::change_app_data_folder,
|
||||
// MCP commands
|
||||
core::mcp::get_tools,
|
||||
core::mcp::call_tool,
|
||||
|
||||
@ -20,7 +20,6 @@ export default function LanguageSwitcher() {
|
||||
|
||||
const changeLanguage = (lng: string) => {
|
||||
i18n.changeLanguage(lng)
|
||||
// @ts-ignore
|
||||
setCurrentLanguage(lng as Language)
|
||||
}
|
||||
|
||||
|
||||
@ -41,7 +41,6 @@ export function ModelSetting({ model, provider }: ModelSettingProps) {
|
||||
[key]: {
|
||||
...(model.settings?.[key] != null ? model.settings?.[key] : {}),
|
||||
controller_props: {
|
||||
// @ts-ignore
|
||||
...(model.settings?.[key]?.controller_props ?? {}),
|
||||
value: value,
|
||||
},
|
||||
@ -67,7 +66,6 @@ export function ModelSetting({ model, provider }: ModelSettingProps) {
|
||||
updateModel({
|
||||
id: model.id,
|
||||
settings: Object.entries(updatedModel.settings).map(([key, value]) => ({
|
||||
// @ts-ignore
|
||||
[key]: value.controller_props?.value,
|
||||
})) as ModelSettingParams,
|
||||
})
|
||||
|
||||
@ -8,8 +8,8 @@ import {
|
||||
// Dropdown component
|
||||
type DropdownControlProps = {
|
||||
value: string
|
||||
options?: Array<{ value: string; name: string }>
|
||||
onChange: (value: string) => void
|
||||
options?: Array<{ value: number | string; name: string }>
|
||||
onChange: (value: number | string) => void
|
||||
}
|
||||
|
||||
export function DropdownControl({
|
||||
|
||||
@ -10,12 +10,12 @@ type DynamicControllerProps = {
|
||||
title?: string
|
||||
className?: string
|
||||
description?: string
|
||||
controllerType: 'input' | 'checkbox' | 'dropdown' | 'textarea' | 'slider'
|
||||
controllerType: 'input' | 'checkbox' | 'dropdown' | 'textarea' | 'slider' | string
|
||||
controllerProps: {
|
||||
value?: string | boolean | number
|
||||
placeholder?: string
|
||||
type?: string
|
||||
options?: Array<{ value: string; name: string }>
|
||||
options?: Array<{ value: number | string; name: string }>
|
||||
input_actions?: string[]
|
||||
rows?: number
|
||||
min?: number
|
||||
|
||||
@ -3,11 +3,9 @@ import { persist, createJSONStorage } from 'zustand/middleware'
|
||||
import { localStorageKey } from '@/constants/localStorage'
|
||||
|
||||
type LeftPanelStoreState = {
|
||||
// @ts-ignore
|
||||
currentLanguage: Language
|
||||
spellCheckChatInput: boolean
|
||||
setSpellCheckChatInput: (value: boolean) => void
|
||||
// @ts-ignore
|
||||
setCurrentLanguage: (value: Language) => void
|
||||
}
|
||||
|
||||
|
||||
@ -182,7 +182,7 @@ export const useHardware = create<HardwareStore>()(
|
||||
}
|
||||
}
|
||||
setActiveGpus({
|
||||
gpus: newGPUs.filter((e) => e.activated).map((e) => e.id as unknown as number),
|
||||
gpus: newGPUs.filter((e) => e.activated).map((e) => parseInt(e.id)),
|
||||
})
|
||||
return {
|
||||
hardwareData: {
|
||||
|
||||
@ -283,8 +283,7 @@ export const postMessageProcessing = async (
|
||||
? JSON.parse(toolCall.function.arguments)
|
||||
: {},
|
||||
})
|
||||
// @ts-ignore
|
||||
if (result.error) break
|
||||
if ('error' in result && result.error) break
|
||||
|
||||
message.metadata = {
|
||||
...(message.metadata ?? {}),
|
||||
@ -300,7 +299,6 @@ export const postMessageProcessing = async (
|
||||
},
|
||||
],
|
||||
}
|
||||
// @ts-ignore
|
||||
builder.addToolMessage(result.content[0]?.text ?? '', toolCall.id)
|
||||
// update message metadata
|
||||
return message
|
||||
|
||||
@ -27,6 +27,7 @@ export const AppRoutes = [
|
||||
'restartMcpServers',
|
||||
'getConnectedServers',
|
||||
'readLogs',
|
||||
'changeAppDataFolder',
|
||||
]
|
||||
// Define API routes based on different route types
|
||||
export const Routes = [...CoreRoutes, ...APIRoutes, ...AppRoutes].map((r) => ({
|
||||
|
||||
@ -21,7 +21,11 @@ import {
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@/components/ui/dialog'
|
||||
import { factoryReset, getJanDataFolder } from '@/services/app'
|
||||
import {
|
||||
factoryReset,
|
||||
getJanDataFolder,
|
||||
relocateJanDataFolder,
|
||||
} from '@/services/app'
|
||||
import { IconFolder } from '@tabler/icons-react'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@ -115,6 +119,8 @@ function General() {
|
||||
if (selectedPath === janDataFolder) return
|
||||
if (selectedPath !== null) {
|
||||
setJanDataFolder(selectedPath)
|
||||
await relocateJanDataFolder(selectedPath)
|
||||
window.core?.api?.relaunch()
|
||||
// TODO: we need function to move everything into new folder selectedPath
|
||||
// eg like this
|
||||
// await window.core?.api?.moveDataFolder(selectedPath)
|
||||
|
||||
@ -64,3 +64,12 @@ export const getJanDataFolder = async (): Promise<string | undefined> => {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description This function is used to relocate the Jan data folder.
|
||||
* It will change the app data folder to the specified path.
|
||||
* @param path The new path for the Jan data folder
|
||||
*/
|
||||
export const relocateJanDataFolder = async (path: string) => {
|
||||
window.core?.api?.changeAppDataFolder({ newDataFolder: path })
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ export const setActiveGpus = async (data: { gpus: number[] }) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await extension.setAvtiveGpu(data)
|
||||
const response = await extension.setActiveGpu(data)
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error('Failed to install engine variant:', error)
|
||||
|
||||
@ -36,6 +36,6 @@ export const getConnectedServers = (): Promise<string[]> => {
|
||||
export const callTool = (args: {
|
||||
toolName: string
|
||||
arguments: object
|
||||
}): Promise<unknown> => {
|
||||
}): Promise<{ error: string; content: { text: string }[] }> => {
|
||||
return window.core?.api?.callTool(args)
|
||||
}
|
||||
|
||||
1
web-app/src/types/app.d.ts
vendored
Normal file
1
web-app/src/types/app.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
type Language = 'en' | 'id' | 'vn'
|
||||
21
web-app/src/types/global.d.ts
vendored
21
web-app/src/types/global.d.ts
vendored
@ -1,23 +1,22 @@
|
||||
import { ExtensionManager } from '@/lib/extension'
|
||||
export {}
|
||||
|
||||
type Language = 'en' | 'id' | 'vn'
|
||||
declare module 'react-syntax-highlighter-virtualized-renderer'
|
||||
|
||||
type AppCore = {
|
||||
api: APIs
|
||||
extensionManager: ExtensionManager | undefined
|
||||
}
|
||||
|
||||
declare global {
|
||||
declare const IS_TAURI: boolean
|
||||
declare const IS_MACOS: boolean
|
||||
declare const IS_WINDOWS: boolean
|
||||
declare const IS_LINUX: boolean
|
||||
declare const IS_IOS: boolean
|
||||
declare const IS_ANDROID: boolean
|
||||
declare const PLATFORM: string
|
||||
declare const VERSION: string
|
||||
interface Window {
|
||||
core: AppCore | undefined
|
||||
}
|
||||
|
||||
let IS_TAURI: boolean
|
||||
let IS_MACOS: boolean
|
||||
let IS_WINDOWS: boolean
|
||||
let IS_LINUX: boolean
|
||||
let IS_IOS: boolean
|
||||
let IS_ANDROID: boolean
|
||||
let PLATFORM: string
|
||||
let VERSION: string
|
||||
}
|
||||
6
web-app/src/types/modelProviders.d.ts
vendored
6
web-app/src/types/modelProviders.d.ts
vendored
@ -5,7 +5,7 @@ type ControllerProps = {
|
||||
value?: string | boolean | number
|
||||
placeholder?: string
|
||||
type?: string
|
||||
options?: Array<{ value: string; name: string }>
|
||||
options?: Array<{ value: number | string; name: string }>
|
||||
input_actions?: string[]
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ type ProviderSetting = {
|
||||
key: string
|
||||
title: string
|
||||
description: string
|
||||
controller_type: 'input' | 'checkbox' | 'dropdown' | 'slider'
|
||||
controller_type: 'input' | 'checkbox' | 'dropdown' | 'slider' | string
|
||||
controller_props: ControllerProps
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ type Model = {
|
||||
description?: string
|
||||
format?: string
|
||||
capabilities?: string[]
|
||||
settings?: Record<string, unknown>
|
||||
settings?: Record<string, ProviderSetting>
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user