Skip to content

Commit

Permalink
feat: change data folder (#3309)
Browse files Browse the repository at this point in the history
  • Loading branch information
louis-jan authored Aug 8, 2024
1 parent b348110 commit b43242b
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 79 deletions.
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

0 comments on commit b43242b

Please sign in to comment.