fix: quick ask improvement (#2543)

* docs: Update README.md

* fix: quick ask improvement

Signed-off-by: James <james@jan.ai>

---------

Signed-off-by: James <james@jan.ai>
Co-authored-by: hieu-jan <150573299+henryh0x1@users.noreply.github.com>
Co-authored-by: James <james@jan.ai>
This commit is contained in:
NamH 2024-03-30 08:59:52 +07:00 committed by GitHub
parent 53b572029b
commit 96af5fb85a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 111 additions and 91 deletions

View File

@ -44,31 +44,31 @@ Jan is an open-source ChatGPT alternative that runs 100% offline on your compute
<td style="text-align:center"><b>Stable (Recommended)</b></td> <td style="text-align:center"><b>Stable (Recommended)</b></td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://github.com/janhq/jan/releases/download/v0.4.9/jan-win-x64-0.4.9.exe'> <a href='https://github.com/janhq/jan/releases/download/v0.4.9/jan-win-x64-0.4.9.exe'>
<img src='./docs/static/img/windows.png' style="height:14px; width: 14px" /> <img src='https://github.com/janhq/docs/blob/main/static/img/windows.png' style="height:14px; width: 14px" />
<b>jan.exe</b> <b>jan.exe</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://github.com/janhq/jan/releases/download/v0.4.9/jan-mac-x64-0.4.9.dmg'> <a href='https://github.com/janhq/jan/releases/download/v0.4.9/jan-mac-x64-0.4.9.dmg'>
<img src='./docs/static/img/mac.png' style="height:15px; width: 15px" /> <img src='https://github.com/janhq/docs/blob/main/static/img/mac.png' style="height:15px; width: 15px" />
<b>Intel</b> <b>Intel</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://github.com/janhq/jan/releases/download/v0.4.9/jan-mac-arm64-0.4.9.dmg'> <a href='https://github.com/janhq/jan/releases/download/v0.4.9/jan-mac-arm64-0.4.9.dmg'>
<img src='./docs/static/img/mac.png' style="height:15px; width: 15px" /> <img src='https://github.com/janhq/docs/blob/main/static/img/mac.png' style="height:15px; width: 15px" />
<b>M1/M2</b> <b>M1/M2</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://github.com/janhq/jan/releases/download/v0.4.9/jan-linux-amd64-0.4.9.deb'> <a href='https://github.com/janhq/jan/releases/download/v0.4.9/jan-linux-amd64-0.4.9.deb'>
<img src='./docs/static/img/linux.png' style="height:14px; width: 14px" /> <img src='https://github.com/janhq/docs/blob/main/static/img/linux.png' style="height:14px; width: 14px" />
<b>jan.deb</b> <b>jan.deb</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://github.com/janhq/jan/releases/download/v0.4.9/jan-linux-x86_64-0.4.9.AppImage'> <a href='https://github.com/janhq/jan/releases/download/v0.4.9/jan-linux-x86_64-0.4.9.AppImage'>
<img src='./docs/static/img/linux.png' style="height:14px; width: 14px" /> <img src='https://github.com/janhq/docs/blob/main/static/img/linux.png' style="height:14px; width: 14px" />
<b>jan.AppImage</b> <b>jan.AppImage</b>
</a> </a>
</td> </td>
@ -77,31 +77,31 @@ Jan is an open-source ChatGPT alternative that runs 100% offline on your compute
<td style="text-align:center"><b>Experimental (Nightly Build)</b></td> <td style="text-align:center"><b>Experimental (Nightly Build)</b></td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-win-x64-0.4.9-351.exe'> <a href='https://delta.jan.ai/latest/jan-win-x64-0.4.9-351.exe'>
<img src='./docs/static/img/windows.png' style="height:14px; width: 14px" /> <img src='https://github.com/janhq/docs/blob/main/static/img/windows.png' style="height:14px; width: 14px" />
<b>jan.exe</b> <b>jan.exe</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-mac-x64-0.4.9-351.dmg'> <a href='https://delta.jan.ai/latest/jan-mac-x64-0.4.9-351.dmg'>
<img src='./docs/static/img/mac.png' style="height:15px; width: 15px" /> <img src='https://github.com/janhq/docs/blob/main/static/img/mac.png' style="height:15px; width: 15px" />
<b>Intel</b> <b>Intel</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-mac-arm64-0.4.9-351.dmg'> <a href='https://delta.jan.ai/latest/jan-mac-arm64-0.4.9-351.dmg'>
<img src='./docs/static/img/mac.png' style="height:15px; width: 15px" /> <img src='https://github.com/janhq/docs/blob/main/static/img/mac.png' style="height:15px; width: 15px" />
<b>M1/M2</b> <b>M1/M2</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-linux-amd64-0.4.9-351.deb'> <a href='https://delta.jan.ai/latest/jan-linux-amd64-0.4.9-351.deb'>
<img src='./docs/static/img/linux.png' style="height:14px; width: 14px" /> <img src='https://github.com/janhq/docs/blob/main/static/img/linux.png' style="height:14px; width: 14px" />
<b>jan.deb</b> <b>jan.deb</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-linux-x86_64-0.4.9-351.AppImage'> <a href='https://delta.jan.ai/latest/jan-linux-x86_64-0.4.9-351.AppImage'>
<img src='./docs/static/img/linux.png' style="height:14px; width: 14px" /> <img src='https://github.com/janhq/docs/blob/main/static/img/linux.png' style="height:14px; width: 14px" />
<b>jan.AppImage</b> <b>jan.AppImage</b>
</a> </a>
</td> </td>

View File

@ -2,8 +2,6 @@ import { PropsWithChildren } from 'react'
import { Metadata } from 'next' import { Metadata } from 'next'
import Providers from '@/containers/Providers'
import '@/styles/main.scss' import '@/styles/main.scss'
export const metadata: Metadata = { export const metadata: Metadata = {
@ -16,8 +14,7 @@ export default function RootLayout({ children }: PropsWithChildren) {
return ( return (
<html lang="en" suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<body className="bg-white font-sans text-sm antialiased dark:bg-background"> <body className="bg-white font-sans text-sm antialiased dark:bg-background">
<div className="title-bar" /> {children}
<Providers>{children}</Providers>
</body> </body>
</html> </html>
) )

View File

@ -1,40 +1,11 @@
'use client'
import { useAtomValue } from 'jotai'
import BaseLayout from '@/containers/Layout' import BaseLayout from '@/containers/Layout'
import { MainViewState } from '@/constants/screens' import Providers from '@/containers/Providers'
import ChatScreen from '@/screens/Chat'
import ExploreModelsScreen from '@/screens/ExploreModels'
import LocalServerScreen from '@/screens/LocalServer'
import SettingsScreen from '@/screens/Settings'
import { mainViewStateAtom } from '@/helpers/atoms/App.atom'
export default function Page() { export default function Page() {
const mainViewState = useAtomValue(mainViewStateAtom) return (
<Providers>
let children = null <BaseLayout />
switch (mainViewState) { </Providers>
case MainViewState.Hub: )
children = <ExploreModelsScreen />
break
case MainViewState.Settings:
children = <SettingsScreen />
break
case MainViewState.LocalServer:
children = <LocalServerScreen />
break
default:
children = <ChatScreen />
break
}
return <BaseLayout>{children}</BaseLayout>
} }

View File

@ -10,6 +10,7 @@ const SelectedText = ({ onCleared }: { onCleared?: () => void }) => {
const containerRef = useRef<HTMLDivElement>(null) const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => { useEffect(() => {
if (window.core?.api?.quickAskSizeUpdated !== 'function') return
if (text.trim().length === 0) { if (text.trim().length === 0) {
window.core?.api?.quickAskSizeUpdated(0) window.core?.api?.quickAskSizeUpdated(0)
} else { } else {

32
web/app/search/layout.tsx Normal file
View File

@ -0,0 +1,32 @@
'use client'
import { useEffect } from 'react'
import ClipboardListener from '@/containers/Providers/ClipboardListener'
import JotaiWrapper from '@/containers/Providers/Jotai'
import ThemeWrapper from '@/containers/Providers/Theme'
import { setupCoreServices } from '@/services/coreService'
import Search from './page'
export default function RootLayout() {
useEffect(() => {
setupCoreServices()
}, [])
return (
<html lang="en" suppressHydrationWarning>
<body className="bg-white font-sans text-sm antialiased dark:bg-background">
<JotaiWrapper>
<ThemeWrapper>
<ClipboardListener>
<Search />
</ClipboardListener>
</ThemeWrapper>
</JotaiWrapper>
</body>
</html>
)
}

View File

@ -68,7 +68,7 @@ const TopBar = () => {
} }
return ( return (
<div className="fixed left-0 top-0 z-20 flex h-12 w-full border-b border-border bg-background/80 backdrop-blur-md"> <div className="title-bar fixed left-0 top-0 z-20 flex h-12 w-full border-b border-border bg-background/80 backdrop-blur-md">
{mainViewState !== MainViewState.Thread && {mainViewState !== MainViewState.Thread &&
mainViewState !== MainViewState.LocalServer ? ( mainViewState !== MainViewState.LocalServer ? (
<div className="relative left-16 flex w-[calc(100%-64px)] items-center justify-between space-x-4 pl-6 pr-2"> <div className="relative left-16 flex w-[calc(100%-64px)] items-center justify-between space-x-4 pl-6 pr-2">

View File

@ -1,4 +1,5 @@
import React, { PropsWithChildren, useEffect } from 'react' 'use client'
import React, { useEffect } from 'react'
import { useTheme } from 'next-themes' import { useTheme } from 'next-themes'
@ -23,12 +24,13 @@ import ImportModelOptionModal from '@/screens/Settings/ImportModelOptionModal'
import ImportingModelModal from '@/screens/Settings/ImportingModelModal' import ImportingModelModal from '@/screens/Settings/ImportingModelModal'
import SelectingModelModal from '@/screens/Settings/SelectingModelModal' import SelectingModelModal from '@/screens/Settings/SelectingModelModal'
import MainViewContainer from '../MainViewContainer'
import InstallingExtensionModal from './BottomBar/InstallingExtension/InstallingExtensionModal' import InstallingExtensionModal from './BottomBar/InstallingExtension/InstallingExtensionModal'
import { mainViewStateAtom } from '@/helpers/atoms/App.atom' import { mainViewStateAtom } from '@/helpers/atoms/App.atom'
const BaseLayout = (props: PropsWithChildren) => { const BaseLayout = () => {
const { children } = props
const [mainViewState, setMainViewState] = useAtom(mainViewStateAtom) const [mainViewState, setMainViewState] = useAtom(mainViewStateAtom)
const importModelStage = useAtomValue(getImportModelStageAtom) const importModelStage = useAtomValue(getImportModelStageAtom)
const { theme, setTheme } = useTheme() const { theme, setTheme } = useTheme()
@ -61,7 +63,7 @@ const BaseLayout = (props: PropsWithChildren) => {
}, },
}} }}
> >
{children} <MainViewContainer />
</m.div> </m.div>
<BottomBar /> <BottomBar />
</div> </div>

View File

@ -0,0 +1,37 @@
import { useAtomValue } from 'jotai'
import { MainViewState } from '@/constants/screens'
import ChatScreen from '@/screens/Chat'
import ExploreModelsScreen from '@/screens/ExploreModels'
import LocalServerScreen from '@/screens/LocalServer'
import SettingsScreen from '@/screens/Settings'
import { mainViewStateAtom } from '@/helpers/atoms/App.atom'
const MainViewContainer: React.FC = () => {
const mainViewState = useAtomValue(mainViewStateAtom)
let children = null
switch (mainViewState) {
case MainViewState.Hub:
children = <ExploreModelsScreen />
break
case MainViewState.Settings:
children = <SettingsScreen />
break
case MainViewState.LocalServer:
children = <LocalServerScreen />
break
default:
children = <ChatScreen />
break
}
return children
}
export default MainViewContainer

View File

@ -7,9 +7,11 @@ import { selectedTextAtom } from './Jotai'
const ClipboardListener = ({ children }: PropsWithChildren) => { const ClipboardListener = ({ children }: PropsWithChildren) => {
const setSelectedText = useSetAtom(selectedTextAtom) const setSelectedText = useSetAtom(selectedTextAtom)
window?.electronAPI?.onSelectedText((_event: string, text: string) => { if (typeof window !== 'undefined') {
setSelectedText(text) window?.electronAPI?.onSelectedText((_event: string, text: string) => {
}) setSelectedText(text)
})
}
return <Fragment>{children}</Fragment> return <Fragment>{children}</Fragment>
} }

View File

@ -1,15 +1,13 @@
import { Fragment, ReactNode, useRef } from 'react' import { Fragment, ReactNode } from 'react'
import { useSetAtom } from 'jotai' import { useSetAtom } from 'jotai'
import { useDebouncedCallback } from 'use-debounce'
import { MainViewState } from '@/constants/screens' import { MainViewState } from '@/constants/screens'
import useSendChatMessage from '@/hooks/useSendChatMessage' import useSendChatMessage from '@/hooks/useSendChatMessage'
import { showRightSideBarAtom } from '@/screens/Chat/Sidebar'
import { showLeftSideBarAtom } from './KeyListener'
import { mainViewStateAtom } from '@/helpers/atoms/App.atom' import { mainViewStateAtom } from '@/helpers/atoms/App.atom'
type Props = { type Props = {
@ -18,19 +16,15 @@ type Props = {
const QuickAskListener: React.FC<Props> = ({ children }) => { const QuickAskListener: React.FC<Props> = ({ children }) => {
const { sendChatMessage } = useSendChatMessage() const { sendChatMessage } = useSendChatMessage()
const setShowRightSideBar = useSetAtom(showRightSideBarAtom)
const setShowLeftSideBar = useSetAtom(showLeftSideBarAtom)
const setMainState = useSetAtom(mainViewStateAtom) const setMainState = useSetAtom(mainViewStateAtom)
const previousMessage = useRef('') const debounced = useDebouncedCallback((value) => {
setMainState(MainViewState.Thread)
sendChatMessage(value)
}, 300)
window.electronAPI?.onUserSubmitQuickAsk((_event: string, input: string) => { window.electronAPI?.onUserSubmitQuickAsk((_event: string, input: string) => {
if (previousMessage.current === input) return debounced(input)
setMainState(MainViewState.Thread)
setShowRightSideBar(false)
setShowLeftSideBar(false)
sendChatMessage(input)
previousMessage.current = input
}) })
return <Fragment>{children}</Fragment> return <Fragment>{children}</Fragment>

View File

@ -4,8 +4,6 @@ import { PropsWithChildren, useCallback, useEffect, useState } from 'react'
import { Toaster } from 'react-hot-toast' import { Toaster } from 'react-hot-toast'
import { usePathname } from 'next/navigation'
import { TooltipProvider } from '@janhq/uikit' import { TooltipProvider } from '@janhq/uikit'
import GPUDriverPrompt from '@/containers/GPUDriverPromptModal' import GPUDriverPrompt from '@/containers/GPUDriverPromptModal'
@ -29,10 +27,7 @@ import KeyListener from './KeyListener'
import { extensionManager } from '@/extension' import { extensionManager } from '@/extension'
const Providers = (props: PropsWithChildren) => { const Providers = ({ children }: PropsWithChildren) => {
const { children } = props
const pathname = usePathname()
const [setupCore, setSetupCore] = useState(false) const [setupCore, setSetupCore] = useState(false)
const [activated, setActivated] = useState(false) const [activated, setActivated] = useState(false)
const [settingUp, setSettingUp] = useState(false) const [settingUp, setSettingUp] = useState(false)
@ -43,11 +38,6 @@ const Providers = (props: PropsWithChildren) => {
setTimeout(async () => { setTimeout(async () => {
if (!isCoreExtensionInstalled()) { if (!isCoreExtensionInstalled()) {
// TODO: Proper window handle
// Do not migrate extension from quick ask window
if (pathname === '/search') {
return
}
setSettingUp(true) setSettingUp(true)
await setupBaseExtensions() await setupBaseExtensions()
return return
@ -57,7 +47,7 @@ const Providers = (props: PropsWithChildren) => {
setSettingUp(false) setSettingUp(false)
setActivated(true) setActivated(true)
}, 500) }, 500)
}, [pathname]) }, [])
// Services Setup // Services Setup
useEffect(() => { useEffect(() => {

View File

@ -46,6 +46,7 @@
"tailwindcss": "3.3.5", "tailwindcss": "3.3.5",
"ulidx": "^2.3.0", "ulidx": "^2.3.0",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"use-debounce": "^10.0.0",
"zod": "^3.22.4" "zod": "^3.22.4"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import { ExtensionTypeEnum } from '@janhq/core' import { ExtensionTypeEnum } from '@janhq/core'
import { extensionManager } from '@/extension/ExtensionManager' import { extensionManager } from '@/extension/ExtensionManager'
@ -13,6 +11,7 @@ export const isCoreExtensionInstalled = () => {
} }
return true return true
} }
export const setupBaseExtensions = async () => { export const setupBaseExtensions = async () => {
if (typeof window === 'undefined') { if (typeof window === 'undefined') {
return return

View File

@ -8,12 +8,6 @@
} }
.title-bar { .title-bar {
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 24px;
user-select: none;
-webkit-app-region: drag; -webkit-app-region: drag;
} }