Skip to content

Commit

Permalink
Move my models into setting page
Browse files Browse the repository at this point in the history
  • Loading branch information
urmauur authored and louis-jan committed Dec 4, 2023
1 parent 159b4c9 commit 0074f40
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 20 deletions.
2 changes: 1 addition & 1 deletion uikit/src/badge/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion web/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function Page() {
children = <ExploreModelsScreen />
break

case MainViewState.Setting:
case MainViewState.Settings:
children = <SettingsScreen />
break

Expand Down
2 changes: 1 addition & 1 deletion web/constants/screens.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export enum MainViewState {
Hub,
MyModels,
Setting,
Settings,
Thread,
SystemMonitor,
}
10 changes: 7 additions & 3 deletions web/containers/CardSidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ReactNode, useState } from 'react'
import { ReactNode, useState, useRef } from 'react'

import {
ChevronDownIcon,
Expand All @@ -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<HTMLDivElement | null>(null)
const [toggle, setToggle] = useState<HTMLDivElement | null>(null)

useClickOutside(() => setMore(false), null, [menu, toggle])

return (
<div
Expand Down Expand Up @@ -52,6 +55,7 @@ export default function CardSidebar({
<span className="font-bold">{title}</span>
</button>
<div
ref={setToggle}
className="cursor-pointer bg-zinc-200 p-2 dark:bg-zinc-600/10"
onClick={() => setMore(!more)}
>
Expand All @@ -60,7 +64,7 @@ export default function CardSidebar({
{more && (
<div
className="absolute right-0 top-8 z-20 w-52 overflow-hidden rounded-lg border border-border bg-background shadow-lg"
ref={ref}
ref={setMenu}
>
<div
className="flex cursor-pointer items-center space-x-2 px-4 py-2 hover:bg-secondary"
Expand Down
2 changes: 1 addition & 1 deletion web/containers/Layout/Ribbon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default function RibbonNav() {
className="flex-shrink-0 text-muted-foreground"
/>
),
state: MainViewState.Setting,
state: MainViewState.Settings,
},
]
return (
Expand Down
4 changes: 2 additions & 2 deletions web/containers/Layout/TopBar/CommandSearch/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default function CommandSearch() {
{
name: 'Settings',
icon: <SettingsIcon size={16} className="mr-3 text-muted-foreground" />,
state: MainViewState.Setting,
state: MainViewState.Settings,
shortcut: <ShortCut menu="," />,
},
]
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion web/containers/Layout/TopBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down
2 changes: 1 addition & 1 deletion web/containers/Toast/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function toaster(props: Props) {
return (
<div
className={twMerge(
'relative flex min-w-[200px] max-w-[350px] gap-x-4 rounded-lg border border-border bg-background px-4 py-3',
'unset-drag relative flex min-w-[200px] max-w-[350px] gap-x-4 rounded-lg border border-border bg-background px-4 py-3',
t.visible ? 'animate-enter' : 'animate-leave',
type === 'success' && 'bg-primary text-primary-foreground'
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import React, { useState, useEffect, useRef, useContext } from 'react'

import { Switch, Button } from '@janhq/uikit'
import { Button } from '@janhq/uikit'

import Loader from '@/containers/Loader'

Expand Down
137 changes: 137 additions & 0 deletions web/screens/Settings/Models/Row.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { useState } from 'react'

import { Model } from '@janhq/core'
import { Badge } from '@janhq/uikit'

import {
MoreVerticalIcon,
Trash2Icon,
PlayIcon,
StopCircleIcon,
} from 'lucide-react'

import { useActiveModel } from '@/hooks/useActiveModel'
import { useClickOutside } from '@/hooks/useClickOutside'

import useDeleteModel from '@/hooks/useDeleteModel'

import { toGigabytes } from '@/utils/converter'

type RowModelProps = {
data: Model
}

export default function RowModel(props: RowModelProps) {
const [more, setMore] = useState(false)

const [menu, setMenu] = useState<HTMLDivElement | null>(null)
const [toggle, setToggle] = useState<HTMLDivElement | null>(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 (
<tr className="relative border-b border-border last:border-none">
<td className="px-6 py-4 font-bold">{props.data.name}</td>
<td className="px-6 py-4 font-bold">{props.data.id}</td>
<td className="px-6 py-4">
<Badge themes="secondary">
{toGigabytes(props.data.metadata.size)}
</Badge>
</td>
<td className="px-6 py-4">
<Badge themes="secondary">v{props.data.version}</Badge>
</td>
<td className="px-6 py-4">
{stateModel.loading && stateModel.model === props.data.id ? (
<Badge
className="inline-flex items-center space-x-2"
themes="secondary"
>
<span className="h-2 w-2 rounded-full bg-gray-500" />
<span className="capitalize">
{stateModel.state === 'start' ? 'Starting...' : 'Stopping...'}
</span>
</Badge>
) : activeModel && activeModel.id === props.data.id ? (
<Badge
themes="success"
className="inline-flex items-center space-x-2"
>
<span className="h-2 w-2 rounded-full bg-green-500" />
<span>Active</span>
</Badge>
) : (
<Badge
themes="secondary"
className="inline-flex items-center space-x-2"
>
<span className="h-2 w-2 rounded-full bg-gray-500" />
<span>Inactive</span>
</Badge>
)}
</td>
<td className="px-6 py-4 text-center">
<div
className="cursor-pointer"
ref={setToggle}
onClick={() => {
setMore(!more)
}}
>
<MoreVerticalIcon className="h-5 w-5" />
</div>
{more && (
<div
className="absolute right-4 top-10 z-20 w-52 overflow-hidden rounded-lg border border-border bg-background py-2 shadow-lg"
ref={setMenu}
>
<div
className="flex cursor-pointer items-center space-x-2 px-4 py-2 hover:bg-secondary"
onClick={() => {
onModelActionClick(props.data.id)
setMore(false)
}}
>
{activeModel && activeModel.id === props.data.id ? (
<StopCircleIcon size={16} className="text-muted-foreground" />
) : (
<PlayIcon size={16} className="text-muted-foreground" />
)}
<span className="text-bold capitalize text-black dark:text-muted-foreground">
{isActiveModel ? stateModel.state : 'Start'}
&nbsp;Model
</span>
</div>
<div
className="flex cursor-pointer items-center space-x-2 px-4 py-2 hover:bg-secondary"
onClick={() => {
setTimeout(async () => {
await stopModel(props.data.id)
deleteModel(props.data)
}, 500)
setMore(false)
}}
>
<Trash2Icon size={16} className="text-muted-foreground" />
<span className="text-bold text-black dark:text-muted-foreground">
Delete Model
</span>
</div>
</div>
)}
</td>
</tr>
)
}
65 changes: 65 additions & 0 deletions web/screens/Settings/Models/index.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="rounded-xl border border-border shadow-sm">
<div className="px-6 py-5">
<div className="relative w-1/3">
<SearchIcon
size={20}
className="absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground"
/>
<Input
placeholder="Search"
className="pl-8"
onChange={(e) => {
setsearchValue(e.target.value)
}}
/>
</div>
</div>
<div className="relative">
<table className="w-full px-8">
<thead className="w-full border-b border-border bg-secondary">
<tr>
{Column.map((col, i) => {
return (
<th
key={i}
className="px-6 py-2 text-left font-normal last:text-center"
>
{col}
</th>
)
})}
</tr>
</thead>
<tbody>
{filteredDownloadedModels
? filteredDownloadedModels.map((x, i) => {
return <RowModel key={i} data={x} />
})
: null}
</tbody>
</table>
</div>
</div>
)
}
Loading

0 comments on commit 0074f40

Please sign in to comment.