Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: change data folder #3309

Merged
merged 1 commit into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions core/src/types/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export enum NativeRoute {
syncModelFileToCortex = 'syncModelFileToCortex',

openAppLog = 'openAppLog',
appDataFolder = 'appDataFolder',
changeDataFolder = 'changeDataFolder',
isDirectoryEmpty = 'isDirectoryEmpty',
}

export enum AppEvent {
Expand Down
8 changes: 6 additions & 2 deletions core/src/types/config/appConfigEntity.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export type AppConfiguration = {
data_folder: string
quick_ask: boolean
dataFolderPath: string,
quickAsk: boolean,
cortexCppHost: string,
cortexCppPort: number,
apiServerHost: string,
apiServerPort: number,
}
65 changes: 57 additions & 8 deletions electron/handlers/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import {
NativeRoute,
SelectFileProp,
SelectFileOption,
AppConfiguration,
} from '@janhq/core/node'
import { menu } from '../utils/menu'
import { join } from 'path'
import { getAppConfigurations, getJanDataFolderPath } from './../utils/path'
import { getAppConfigurations, getJanDataFolderPath, legacyDataPath, updateAppConfiguration } from './../utils/path'
import {
readdirSync,
writeFileSync,
readFileSync,
existsSync,
mkdirSync,
} from 'fs'
import { dump } from 'js-yaml'

import { dump, load } from 'js-yaml'
const isMac = process.platform === 'darwin'

export function handleAppIPCs() {
Expand Down Expand Up @@ -209,7 +209,7 @@ export function handleAppIPCs() {

ipcMain.handle(NativeRoute.openAppLog, async (_event): Promise<void> => {
const configuration = getAppConfigurations()
const dataFolder = configuration.data_folder
const dataFolder = configuration.dataFolderPath

try {
const errorMessage = await shell.openPath(join(dataFolder))
Expand All @@ -224,11 +224,14 @@ export function handleAppIPCs() {
})

ipcMain.handle(NativeRoute.syncModelFileToCortex, async (_event) => {
const janModelFolderPath = join(getJanDataFolderPath(), 'models')

// Read models from legacy data folder
const janModelFolderPath = join(legacyDataPath(), 'models')
const allModelFolders = readdirSync(janModelFolderPath)

// Latest app configs
const configration = getAppConfigurations()
const destinationFolderPath = join(configration.data_folder, 'models')
const destinationFolderPath = join(configration.dataFolderPath, 'models')

if (!existsSync(destinationFolderPath)) mkdirSync(destinationFolderPath)

Expand Down Expand Up @@ -332,7 +335,7 @@ export function handleAppIPCs() {
ipcMain.handle(
NativeRoute.getAllMessagesAndThreads,
async (_event): Promise<any> => {
const janThreadFolderPath = join(getJanDataFolderPath(), 'threads')
const janThreadFolderPath = join(legacyDataPath(), 'threads')
// check if exist
if (!existsSync(janThreadFolderPath)) {
return {
Expand Down Expand Up @@ -382,7 +385,7 @@ export function handleAppIPCs() {
ipcMain.handle(
NativeRoute.getAllLocalModels,
async (_event): Promise<boolean> => {
const janModelsFolderPath = join(getJanDataFolderPath(), 'models')
const janModelsFolderPath = join(legacyDataPath(), 'models')

if (!existsSync(janModelsFolderPath)) {
console.debug('No local models found')
Expand All @@ -408,4 +411,50 @@ export function handleAppIPCs() {
return hasLocalModels
}
)
ipcMain.handle(NativeRoute.appDataFolder, () => {
return getJanDataFolderPath()
})

ipcMain.handle(NativeRoute.changeDataFolder, async (_event, path) => {
const appConfiguration: AppConfiguration = getAppConfigurations()
const currentJanDataFolder = appConfiguration.dataFolderPath

appConfiguration.dataFolderPath = path

const reflect = require('@alumna/reflect')
const { err } = await reflect({
src: currentJanDataFolder,
dest: path,
recursive: true,
delete: false,
overwrite: true,
errorOnExist: false,
})
if (err) {
console.error(err)
throw err
}

// Migrate models
const janModelsPath = join(path, 'models')
if (existsSync(janModelsPath)) {
const modelYamls = readdirSync(janModelsPath).filter((x) =>
x.endsWith('.yaml') || x.endsWith('.yml')
)
for(const yaml of modelYamls) {
const modelPath = join(janModelsPath, yaml)
const model = load(readFileSync(modelPath, 'utf-8')) as any
if('files' in model && Array.isArray(model.files) && model.files.length > 0) {
model.files[0] = model.files[0].replace(currentJanDataFolder, path)
}
writeFileSync(modelPath, dump(model))
}
}
await updateAppConfiguration(appConfiguration)
})

ipcMain.handle(NativeRoute.isDirectoryEmpty, async (_event, path) => {
const dirChildren = readdirSync(path)
return dirChildren.filter((x) => x !== '.DS_Store').length === 0
})
}
2 changes: 1 addition & 1 deletion electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ app
.then(() => killProcessesOnPort(cortexJsPort))
.then(() => {
const appConfiguration = getAppConfigurations()
const janDataFolder = appConfiguration.data_folder
const janDataFolder = appConfiguration.dataFolderPath

start('jan', host, cortexJsPort, cortexCppPort, janDataFolder)
})
Expand Down
2 changes: 1 addition & 1 deletion electron/managers/tray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class TrayManager {

createSystemTray = () => {
// Feature Toggle for Quick Ask
if (!getAppConfigurations().quick_ask) return
if (!getAppConfigurations().quickAsk) return

if (this.currentTray) {
return
Expand Down
2 changes: 1 addition & 1 deletion electron/managers/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class WindowManager {

windowManager.mainWindow?.on('close', function (evt) {
// Feature Toggle for Quick Ask
if (!getAppConfigurations().quick_ask) return
if (!getAppConfigurations().quickAsk) return

if (!isAppQuitting) {
evt.preventDefault()
Expand Down
30 changes: 2 additions & 28 deletions electron/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
* @module preload
*/

import { APIEvents, APIRoutes, AppConfiguration } from '@janhq/core/node'
import { APIEvents, APIRoutes } from '@janhq/core/node'
import { contextBridge, ipcRenderer } from 'electron'
import { readdirSync } from 'fs'

const interfaces: { [key: string]: (...args: any[]) => any } = {}

Expand All @@ -25,32 +24,7 @@ APIEvents.forEach((method) => {
interfaces[method] = (handler: any) => ipcRenderer.on(method, handler)
})

interfaces['changeDataFolder'] = async (path) => {
const appConfiguration: AppConfiguration = await ipcRenderer.invoke(
'getAppConfigurations'
)
const currentJanDataFolder = appConfiguration.data_folder
appConfiguration.data_folder = path
const reflect = require('@alumna/reflect')
const { err } = await reflect({
src: currentJanDataFolder,
dest: path,
recursive: true,
delete: false,
overwrite: true,
errorOnExist: false,
})
if (err) {
console.error(err)
throw err
}
await ipcRenderer.invoke('updateAppConfiguration', appConfiguration)
}

interfaces['isDirectoryEmpty'] = async (path) => {
const dirChildren = await readdirSync(path)
return dirChildren.filter((x) => x !== '.DS_Store').length === 0
}


// Expose the 'interfaces' object in the main world under the name 'electronAPI'
// This allows the renderer process to access these methods directly
Expand Down
56 changes: 39 additions & 17 deletions electron/utils/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ import { existsSync, writeFileSync, readFileSync } from 'fs'
import { join } from 'path'
import { AppConfiguration } from '@janhq/core/node'
import os from 'os'
import { dump, load } from 'js-yaml'

const configurationFileName = 'settings.json'
const configurationFileName = '.janrc'

const defaultJanDataFolder = join(os.homedir(), 'jan')

const defaultAppConfig: AppConfiguration = {
data_folder: defaultJanDataFolder,
quick_ask: false,
dataFolderPath: defaultJanDataFolder,
quickAsk: true,
cortexCppHost: '127.0.0.1',
cortexCppPort: 3940,
apiServerHost: '127.0.0.1',
apiServerPort: 1338
}

export async function createUserSpace(): Promise<void> {
Expand Down Expand Up @@ -66,15 +71,14 @@ export const getAppConfigurations = (): AppConfiguration => {
console.debug(
`App config not found, creating default config at ${configurationFile}`
)
writeFileSync(configurationFile, JSON.stringify(defaultAppConfig))
writeFileSync(configurationFile, dump(defaultAppConfig))
return defaultAppConfig
}

try {
const appConfigurations: AppConfiguration = JSON.parse(
readFileSync(configurationFile, 'utf-8')
)
console.debug('app config', JSON.stringify(appConfigurations))
const configYaml = readFileSync(configurationFile, 'utf-8')
const appConfigurations = load(configYaml) as AppConfiguration
console.debug('app config', appConfigurations)
return appConfigurations
} catch (err) {
console.error(
Expand All @@ -84,12 +88,15 @@ export const getAppConfigurations = (): AppConfiguration => {
}
}

const getConfigurationFilePath = () =>
join(
global.core?.appPath() ||
process.env[process.platform == 'win32' ? 'USERPROFILE' : 'HOME'],
configurationFileName
)
// Get configuration file path of the application
const getConfigurationFilePath = () => {
const homeDir = os.homedir();
const configPath = join(
homeDir,
configurationFileName,
);
return configPath
}

export const updateAppConfiguration = (
configuration: AppConfiguration
Expand All @@ -100,7 +107,7 @@ export const updateAppConfiguration = (
configurationFile
)

writeFileSync(configurationFile, JSON.stringify(configuration))
writeFileSync(configurationFile, dump(configuration))
return Promise.resolve()
}

Expand All @@ -110,6 +117,21 @@ export const updateAppConfiguration = (
* @returns {string} The data folder path.
*/
export const getJanDataFolderPath = (): string => {
const appConfigurations = getAppConfigurations()
return appConfigurations.data_folder
return getAppConfigurations().dataFolderPath
}

// This is to support pulling legacy configs for migration purpose
export const legacyConfigs = () => {
const legacyConfigFilePath = join(
process.env[process.platform == 'win32' ? 'USERPROFILE' : 'HOME'] ?? '',
'settings.json'
)
const legacyConfigs = JSON.parse(readFileSync(legacyConfigFilePath, 'utf-8')) as any

return legacyConfigs
}

// This is to support pulling legacy data path for migration purpose
export const legacyDataPath = () => {
return legacyConfigs().data_path
}
2 changes: 1 addition & 1 deletion electron/utils/shortcut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { windowManager } from '../managers/window'
const quickAskHotKey = 'CommandOrControl+J'

export function registerGlobalShortcuts() {
if (!getAppConfigurations().quick_ask) return
if (!getAppConfigurations().quickAsk) return
const ret = registerShortcut(quickAskHotKey, (selectedText: string) => {
// Feature Toggle for Quick Ask
if (!windowManager.isQuickAskWindowVisible()) {
Expand Down
10 changes: 3 additions & 7 deletions web/app/search/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import { useEffect } from 'react'

import { AppConfiguration } from '@janhq/core'

import { useSetAtom } from 'jotai'

import ClipboardListener from '@/containers/Providers/ClipboardListener'
Expand All @@ -29,11 +27,9 @@ export default function RootLayout() {
}, [])

useEffect(() => {
window.core?.api
?.getAppConfigurations()
?.then((appConfig: AppConfiguration) => {
setJanDataFolderPath(appConfig.data_folder)
})
window.electronAPI?.appDataFolder()?.then((path: string) => {
setJanDataFolderPath(path)
})
}, [setJanDataFolderPath])

useEffect(() => {
Expand Down
Loading
Loading