feat: update UI allow user change folder (#1738)
* feat: wip ui jan folder setting * change input disabled * finished change directory jan folder * fix overlap value input current path folder * make app reload to latest page * fix: add experimental feature toggle til the next release --------- Co-authored-by: Louis <louis@jan.ai>
This commit is contained in:
parent
1b794b5337
commit
6ba48bc1e3
@ -14,6 +14,7 @@ const buttonVariants = cva('btn', {
|
||||
outline: 'btn-outline',
|
||||
secondary: 'btn-secondary',
|
||||
secondaryBlue: 'btn-secondary-blue',
|
||||
secondaryDanger: 'btn-secondary-danger',
|
||||
ghost: 'btn-ghost',
|
||||
success: 'btn-success',
|
||||
},
|
||||
|
||||
@ -9,13 +9,17 @@
|
||||
}
|
||||
|
||||
&-secondary-blue {
|
||||
@apply bg-blue-200 text-blue-600 hover:bg-blue-500/80;
|
||||
@apply bg-blue-200 text-blue-600 hover:bg-blue-500/50;
|
||||
}
|
||||
|
||||
&-danger {
|
||||
@apply bg-danger text-danger-foreground hover:bg-danger/90;
|
||||
}
|
||||
|
||||
&-secondary-danger {
|
||||
@apply bg-red-200 text-red-600 hover:bg-red-500/50;
|
||||
}
|
||||
|
||||
&-outline {
|
||||
@apply border-input border bg-transparent;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
.input {
|
||||
@apply border-border placeholder:text-muted-foreground flex h-9 w-full rounded-lg border bg-transparent px-3 py-1 transition-colors;
|
||||
@apply disabled:cursor-not-allowed disabled:opacity-50;
|
||||
@apply disabled:cursor-not-allowed disabled:bg-zinc-100;
|
||||
@apply focus-within:outline-none focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-1;
|
||||
@apply file:border-0 file:bg-transparent file:font-medium;
|
||||
}
|
||||
|
||||
@ -9,11 +9,14 @@ import RibbonNav from '@/containers/Layout/Ribbon'
|
||||
|
||||
import TopBar from '@/containers/Layout/TopBar'
|
||||
|
||||
import { MainViewState } from '@/constants/screens'
|
||||
|
||||
import { useMainViewState } from '@/hooks/useMainViewState'
|
||||
import { SUCCESS_SET_NEW_DESTINATION } from '@/hooks/useVaultDirectory'
|
||||
|
||||
const BaseLayout = (props: PropsWithChildren) => {
|
||||
const { children } = props
|
||||
const { mainViewState } = useMainViewState()
|
||||
const { mainViewState, setMainViewState } = useMainViewState()
|
||||
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
@ -21,6 +24,12 @@ const BaseLayout = (props: PropsWithChildren) => {
|
||||
setTheme(theme as string)
|
||||
}, [setTheme, theme])
|
||||
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem(SUCCESS_SET_NEW_DESTINATION) === 'true') {
|
||||
setMainViewState(MainViewState.Settings)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-screen flex-1 overflow-hidden">
|
||||
<RibbonNav />
|
||||
|
||||
@ -50,10 +50,12 @@ const availableShortcuts = [
|
||||
|
||||
const ShortcutModal: React.FC = () => (
|
||||
<Modal>
|
||||
<ModalTrigger asChild>
|
||||
<Button size="sm" themes="secondary">
|
||||
Show
|
||||
</Button>
|
||||
<ModalTrigger>
|
||||
<div>
|
||||
<Button size="sm" themes="secondaryBlue">
|
||||
Show
|
||||
</Button>
|
||||
</div>
|
||||
</ModalTrigger>
|
||||
<ModalContent className="max-w-2xl">
|
||||
<ModalHeader>
|
||||
|
||||
105
web/hooks/useVaultDirectory.ts
Normal file
105
web/hooks/useVaultDirectory.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { fs, AppConfiguration } from '@janhq/core'
|
||||
|
||||
import { atom, useAtom } from 'jotai'
|
||||
|
||||
import { useMainViewState } from './useMainViewState'
|
||||
|
||||
const isSameDirectoryAtom = atom(false)
|
||||
const isDirectoryConfirmAtom = atom(false)
|
||||
const isErrorSetNewDestAtom = atom(false)
|
||||
const currentPathAtom = atom('')
|
||||
const newDestinationPathAtom = atom('')
|
||||
|
||||
export const SUCCESS_SET_NEW_DESTINATION = 'successSetNewDestination'
|
||||
|
||||
export function useVaultDirectory() {
|
||||
const [isSameDirectory, setIsSameDirectory] = useAtom(isSameDirectoryAtom)
|
||||
const { setMainViewState } = useMainViewState()
|
||||
const [isDirectoryConfirm, setIsDirectoryConfirm] = useAtom(
|
||||
isDirectoryConfirmAtom
|
||||
)
|
||||
const [isErrorSetNewDest, setIsErrorSetNewDest] = useAtom(
|
||||
isErrorSetNewDestAtom
|
||||
)
|
||||
const [currentPath, setCurrentPath] = useAtom(currentPathAtom)
|
||||
const [newDestinationPath, setNewDestinationPath] = useAtom(
|
||||
newDestinationPathAtom
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
window.core?.api
|
||||
?.getAppConfigurations()
|
||||
?.then((appConfig: AppConfiguration) => {
|
||||
setCurrentPath(appConfig.data_folder)
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const setNewDestination = async () => {
|
||||
const destFolder = await window.core?.api?.selectDirectory()
|
||||
setNewDestinationPath(destFolder)
|
||||
|
||||
if (destFolder) {
|
||||
console.debug(`Destination folder selected: ${destFolder}`)
|
||||
try {
|
||||
const appConfiguration: AppConfiguration =
|
||||
await window.core?.api?.getAppConfigurations()
|
||||
const currentJanDataFolder = appConfiguration.data_folder
|
||||
|
||||
if (currentJanDataFolder === destFolder) {
|
||||
console.debug(
|
||||
`Destination folder is the same as current folder. Ignore..`
|
||||
)
|
||||
setIsSameDirectory(true)
|
||||
setIsDirectoryConfirm(false)
|
||||
return
|
||||
} else {
|
||||
setIsSameDirectory(false)
|
||||
setIsDirectoryConfirm(true)
|
||||
}
|
||||
setIsErrorSetNewDest(false)
|
||||
} catch (e) {
|
||||
console.error(`Error: ${e}`)
|
||||
setIsErrorSetNewDest(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const applyNewDestination = async () => {
|
||||
try {
|
||||
const appConfiguration: AppConfiguration =
|
||||
await window.core?.api?.getAppConfigurations()
|
||||
const currentJanDataFolder = appConfiguration.data_folder
|
||||
|
||||
appConfiguration.data_folder = newDestinationPath
|
||||
|
||||
await fs.syncFile(currentJanDataFolder, newDestinationPath)
|
||||
await window.core?.api?.updateAppConfiguration(appConfiguration)
|
||||
console.debug(
|
||||
`File sync finished from ${currentPath} to ${newDestinationPath}`
|
||||
)
|
||||
|
||||
setIsErrorSetNewDest(false)
|
||||
localStorage.setItem(SUCCESS_SET_NEW_DESTINATION, 'true')
|
||||
await window.core?.api?.relaunch()
|
||||
} catch (e) {
|
||||
console.error(`Error: ${e}`)
|
||||
setIsErrorSetNewDest(true)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setNewDestination,
|
||||
newDestinationPath,
|
||||
applyNewDestination,
|
||||
isSameDirectory,
|
||||
setIsDirectoryConfirm,
|
||||
isDirectoryConfirm,
|
||||
setIsSameDirectory,
|
||||
currentPath,
|
||||
isErrorSetNewDest,
|
||||
setIsErrorSetNewDest,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
import React from 'react'
|
||||
|
||||
import {
|
||||
Modal,
|
||||
ModalPortal,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalTitle,
|
||||
ModalFooter,
|
||||
ModalClose,
|
||||
Button,
|
||||
} from '@janhq/uikit'
|
||||
|
||||
import { useVaultDirectory } from '@/hooks/useVaultDirectory'
|
||||
|
||||
const ModalChangeDirectory = () => {
|
||||
const {
|
||||
isDirectoryConfirm,
|
||||
setIsDirectoryConfirm,
|
||||
applyNewDestination,
|
||||
newDestinationPath,
|
||||
} = useVaultDirectory()
|
||||
return (
|
||||
<Modal
|
||||
open={isDirectoryConfirm}
|
||||
onOpenChange={() => setIsDirectoryConfirm(false)}
|
||||
>
|
||||
<ModalPortal />
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<ModalTitle>Relocate Jan Data Folder</ModalTitle>
|
||||
</ModalHeader>
|
||||
<p className="text-muted-foreground">
|
||||
Are you sure you want to relocate Jan data folder to{' '}
|
||||
<span className="font-medium text-foreground">
|
||||
{newDestinationPath}
|
||||
</span>
|
||||
? A restart will be required afterward.
|
||||
</p>
|
||||
<ModalFooter>
|
||||
<div className="flex gap-x-2">
|
||||
<ModalClose asChild onClick={() => setIsDirectoryConfirm(false)}>
|
||||
<Button themes="ghost">Cancel</Button>
|
||||
</ModalClose>
|
||||
<ModalClose asChild>
|
||||
<Button onClick={applyNewDestination} autoFocus>
|
||||
Yes, Proceed
|
||||
</Button>
|
||||
</ModalClose>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModalChangeDirectory
|
||||
@ -0,0 +1,44 @@
|
||||
import React from 'react'
|
||||
|
||||
import {
|
||||
Modal,
|
||||
ModalPortal,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalTitle,
|
||||
ModalFooter,
|
||||
ModalClose,
|
||||
Button,
|
||||
} from '@janhq/uikit'
|
||||
|
||||
import { useVaultDirectory } from '@/hooks/useVaultDirectory'
|
||||
|
||||
const ModalErrorSetDestGlobal = () => {
|
||||
const { isErrorSetNewDest, setIsErrorSetNewDest } = useVaultDirectory()
|
||||
return (
|
||||
<Modal
|
||||
open={isErrorSetNewDest}
|
||||
onOpenChange={() => setIsErrorSetNewDest(false)}
|
||||
>
|
||||
<ModalPortal />
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<ModalTitle>Error Occurred</ModalTitle>
|
||||
</ModalHeader>
|
||||
<p className="text-muted-foreground">
|
||||
Oops! Something went wrong. Jan data folder remains the same. Please
|
||||
try again.
|
||||
</p>
|
||||
<ModalFooter>
|
||||
<div className="flex gap-x-2">
|
||||
<ModalClose asChild onClick={() => setIsErrorSetNewDest(false)}>
|
||||
<Button themes="danger">Got it</Button>
|
||||
</ModalClose>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModalErrorSetDestGlobal
|
||||
@ -0,0 +1,49 @@
|
||||
import React from 'react'
|
||||
|
||||
import {
|
||||
Modal,
|
||||
ModalPortal,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalTitle,
|
||||
ModalFooter,
|
||||
ModalClose,
|
||||
Button,
|
||||
} from '@janhq/uikit'
|
||||
|
||||
import { useVaultDirectory } from '@/hooks/useVaultDirectory'
|
||||
|
||||
const ModalSameDirectory = () => {
|
||||
const { isSameDirectory, setIsSameDirectory, setNewDestination } =
|
||||
useVaultDirectory()
|
||||
return (
|
||||
<Modal
|
||||
open={isSameDirectory}
|
||||
onOpenChange={() => setIsSameDirectory(false)}
|
||||
>
|
||||
<ModalPortal />
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<ModalTitle>Unable to move files</ModalTitle>
|
||||
</ModalHeader>
|
||||
<p className="text-muted-foreground">
|
||||
{`It seems like the folder you've chosen same with current directory`}
|
||||
</p>
|
||||
<ModalFooter>
|
||||
<div className="flex gap-x-2">
|
||||
<ModalClose asChild onClick={() => setIsSameDirectory(false)}>
|
||||
<Button themes="ghost">Cancel</Button>
|
||||
</ModalClose>
|
||||
<ModalClose asChild>
|
||||
<Button themes="danger" onClick={setNewDestination} autoFocus>
|
||||
Choose a different folder
|
||||
</Button>
|
||||
</ModalClose>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModalSameDirectory
|
||||
52
web/screens/Settings/Advanced/DataFolder/index.tsx
Normal file
52
web/screens/Settings/Advanced/DataFolder/index.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import { Button, Input } from '@janhq/uikit'
|
||||
import { PencilIcon, FolderOpenIcon } from 'lucide-react'
|
||||
|
||||
import { useVaultDirectory } from '@/hooks/useVaultDirectory'
|
||||
|
||||
import ModalChangeDirectory from './ModalChangeDirectory'
|
||||
import ModalErrorSetDestGlobal from './ModalErrorSetDestGlobal'
|
||||
import ModalSameDirectory from './ModalSameDirectory'
|
||||
|
||||
const DataFolder = () => {
|
||||
const { currentPath, setNewDestination } = useVaultDirectory()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">
|
||||
Jan Data Folder
|
||||
</h6>
|
||||
</div>
|
||||
<p className="leading-relaxed">
|
||||
Where messages, model configurations, and other user data are
|
||||
placed.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-3">
|
||||
<div className="relative">
|
||||
<Input value={currentPath} className="w-[240px] pr-8" disabled />
|
||||
<FolderOpenIcon
|
||||
size={16}
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
themes="outline"
|
||||
className="h-9 w-9 p-0"
|
||||
onClick={setNewDestination}
|
||||
>
|
||||
<PencilIcon size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<ModalSameDirectory />
|
||||
<ModalChangeDirectory />
|
||||
<ModalErrorSetDestGlobal />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default DataFolder
|
||||
@ -9,7 +9,7 @@ import {
|
||||
ChangeEvent,
|
||||
} from 'react'
|
||||
|
||||
import { fs, AppConfiguration } from '@janhq/core'
|
||||
import { fs } from '@janhq/core'
|
||||
import { Switch, Button, Input } from '@janhq/uikit'
|
||||
|
||||
import ShortcutModal from '@/containers/ShortcutModal'
|
||||
@ -20,6 +20,8 @@ import { FeatureToggleContext } from '@/context/FeatureToggle'
|
||||
|
||||
import { useSettings } from '@/hooks/useSettings'
|
||||
|
||||
import DataFolder from './DataFolder'
|
||||
|
||||
const Advanced = () => {
|
||||
const {
|
||||
experimentalFeature,
|
||||
@ -31,6 +33,7 @@ const Advanced = () => {
|
||||
} = useContext(FeatureToggleContext)
|
||||
const [partialProxy, setPartialProxy] = useState<string>(proxy)
|
||||
const [gpuEnabled, setGpuEnabled] = useState<boolean>(false)
|
||||
|
||||
const { readSettings, saveSettings, validateSettings, setShowNotification } =
|
||||
useSettings()
|
||||
const onProxyChange = useCallback(
|
||||
@ -46,17 +49,6 @@ const Advanced = () => {
|
||||
[setPartialProxy, setProxy]
|
||||
)
|
||||
|
||||
// TODO: remove me later.
|
||||
const [currentPath, setCurrentPath] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
window.core?.api
|
||||
?.getAppConfigurations()
|
||||
?.then((appConfig: AppConfiguration) => {
|
||||
setCurrentPath(appConfig.data_folder)
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
readSettings().then((settings) => {
|
||||
setGpuEnabled(settings.run_mode === 'gpu')
|
||||
@ -73,45 +65,55 @@ const Advanced = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const onJanVaultDirectoryClick = async () => {
|
||||
const destFolder = await window.core?.api?.selectDirectory()
|
||||
if (destFolder) {
|
||||
console.debug(`Destination folder selected: ${destFolder}`)
|
||||
|
||||
try {
|
||||
const appConfiguration: AppConfiguration =
|
||||
await window.core?.api?.getAppConfigurations()
|
||||
const currentJanDataFolder = appConfiguration.data_folder
|
||||
if (currentJanDataFolder === destFolder) {
|
||||
console.debug(
|
||||
`Destination folder is the same as current folder. Ignore..`
|
||||
)
|
||||
return
|
||||
}
|
||||
appConfiguration.data_folder = destFolder
|
||||
|
||||
await fs.syncFile(currentJanDataFolder, destFolder)
|
||||
await window.core?.api?.updateAppConfiguration(appConfiguration)
|
||||
console.debug(
|
||||
`File sync finished from ${currentJanDataFolder} to ${destFolder}`
|
||||
)
|
||||
await window.core?.api?.relaunch()
|
||||
} catch (e) {
|
||||
console.error(`Error: ${e}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="block w-full">
|
||||
{/* Keyboard shortcut */}
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">
|
||||
Keyboard Shortcuts
|
||||
</h6>
|
||||
</div>
|
||||
<p className="leading-relaxed">
|
||||
Shortcuts that you might find useful in Jan app.
|
||||
</p>
|
||||
</div>
|
||||
<ShortcutModal />
|
||||
</div>
|
||||
|
||||
{/* Experimental */}
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">
|
||||
Experimental Mode
|
||||
</h6>
|
||||
</div>
|
||||
<p className="leading-relaxed">
|
||||
Enable experimental features that may be unstable tested.
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={experimentalFeature}
|
||||
onCheckedChange={(e) => {
|
||||
if (e === true) {
|
||||
setExperimentalFeature(true)
|
||||
} else {
|
||||
setExperimentalFeature(false)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* CPU / GPU switching */}
|
||||
{!isMac && (
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="w-4/5 flex-shrink-0 space-y-1.5">
|
||||
<div className="flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">NVidia GPU</h6>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap leading-relaxed">
|
||||
<p className="leading-relaxed">
|
||||
Enable GPU acceleration for NVidia GPUs.
|
||||
</p>
|
||||
</div>
|
||||
@ -133,36 +135,17 @@ const Advanced = () => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{/* Experimental */}
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="w-4/5 flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">
|
||||
Experimental Mode
|
||||
</h6>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap leading-relaxed">
|
||||
Enable experimental features that may be unstable tested.
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={experimentalFeature}
|
||||
onCheckedChange={(e) => {
|
||||
if (e === true) {
|
||||
setExperimentalFeature(true)
|
||||
} else {
|
||||
setExperimentalFeature(false)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Directory */}
|
||||
{experimentalFeature && <DataFolder />}
|
||||
|
||||
{/* Proxy */}
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="w-4/5 flex-shrink-0 space-y-1.5">
|
||||
<div className="flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">HTTPS Proxy</h6>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap leading-relaxed">
|
||||
<p className="leading-relaxed">
|
||||
Specify the HTTPS proxy or leave blank (proxy auto-configuration and
|
||||
SOCKS not supported).
|
||||
</p>
|
||||
@ -173,15 +156,16 @@ const Advanced = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Ignore SSL certificates */}
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="w-4/5 flex-shrink-0 space-y-1.5">
|
||||
<div className="flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">
|
||||
Ignore SSL certificates
|
||||
</h6>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap leading-relaxed">
|
||||
<p className="leading-relaxed">
|
||||
Allow self-signed or unverified certificates - may be required for
|
||||
certain proxies.
|
||||
</p>
|
||||
@ -197,79 +181,19 @@ const Advanced = () => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{window.electronAPI && (
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="w-4/5 flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">
|
||||
Open App Directory
|
||||
</h6>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap leading-relaxed">
|
||||
Open the directory where your app data, like conversation history
|
||||
and model configurations, is located.
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
themes="secondary"
|
||||
onClick={() => window.electronAPI.openAppDirectory()}
|
||||
>
|
||||
Open
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Claer log */}
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="w-4/5 flex-shrink-0 space-y-1.5">
|
||||
<div className="flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">Clear logs</h6>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap leading-relaxed">
|
||||
Clear all logs from Jan app.
|
||||
</p>
|
||||
<p className="leading-relaxed">Clear all logs from Jan app.</p>
|
||||
</div>
|
||||
<Button size="sm" themes="secondary" onClick={clearLogs}>
|
||||
<Button size="sm" themes="secondaryDanger" onClick={clearLogs}>
|
||||
Clear
|
||||
</Button>
|
||||
</div>
|
||||
{experimentalFeature && (
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="w-4/5 flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">
|
||||
Jan Data Folder
|
||||
</h6>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap leading-relaxed">
|
||||
Where messages, model configurations, and other user data is
|
||||
placed.
|
||||
</p>
|
||||
<p className="whitespace-pre-wrap leading-relaxed text-gray-500">
|
||||
{`${currentPath}`}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
themes="secondary"
|
||||
onClick={onJanVaultDirectoryClick}
|
||||
>
|
||||
Select
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="w-4/5 flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">
|
||||
Keyboard Shortcuts
|
||||
</h6>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap leading-relaxed">
|
||||
Shortcuts that you might find useful in Jan app.
|
||||
</p>
|
||||
</div>
|
||||
<ShortcutModal />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ const ExtensionCatalog = () => {
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
<Button
|
||||
themes="secondary"
|
||||
themes="secondaryBlue"
|
||||
size="sm"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
>
|
||||
|
||||
@ -7,14 +7,14 @@ import { motion as m } from 'framer-motion'
|
||||
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
import { SUCCESS_SET_NEW_DESTINATION } from '@/hooks/useVaultDirectory'
|
||||
|
||||
import Advanced from '@/screens/Settings/Advanced'
|
||||
import AppearanceOptions from '@/screens/Settings/Appearance'
|
||||
import ExtensionCatalog from '@/screens/Settings/CoreExtensions'
|
||||
|
||||
import Models from '@/screens/Settings/Models'
|
||||
|
||||
import { formatExtensionsName } from '@/utils/converter'
|
||||
|
||||
const SettingsScreen = () => {
|
||||
const [activeStaticMenu, setActiveStaticMenu] = useState('My Models')
|
||||
const [menus, setMenus] = useState<any[]>([])
|
||||
@ -46,6 +46,12 @@ const SettingsScreen = () => {
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem(SUCCESS_SET_NEW_DESTINATION) === 'true') {
|
||||
setActiveStaticMenu('Advanced Settings')
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex h-full bg-background">
|
||||
<div className="flex h-full w-64 flex-shrink-0 flex-col overflow-y-auto border-r border-border">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user