diff --git a/uikit/src/badge/styles.scss b/uikit/src/badge/styles.scss index e5a783d88d..cf8e52c8be 100644 --- a/uikit/src/badge/styles.scss +++ b/uikit/src/badge/styles.scss @@ -6,7 +6,7 @@ } &-success { - @apply border-transparent bg-green-500 text-green-900 hover:bg-green-500/80; + @apply border-transparent bg-green-100 text-green-600; } &-secondary { diff --git a/web/app/page.tsx b/web/app/page.tsx index b07ec6419e..cae3262a7c 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -21,7 +21,7 @@ export default function Page() { children = break - case MainViewState.Setting: + case MainViewState.Settings: children = break diff --git a/web/constants/screens.ts b/web/constants/screens.ts index b62e4a0ffd..19f82aaac2 100644 --- a/web/constants/screens.ts +++ b/web/constants/screens.ts @@ -1,7 +1,7 @@ export enum MainViewState { Hub, MyModels, - Setting, + Settings, Thread, SystemMonitor, } diff --git a/web/containers/CardSidebar/index.tsx b/web/containers/CardSidebar/index.tsx index 82455181c8..38264e457c 100644 --- a/web/containers/CardSidebar/index.tsx +++ b/web/containers/CardSidebar/index.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useState } from 'react' +import { ReactNode, useState, useRef } from 'react' import { ChevronDownIcon, @@ -24,7 +24,10 @@ export default function CardSidebar({ }: Props) { const [show, setShow] = useState(true) const [more, setMore] = useState(false) - const ref = useClickOutside(() => setMore(false)) + const [menu, setMenu] = useState(null) + const [toggle, setToggle] = useState(null) + + useClickOutside(() => setMore(false), null, [menu, toggle]) return (
{title}
setMore(!more)} > @@ -60,7 +64,7 @@ export default function CardSidebar({ {more && (
), - state: MainViewState.Setting, + state: MainViewState.Settings, }, ] return ( diff --git a/web/containers/Layout/TopBar/CommandSearch/index.tsx b/web/containers/Layout/TopBar/CommandSearch/index.tsx index 814ffbcbd7..d83feb22e0 100644 --- a/web/containers/Layout/TopBar/CommandSearch/index.tsx +++ b/web/containers/Layout/TopBar/CommandSearch/index.tsx @@ -38,7 +38,7 @@ export default function CommandSearch() { { name: 'Settings', icon: , - state: MainViewState.Setting, + state: MainViewState.Settings, shortcut: , }, ] @@ -51,7 +51,7 @@ export default function CommandSearch() { } if (e.key === ',' && (e.metaKey || e.ctrlKey)) { e.preventDefault() - setMainViewState(MainViewState.Setting) + setMainViewState(MainViewState.Settings) } } document.addEventListener('keydown', down) diff --git a/web/containers/Layout/TopBar/index.tsx b/web/containers/Layout/TopBar/index.tsx index da3768c762..aa7912bd3d 100644 --- a/web/containers/Layout/TopBar/index.tsx +++ b/web/containers/Layout/TopBar/index.tsx @@ -27,7 +27,7 @@ const TopBar = () => { return activeThread ? activeThread?.title : 'New Thread' default: - return MainViewState[viewStateName].replace(/([A-Z])/g, ' $1').trim() + return MainViewState[viewStateName]?.replace(/([A-Z])/g, ' $1').trim() } } diff --git a/web/containers/Toast/index.tsx b/web/containers/Toast/index.tsx index 9833f99d95..c5e5f03da2 100644 --- a/web/containers/Toast/index.tsx +++ b/web/containers/Toast/index.tsx @@ -16,7 +16,7 @@ export function toaster(props: Props) { return (
(null) + const [toggle, setToggle] = useState(null) + useClickOutside(() => setMore(false), null, [menu, toggle]) + + const { activeModel, startModel, stopModel, stateModel } = useActiveModel() + const { deleteModel } = useDeleteModel() + + const isActiveModel = stateModel.model === props.data.id + + const onModelActionClick = (modelId: string) => { + if (activeModel && activeModel.id === modelId) { + stopModel(modelId) + } else { + startModel(modelId) + } + } + + return ( + + {props.data.name} + {props.data.id} + + + {toGigabytes(props.data.metadata.size)} + + + + v{props.data.version} + + + {stateModel.loading && stateModel.model === props.data.id ? ( + + + + {stateModel.state === 'start' ? 'Starting...' : 'Stopping...'} + + + ) : activeModel && activeModel.id === props.data.id ? ( + + + Active + + ) : ( + + + Inactive + + )} + + +
{ + setMore(!more) + }} + > + +
+ {more && ( +
+
{ + onModelActionClick(props.data.id) + setMore(false) + }} + > + {activeModel && activeModel.id === props.data.id ? ( + + ) : ( + + )} + + {isActiveModel ? stateModel.state : 'Start'} +  Model + +
+
{ + setTimeout(async () => { + await stopModel(props.data.id) + deleteModel(props.data) + }, 500) + setMore(false) + }} + > + + + Delete Model + +
+
+ )} + + + ) +} diff --git a/web/screens/Settings/Models/index.tsx b/web/screens/Settings/Models/index.tsx new file mode 100644 index 0000000000..79859c2758 --- /dev/null +++ b/web/screens/Settings/Models/index.tsx @@ -0,0 +1,65 @@ +import { useState } from 'react' + +import { Input } from '@janhq/uikit' + +import { SearchIcon } from 'lucide-react' + +import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' + +import RowModel from './Row' + +const Column = ['Name', 'Model ID', 'Size', 'Version', 'Status', ''] + +export default function Models() { + const { downloadedModels } = useGetDownloadedModels() + const [searchValue, setsearchValue] = useState('') + + const filteredDownloadedModels = downloadedModels.filter((x) => { + return x.name.toLowerCase().includes(searchValue.toLowerCase()) + }) + + return ( +
+
+
+ + { + setsearchValue(e.target.value) + }} + /> +
+
+
+ + + + {Column.map((col, i) => { + return ( + + ) + })} + + + + {filteredDownloadedModels + ? filteredDownloadedModels.map((x, i) => { + return + }) + : null} + +
+ {col} +
+
+
+ ) +} diff --git a/web/screens/Settings/index.tsx b/web/screens/Settings/index.tsx index b14039f749..77e70d33b9 100644 --- a/web/screens/Settings/index.tsx +++ b/web/screens/Settings/index.tsx @@ -12,6 +12,8 @@ import AppearanceOptions from '@/screens/Settings/Appearance' import ExtensionCatalog from '@/screens/Settings/CoreExtensions/ExtensionsCatalog' import PreferenceExtensions from '@/screens/Settings/CoreExtensions/PreferenceExtensions' +import Models from '@/screens/Settings/Models' + import { formatExtensionsName } from '@/utils/converter' const SettingsScreen = () => { @@ -24,7 +26,7 @@ const SettingsScreen = () => { const menu = ['Appearance'] if (typeof window !== 'undefined' && window.electronAPI) { - menu.push('Core Extensions') + menu.push('Extensions') } menu.push('Advanced') setMenus(menu) @@ -40,7 +42,7 @@ const SettingsScreen = () => { const handleShowOptions = (menu: string) => { switch (menu) { - case 'Core Extensions': + case 'Extensions': return case 'Appearance': @@ -49,6 +51,9 @@ const SettingsScreen = () => { case 'Advanced': return + case 'Models': + return + default: return ( {
{menus.map((menu, i) => { @@ -135,6 +140,38 @@ const SettingsScreen = () => { })}
+ +
+ +
+
+
{ + setActiveStaticMenu('Models') + setActivePreferenceExtension('') + }} + className="block w-full cursor-pointer" + > + + Models + +
+ {activeStaticMenu === 'Models' && ( + + )} +
+
+
diff --git a/web/screens/SystemMonitor/index.tsx b/web/screens/SystemMonitor/index.tsx index a1e511b86b..51547f2926 100644 --- a/web/screens/SystemMonitor/index.tsx +++ b/web/screens/SystemMonitor/index.tsx @@ -30,7 +30,7 @@ export default function SystemMonitorScreen() {

ram ({Math.round((usedRam / totalRam) * 100)}%)

- + {toGigabytes(usedRam)} GB of {toGigabytes(totalRam)} GB used
@@ -46,7 +46,7 @@ export default function SystemMonitorScreen() {

cpu ({cpuUsage}%)

- + {cpuUsage}% of 100%
@@ -79,15 +79,17 @@ export default function SystemMonitorScreen() { - {activeModel.name} - {activeModel.id} + + {activeModel.name} + + {activeModel.id} {toGigabytes(activeModel.metadata.size)} - {activeModel.version} + v{activeModel.version}