* chore: upgrade marked-katex-extension (#3049) * fix: handle long word without space to avoid right panel disappears (#3048) * add time weighted retrieval (#2908) * add time weighted retrieval * add missing configuration for timeWeightedVectorStore * resolving conflict * add missing configuration for timeWeightedVectorStore * resolving conflict * fix linting issues * fix build failed due to requirement for useTimeWeightedRetriever in AssistantTool * update web packages complying the new structure --------- Co-authored-by: thu <thu@treehouse.finance> * fix: model dropdown search by configured model (#3047) * bump version (#3082) (#3083) Co-authored-by: Hoang Ha <64120343+hahuyhoang411@users.noreply.github.com> * Update cortex cpp nightly to version 0.4.18 (#3072) * Update cortex cpp nightly to version 0.4.17 * update linux downloadnitro * cortex 0.4.18 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Van Pham <64197333+Van-QA@users.noreply.github.com> * chore: update download.ts (#3088) infomation -> information * chore: cortex version update (#3098) * fix: handle words without space (#3101) * fix: handle long thread title without space (#3107) * fix: handle long thread title without space, and make searchbar autofocus inside model dropdown * feat: enable right click to show setting on thread items (#3108) * chore: Bump-cortex-0.4.17 (#3111) * Update cortex cpp nightly to version 0.4.18 (#3114) * Update cortex cpp nightly to version 0.4.18 * cortex 0.4.19 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Van Pham <64197333+Van-QA@users.noreply.github.com> * Chore: Add stop token for Gemma 2b (#3125) * add stop token * Bump version * fix: set specific version for terminate (#3126) Signed-off-by: James <namnh0122@gmail.com> * feat: add claude 3.5 sonnet (#3129) Signed-off-by: James <namnh0122@gmail.com> * feat: add options config spell check for chat input (#3131) * fixed grammar nits (#3132) * Update cortex cpp nightly to version 0.4.20 * fix: toggle button for expand log section on modal troubleshoot (#3130) * fix: add tooltip messages toolbar (#3138) * fix: handle error message when apikey is not setup (#3149) * fix: title thread not updated on input edit title (#3148) * merge dev * fix move jan folder * Update electron/preload.ts * refactor * Update electron/preload.ts * fix wrong param * use correct method * chore: fix lint --------- Signed-off-by: James <namnh0122@gmail.com> Co-authored-by: Faisal Amir <urmauur@gmail.com> Co-authored-by: Nathan <thu.nhuanh99@gmail.com> Co-authored-by: thu <thu@treehouse.finance> Co-authored-by: Van Pham <64197333+Van-QA@users.noreply.github.com> Co-authored-by: Hoang Ha <64120343+hahuyhoang411@users.noreply.github.com> Co-authored-by: jan-service-account <136811300+jan-service-account@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Ikko Eltociear Ashimine <eltociear@gmail.com> Co-authored-by: NamH <NamNh0122@gmail.com> Co-authored-by: Saurabh <saurabhrai1717@gmail.com> Co-authored-by: Louis <louis@jan.ai>
198 lines
5.7 KiB
TypeScript
198 lines
5.7 KiB
TypeScript
import { useCallback, useEffect, useState } from 'react'
|
|
|
|
import {
|
|
BaseExtension,
|
|
Compatibility,
|
|
InstallationState,
|
|
abortDownload,
|
|
} from '@janhq/core'
|
|
import { Button, Progress, Tooltip } from '@janhq/joi'
|
|
|
|
import { InfoCircledIcon } from '@radix-ui/react-icons'
|
|
import { useAtomValue } from 'jotai'
|
|
|
|
import { Marked, Renderer } from 'marked'
|
|
|
|
import { extensionManager } from '@/extension'
|
|
import { installingExtensionAtom } from '@/helpers/atoms/Extension.atom'
|
|
|
|
type Props = {
|
|
item: BaseExtension
|
|
}
|
|
|
|
const ExtensionItem: React.FC<Props> = ({ item }) => {
|
|
const [compatibility, setCompatibility] = useState<Compatibility | undefined>(
|
|
undefined
|
|
)
|
|
const [installState, setInstallState] =
|
|
useState<InstallationState>('NotRequired')
|
|
const installingExtensions = useAtomValue(installingExtensionAtom)
|
|
const isInstalling = installingExtensions.some(
|
|
(e) => e.extensionId === item.name
|
|
)
|
|
|
|
const progress = isInstalling
|
|
? (installingExtensions.find((e) => e.extensionId === item.name)
|
|
?.percentage ?? -1)
|
|
: -1
|
|
|
|
useEffect(() => {
|
|
const getExtensionInstallationState = async () => {
|
|
const extension = extensionManager.getByName(item.name)
|
|
if (!extension) return
|
|
|
|
if (typeof extension?.installationState === 'function') {
|
|
const installState = await extension.installationState()
|
|
setInstallState(installState)
|
|
}
|
|
}
|
|
|
|
getExtensionInstallationState()
|
|
}, [item.name, isInstalling])
|
|
|
|
useEffect(() => {
|
|
const extension = extensionManager.getByName(item.name)
|
|
if (!extension) return
|
|
setCompatibility(extension.compatibility())
|
|
}, [setCompatibility, item.name])
|
|
|
|
const onInstallClick = useCallback(async () => {
|
|
const extension = extensionManager.getByName(item.name)
|
|
if (!extension) return
|
|
|
|
await extension.install()
|
|
}, [item.name])
|
|
|
|
const onCancelInstallingClick = () => {
|
|
const extension = installingExtensions.find(
|
|
(e) => e.extensionId === item.name
|
|
)
|
|
if (extension?.localPath) {
|
|
abortDownload(extension.localPath)
|
|
}
|
|
}
|
|
|
|
const description = marked.parse(item.description ?? '', { async: false })
|
|
|
|
return (
|
|
<div className="mx-4 flex items-start justify-between border-b border-[hsla(var(--app-border))] py-6 first:pt-4 last:border-none">
|
|
<div className="flex-1 flex-shrink-0 space-y-1">
|
|
<div className="flex items-center gap-x-2">
|
|
<h6 className="font-semibold">Additional Dependencies</h6>
|
|
</div>
|
|
<div
|
|
dangerouslySetInnerHTML={{ __html: description }}
|
|
className="font-medium leading-relaxed text-[hsla(var(--text-secondary))]"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex min-w-[150px] flex-row justify-end">
|
|
<InstallStateIndicator
|
|
installProgress={progress}
|
|
installState={installState}
|
|
compatibility={compatibility}
|
|
onInstallClick={onInstallClick}
|
|
onCancelClick={onCancelInstallingClick}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
type InstallStateProps = {
|
|
installProgress: number
|
|
compatibility?: Compatibility
|
|
installState: InstallationState
|
|
onInstallClick: () => void
|
|
onCancelClick: () => void
|
|
}
|
|
|
|
const InstallStateIndicator: React.FC<InstallStateProps> = ({
|
|
installProgress,
|
|
compatibility,
|
|
installState,
|
|
onInstallClick,
|
|
onCancelClick,
|
|
}) => {
|
|
if (installProgress !== -1) {
|
|
const progress = installProgress * 100
|
|
return (
|
|
<div className="text-primary dark flex h-10 flex-row items-center justify-center space-x-2 rounded-lg px-4">
|
|
<button onClick={onCancelClick} className="text-primary font-semibold">
|
|
Cancel
|
|
</button>
|
|
<div className="flex w-[113px] flex-row items-center justify-center space-x-2 rounded-md px-2 py-[2px]">
|
|
<Progress className="h-1 w-[69px]" value={progress} />
|
|
<span className="text-primary text-xs font-bold">
|
|
{progress.toFixed(0)}%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
switch (installState) {
|
|
case 'Installed':
|
|
return (
|
|
<div className="rounded-md px-3 py-1.5 font-semibold text-[hsla(var(--text-secondary))]">
|
|
Installed
|
|
</div>
|
|
)
|
|
case 'NotCompatible':
|
|
return (
|
|
<div className="rounded-md px-3 py-1.5 font-semibold text-[hsla(var(--text-secondary))]">
|
|
<div className="flex flex-row items-center justify-center gap-1">
|
|
Incompatible
|
|
<Tooltip
|
|
trigger={
|
|
<InfoCircledIcon className="cursor-pointer text-[hsla(var(--text-secondary))]" />
|
|
}
|
|
content={
|
|
compatibility &&
|
|
!compatibility['platform']?.includes(PLATFORM) ? (
|
|
<span>
|
|
Only available on
|
|
{compatibility?.platform
|
|
?.map((e: string) =>
|
|
e === 'win32'
|
|
? 'Windows'
|
|
: e === 'linux'
|
|
? 'Linux'
|
|
: 'MacOS'
|
|
)
|
|
.join(', ')}
|
|
</span>
|
|
) : (
|
|
<span>Your GPUs are not compatible with this extension</span>
|
|
)
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
case 'NotInstalled':
|
|
return (
|
|
<Button size="small" variant="soft" onClick={onInstallClick}>
|
|
Install
|
|
</Button>
|
|
)
|
|
default:
|
|
return <div></div>
|
|
}
|
|
}
|
|
|
|
const marked: Marked = new Marked({
|
|
renderer: {
|
|
link: (href, title, text) => {
|
|
return Renderer.prototype.link
|
|
?.apply(this, [href, title, text])
|
|
.replace(
|
|
'<a',
|
|
"<a class='text-[hsla(var(--app-link))]' target='_blank'"
|
|
)
|
|
},
|
|
},
|
|
})
|
|
|
|
export default ExtensionItem
|