diff --git a/web/app/search/layout.tsx b/web/app/search/layout.tsx
index 6c491c381..8af34dc00 100644
--- a/web/app/search/layout.tsx
+++ b/web/app/search/layout.tsx
@@ -52,9 +52,8 @@ export default function RootLayout() {
-
-
-
+
+
diff --git a/web/containers/ListContainer/index.tsx b/web/containers/ListContainer/index.tsx
index 2b720fb43..c55287bc6 100644
--- a/web/containers/ListContainer/index.tsx
+++ b/web/containers/ListContainer/index.tsx
@@ -1,12 +1,8 @@
-import { ReactNode, useCallback, useEffect, useRef } from 'react'
+import { PropsWithChildren, useCallback, useEffect, useRef } from 'react'
import { ScrollArea } from '@janhq/joi'
-type Props = {
- children: ReactNode
-}
-
-const ListContainer = ({ children }: Props) => {
+const ListContainer = ({ children }: PropsWithChildren) => {
const listRef = useRef(null)
const prevScrollTop = useRef(0)
const isUserManuallyScrollingUp = useRef(false)
diff --git a/web/containers/ModelConfigInput/index.test.tsx b/web/containers/ModelConfigInput/index.test.tsx
index b92bdfcb2..cf9cb9da3 100644
--- a/web/containers/ModelConfigInput/index.test.tsx
+++ b/web/containers/ModelConfigInput/index.test.tsx
@@ -2,7 +2,6 @@ import '@testing-library/jest-dom'
import React from 'react'
import { render, fireEvent } from '@testing-library/react'
import ModelConfigInput from './index'
-import { Tooltip } from '@janhq/joi'
// Mocking the Tooltip component to simplify testing
jest.mock('@janhq/joi', () => ({
diff --git a/web/containers/Providers/AppUpdateListener.tsx b/web/containers/Providers/AppUpdateListener.tsx
index 77b39bb06..4d05f6010 100644
--- a/web/containers/Providers/AppUpdateListener.tsx
+++ b/web/containers/Providers/AppUpdateListener.tsx
@@ -1,4 +1,4 @@
-import { Fragment, PropsWithChildren, useEffect } from 'react'
+import { Fragment, useEffect } from 'react'
import { AppUpdateInfo } from '@janhq/core'
import { useSetAtom } from 'jotai'
@@ -8,7 +8,7 @@ import {
updateVersionErrorAtom,
} from '@/helpers/atoms/App.atom'
-const AppUpdateListener = ({ children }: PropsWithChildren) => {
+const AppUpdateListener = () => {
const setProgress = useSetAtom(appDownloadProgressAtom)
const setUpdateVersionError = useSetAtom(updateVersionErrorAtom)
@@ -39,7 +39,7 @@ const AppUpdateListener = ({ children }: PropsWithChildren) => {
}
}, [setProgress, setUpdateVersionError])
- return {children}
+ return
}
export default AppUpdateListener
diff --git a/web/containers/Providers/ClipboardListener.tsx b/web/containers/Providers/ClipboardListener.tsx
index 2d9910b9b..d1124794e 100644
--- a/web/containers/Providers/ClipboardListener.tsx
+++ b/web/containers/Providers/ClipboardListener.tsx
@@ -1,10 +1,10 @@
-import { Fragment, PropsWithChildren } from 'react'
+import { Fragment } from 'react'
import { useSetAtom } from 'jotai'
import { selectedTextAtom } from './Jotai'
-const ClipboardListener = ({ children }: PropsWithChildren) => {
+const ClipboardListener = () => {
const setSelectedText = useSetAtom(selectedTextAtom)
if (typeof window !== 'undefined') {
@@ -13,7 +13,7 @@ const ClipboardListener = ({ children }: PropsWithChildren) => {
})
}
- return {children}
+ return
}
export default ClipboardListener
diff --git a/web/containers/Providers/DataLoader.tsx b/web/containers/Providers/DataLoader.tsx
index 245c254ac..470294996 100644
--- a/web/containers/Providers/DataLoader.tsx
+++ b/web/containers/Providers/DataLoader.tsx
@@ -1,13 +1,12 @@
'use client'
-import { Fragment, ReactNode, useEffect } from 'react'
+import { Fragment, useEffect } from 'react'
import { AppConfiguration, getUserHomePath } from '@janhq/core'
import { useSetAtom } from 'jotai'
import useAssistants from '@/hooks/useAssistants'
import useGetSystemResources from '@/hooks/useGetSystemResources'
-import { useLoadTheme } from '@/hooks/useLoadTheme'
import useModels from '@/hooks/useModels'
import useThreads from '@/hooks/useThreads'
@@ -20,27 +19,20 @@ import {
} from '@/helpers/atoms/AppConfig.atom'
import { janSettingScreenAtom } from '@/helpers/atoms/Setting.atom'
-type Props = {
- children: ReactNode
-}
-
-const DataLoader: React.FC = ({ children }) => {
+const DataLoader: React.FC = () => {
const setJanDataFolderPath = useSetAtom(janDataFolderPathAtom)
const setQuickAskEnabled = useSetAtom(quickAskEnabledAtom)
const setJanDefaultDataFolder = useSetAtom(defaultJanDataFolderAtom)
const setJanSettingScreen = useSetAtom(janSettingScreenAtom)
- const { loadDataModel, configurePullOptions } = useModels()
+ const { getData: loadModels } = useModels()
useThreads()
useAssistants()
useGetSystemResources()
- useLoadTheme()
useEffect(() => {
// Load data once
- loadDataModel()
- // Configure pull options once
- configurePullOptions()
+ loadModels()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
@@ -71,7 +63,7 @@ const DataLoader: React.FC = ({ children }) => {
console.debug('Load Data...')
- return {children}
+ return
}
export default DataLoader
diff --git a/web/containers/Providers/DeepLinkListener.tsx b/web/containers/Providers/DeepLinkListener.tsx
index d5941204f..b991996a2 100644
--- a/web/containers/Providers/DeepLinkListener.tsx
+++ b/web/containers/Providers/DeepLinkListener.tsx
@@ -1,4 +1,4 @@
-import { Fragment, ReactNode } from 'react'
+import { Fragment } from 'react'
import { useSetAtom } from 'jotai'
@@ -13,11 +13,8 @@ import {
importHuggingFaceModelStageAtom,
importingHuggingFaceRepoDataAtom,
} from '@/helpers/atoms/HuggingFace.atom'
-type Props = {
- children: ReactNode
-}
-const DeepLinkListener: React.FC = ({ children }) => {
+const DeepLinkListener: React.FC = () => {
const { getHfRepoData } = useGetHFRepoData()
const setLoadingInfo = useSetAtom(loadingModalInfoAtom)
const setImportingHuggingFaceRepoData = useSetAtom(
@@ -69,7 +66,7 @@ const DeepLinkListener: React.FC = ({ children }) => {
handleDeepLinkAction(action)
})
- return {children}
+ return
}
type DeepLinkAction = {
diff --git a/web/containers/Providers/EventListener.tsx b/web/containers/Providers/EventListener.tsx
index c1dcf7c40..bad1afda9 100644
--- a/web/containers/Providers/EventListener.tsx
+++ b/web/containers/Providers/EventListener.tsx
@@ -1,4 +1,4 @@
-import { PropsWithChildren, useCallback, useEffect } from 'react'
+import { useCallback, useEffect } from 'react'
import React from 'react'
@@ -23,7 +23,7 @@ import { toaster } from '../Toast'
import AppUpdateListener from './AppUpdateListener'
import ClipboardListener from './ClipboardListener'
-import EventHandler from './EventHandler'
+import ModelHandler from './ModelHandler'
import ModelImportListener from './ModelImportListener'
import QuickAskListener from './QuickAskListener'
@@ -39,7 +39,7 @@ import {
removeDownloadingModelAtom,
} from '@/helpers/atoms/Model.atom'
-const EventListenerWrapper = ({ children }: PropsWithChildren) => {
+const EventListener = () => {
const setDownloadState = useSetAtom(setDownloadStateAtom)
const setInstallingExtension = useSetAtom(setInstallingExtensionAtom)
const removeInstallingExtension = useSetAtom(removeInstallingExtensionAtom)
@@ -156,16 +156,14 @@ const EventListenerWrapper = ({ children }: PropsWithChildren) => {
])
return (
-
-
-
-
- {children}
-
-
-
-
+ <>
+
+
+
+
+
+ >
)
}
-export default EventListenerWrapper
+export default EventListener
diff --git a/web/containers/Providers/Jotai.tsx b/web/containers/Providers/Jotai.tsx
index eee24a43a..c68226fef 100644
--- a/web/containers/Providers/Jotai.tsx
+++ b/web/containers/Providers/Jotai.tsx
@@ -1,13 +1,9 @@
'use client'
-import { ReactNode } from 'react'
+import { PropsWithChildren } from 'react'
import { Provider, atom } from 'jotai'
-type Props = {
- children: ReactNode
-}
-
export const editPromptAtom = atom('')
export const currentPromptAtom = atom('')
export const fileUploadAtom = atom([])
@@ -16,7 +12,7 @@ export const searchAtom = atom('')
export const selectedTextAtom = atom('')
-export default function JotaiWrapper({ children }: Props) {
+export default function JotaiWrapper({ children }: PropsWithChildren) {
return {children}
}
diff --git a/web/containers/Providers/KeyListener.tsx b/web/containers/Providers/KeyListener.tsx
index 6d09f026d..02a1d4eb5 100644
--- a/web/containers/Providers/KeyListener.tsx
+++ b/web/containers/Providers/KeyListener.tsx
@@ -1,6 +1,6 @@
'use client'
-import { Fragment, ReactNode, useEffect } from 'react'
+import { Fragment, useEffect } from 'react'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
@@ -22,11 +22,7 @@ import {
ThreadModalAction,
} from '@/helpers/atoms/Thread.atom'
-type Props = {
- children: ReactNode
-}
-
-export default function KeyListener({ children }: Props) {
+export default function KeyListener() {
const setShowLeftPanel = useSetAtom(showLeftPanelAtom)
const setShowRightPanel = useSetAtom(showRightPanelAtom)
const [mainViewState, setMainViewState] = useAtom(mainViewStateAtom)
@@ -94,5 +90,5 @@ export default function KeyListener({ children }: Props) {
setShowRightPanel,
])
- return {children}
+ return
}
diff --git a/web/containers/Providers/EventHandler.tsx b/web/containers/Providers/ModelHandler.tsx
similarity index 98%
rename from web/containers/Providers/EventHandler.tsx
rename to web/containers/Providers/ModelHandler.tsx
index b51468099..5f0951459 100644
--- a/web/containers/Providers/EventHandler.tsx
+++ b/web/containers/Providers/ModelHandler.tsx
@@ -1,4 +1,4 @@
-import { Fragment, ReactNode, useCallback, useEffect, useRef } from 'react'
+import { Fragment, useCallback, useEffect, useRef } from 'react'
import {
ChatCompletionMessage,
@@ -43,7 +43,7 @@ import {
const maxWordForThreadTitle = 10
const defaultThreadTitle = 'New Thread'
-export default function EventHandler({ children }: { children: ReactNode }) {
+export default function ModelHandler() {
const messages = useAtomValue(getCurrentChatMessagesAtom)
const addNewMessage = useSetAtom(addNewMessageAtom)
const updateMessage = useSetAtom(updateMessageAtom)
@@ -333,5 +333,5 @@ export default function EventHandler({ children }: { children: ReactNode }) {
}
}, [onNewMessageResponse, onMessageResponseUpdate, onModelStopped])
- return {children}
+ return
}
diff --git a/web/containers/Providers/ModelImportListener.tsx b/web/containers/Providers/ModelImportListener.tsx
index a60b7be80..e99b1e6fc 100644
--- a/web/containers/Providers/ModelImportListener.tsx
+++ b/web/containers/Providers/ModelImportListener.tsx
@@ -1,4 +1,4 @@
-import { Fragment, PropsWithChildren, useCallback, useEffect } from 'react'
+import { Fragment, useCallback, useEffect } from 'react'
import {
ImportingModel,
@@ -17,7 +17,7 @@ import {
updateImportingModelProgressAtom,
} from '@/helpers/atoms/Model.atom'
-const ModelImportListener = ({ children }: PropsWithChildren) => {
+const ModelImportListener = () => {
const updateImportingModelProgress = useSetAtom(
updateImportingModelProgressAtom
)
@@ -103,7 +103,7 @@ const ModelImportListener = ({ children }: PropsWithChildren) => {
onImportModelFailed,
])
- return {children}
+ return
}
export default ModelImportListener
diff --git a/web/containers/Providers/QuickAskListener.tsx b/web/containers/Providers/QuickAskListener.tsx
index 415fc19a6..03d685953 100644
--- a/web/containers/Providers/QuickAskListener.tsx
+++ b/web/containers/Providers/QuickAskListener.tsx
@@ -1,4 +1,4 @@
-import { Fragment, ReactNode } from 'react'
+import { Fragment } from 'react'
import { useSetAtom } from 'jotai'
@@ -10,11 +10,7 @@ import useSendChatMessage from '@/hooks/useSendChatMessage'
import { mainViewStateAtom } from '@/helpers/atoms/App.atom'
-type Props = {
- children: ReactNode
-}
-
-const QuickAskListener: React.FC = ({ children }) => {
+const QuickAskListener: React.FC = () => {
const { sendChatMessage } = useSendChatMessage()
const setMainState = useSetAtom(mainViewStateAtom)
@@ -27,7 +23,7 @@ const QuickAskListener: React.FC = ({ children }) => {
debounced(input)
})
- return {children}
+ return
}
export default QuickAskListener
diff --git a/web/containers/Providers/Responsive.test.tsx b/web/containers/Providers/Responsive.test.tsx
index e72a5e7e6..5a03acea2 100644
--- a/web/containers/Providers/Responsive.test.tsx
+++ b/web/containers/Providers/Responsive.test.tsx
@@ -45,17 +45,6 @@ describe('Responsive', () => {
})
})
- it('renders children correctly', () => {
- const { getByText } = render(
-
- Child Content
-
- )
-
- // Check if the child content is rendered
- expect(getByText('Child Content')).toBeInTheDocument()
- })
-
it('hides left and right panels on small screens', () => {
// Simulate mobile view
window.matchMedia = jest.fn().mockImplementation((query) => ({
@@ -64,11 +53,7 @@ describe('Responsive', () => {
removeListener: jest.fn(),
}))
- render(
-
- Child Content
-
- )
+ render()
// Check that the left and right panel states were updated to false
expect(mockSetShowLeftPanel).toHaveBeenCalledWith(false)
@@ -83,11 +68,7 @@ describe('Responsive', () => {
removeListener: jest.fn(),
}))
- render(
-
- Child Content
-
- )
+ render()
// Change back to desktop view
window.matchMedia = jest.fn().mockImplementation((query) => ({
@@ -97,11 +78,7 @@ describe('Responsive', () => {
}))
// Call the effect manually to simulate the component re-rendering
- const rerender = render(
-
- Child Content
-
- )
+ const rerender = render()
// Check that the last known states were restored (which were true initially)
expect(mockSetShowLeftPanel).toHaveBeenCalledWith(true)
diff --git a/web/containers/Providers/Responsive.tsx b/web/containers/Providers/Responsive.tsx
index 940cb68fb..cb7bd4c1c 100644
--- a/web/containers/Providers/Responsive.tsx
+++ b/web/containers/Providers/Responsive.tsx
@@ -1,11 +1,11 @@
-import { Fragment, PropsWithChildren, useEffect, useRef } from 'react'
+import { Fragment, useEffect, useRef } from 'react'
import { useMediaQuery } from '@janhq/joi'
import { useAtom } from 'jotai'
import { showLeftPanelAtom, showRightPanelAtom } from '@/helpers/atoms/App.atom'
-const Responsive = ({ children }: PropsWithChildren) => {
+const Responsive = () => {
const matches = useMediaQuery('(max-width: 880px)')
const [showLeftPanel, setShowLeftPanel] = useAtom(showLeftPanelAtom)
const [showRightPanel, setShowRightPanel] = useAtom(showRightPanelAtom)
@@ -30,7 +30,7 @@ const Responsive = ({ children }: PropsWithChildren) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [matches, setShowLeftPanel, setShowRightPanel])
- return {children}
+ return
}
export default Responsive
diff --git a/web/containers/Providers/SettingsHandler.tsx b/web/containers/Providers/SettingsHandler.tsx
new file mode 100644
index 000000000..0ec34b0d4
--- /dev/null
+++ b/web/containers/Providers/SettingsHandler.tsx
@@ -0,0 +1,20 @@
+'use client'
+
+import { useEffect } from 'react'
+
+import { useConfigurations } from '@/hooks/useConfigurations'
+import { useLoadTheme } from '@/hooks/useLoadTheme'
+
+const SettingsHandler: React.FC = () => {
+ useLoadTheme()
+
+ const { configurePullOptions } = useConfigurations()
+
+ useEffect(() => {
+ configurePullOptions()
+ }, [configurePullOptions])
+
+ return <>>
+}
+
+export default SettingsHandler
diff --git a/web/containers/Providers/index.tsx b/web/containers/Providers/index.tsx
index 4731c600b..f9e240b94 100644
--- a/web/containers/Providers/index.tsx
+++ b/web/containers/Providers/index.tsx
@@ -5,7 +5,7 @@ import { PropsWithChildren, useCallback, useEffect, useState } from 'react'
import { Toaster } from 'react-hot-toast'
import Loader from '@/containers/Loader'
-import EventListenerWrapper from '@/containers/Providers/EventListener'
+import EventListener from '@/containers/Providers/EventListener'
import JotaiWrapper from '@/containers/Providers/Jotai'
import ThemeWrapper from '@/containers/Providers/Theme'
@@ -24,6 +24,8 @@ import DeepLinkListener from './DeepLinkListener'
import KeyListener from './KeyListener'
import Responsive from './Responsive'
+import SettingsHandler from './SettingsHandler'
+
import { extensionManager } from '@/extension'
const Providers = ({ children }: PropsWithChildren) => {
@@ -76,16 +78,14 @@ const Providers = ({ children }: PropsWithChildren) => {
{settingUp && }
{setupCore && activated && (
<>
-
-
-
-
- {children}
-
-
-
-
-
+
+
+
+
+
+
+
+ {children}
>
)}
diff --git a/web/hooks/useConfigurations.ts b/web/hooks/useConfigurations.ts
new file mode 100644
index 000000000..9d4999b2f
--- /dev/null
+++ b/web/hooks/useConfigurations.ts
@@ -0,0 +1,41 @@
+import { useCallback, useEffect } from 'react'
+
+import { ExtensionTypeEnum, ModelExtension } from '@janhq/core'
+import { useAtomValue } from 'jotai'
+
+import { extensionManager } from '@/extension'
+import {
+ ignoreSslAtom,
+ proxyAtom,
+ proxyEnabledAtom,
+} from '@/helpers/atoms/AppConfig.atom'
+
+export const useConfigurations = () => {
+ const proxyEnabled = useAtomValue(proxyEnabledAtom)
+ const proxyUrl = useAtomValue(proxyAtom)
+ const proxyIgnoreSSL = useAtomValue(ignoreSslAtom)
+
+ const configurePullOptions = useCallback(() => {
+ extensionManager
+ .get(ExtensionTypeEnum.Model)
+ ?.configurePullOptions(
+ proxyEnabled
+ ? {
+ proxy_url: proxyUrl,
+ verify_peer_ssl: !proxyIgnoreSSL,
+ }
+ : {
+ proxy_url: '',
+ verify_peer_ssl: false,
+ }
+ )
+ }, [proxyEnabled, proxyUrl, proxyIgnoreSSL])
+
+ useEffect(() => {
+ configurePullOptions()
+ }, [])
+
+ return {
+ configurePullOptions,
+ }
+}
diff --git a/web/hooks/useModels.test.ts b/web/hooks/useModels.test.ts
index e848c455c..331dfd67b 100644
--- a/web/hooks/useModels.test.ts
+++ b/web/hooks/useModels.test.ts
@@ -43,7 +43,7 @@ describe('useModels', () => {
const { result } = renderHook(() => useModels())
await act(() => {
- result.current?.loadDataModel()
+ result.current?.getData()
})
expect(mockModelExtension.getModels).toHaveBeenCalled()
@@ -70,7 +70,7 @@ describe('useModels', () => {
const { result } = renderHook(() => useModels())
await act(() => {
- result.current?.loadDataModel()
+ result.current?.getData()
})
expect(mockModelExtension.getModels()).rejects.toThrow()
diff --git a/web/hooks/useModels.ts b/web/hooks/useModels.ts
index ef15de763..0daedf7f8 100644
--- a/web/hooks/useModels.ts
+++ b/web/hooks/useModels.ts
@@ -9,18 +9,14 @@ import {
ModelManager,
} from '@janhq/core'
-import { useSetAtom, useAtom, useAtomValue } from 'jotai'
+import { useSetAtom } from 'jotai'
import { useDebouncedCallback } from 'use-debounce'
import { isLocalEngine } from '@/utils/modelEngine'
import { extensionManager } from '@/extension'
-import {
- ignoreSslAtom,
- proxyAtom,
- proxyEnabledAtom,
-} from '@/helpers/atoms/AppConfig.atom'
+
import {
configuredModelsAtom,
downloadedModelsAtom,
@@ -32,11 +28,8 @@ import {
* and updates the atoms accordingly.
*/
const useModels = () => {
- const [downloadedModels, setDownloadedModels] = useAtom(downloadedModelsAtom)
+ const setDownloadedModels = useSetAtom(downloadedModelsAtom)
const setExtensionModels = useSetAtom(configuredModelsAtom)
- const proxyEnabled = useAtomValue(proxyEnabledAtom)
- const proxyUrl = useAtomValue(proxyAtom)
- const proxyIgnoreSSL = useAtomValue(ignoreSslAtom)
const getData = useCallback(() => {
const getDownloadedModels = async () => {
@@ -87,18 +80,17 @@ const useModels = () => {
const updateStates = useCallback(() => {
const cachedModels = ModelManager.instance().models.values().toArray()
- const toUpdate = [
+ setDownloadedModels((downloadedModels) => [
...downloadedModels,
...cachedModels.filter(
(e) =>
!isLocalEngine(e.engine) &&
!downloadedModels.some((g: Model) => g.id === e.id)
),
- ]
+ ])
- setDownloadedModels(toUpdate)
setExtensionModels(cachedModels)
- }, [downloadedModels, setDownloadedModels, setExtensionModels])
+ }, [setDownloadedModels, setExtensionModels])
const getModels = async (): Promise =>
extensionManager
@@ -118,25 +110,8 @@ const useModels = () => {
}
}, [reloadData, updateStates])
- const configurePullOptions = useCallback(() => {
- extensionManager
- .get(ExtensionTypeEnum.Model)
- ?.configurePullOptions(
- proxyEnabled
- ? {
- proxy_url: proxyUrl,
- verify_peer_ssl: !proxyIgnoreSSL,
- }
- : {
- proxy_url: '',
- verify_peer_ssl: false,
- }
- )
- }, [proxyEnabled, proxyUrl, proxyIgnoreSSL])
-
return {
- loadDataModel: getData,
- configurePullOptions,
+ getData,
}
}
diff --git a/web/screens/Settings/Advanced/index.tsx b/web/screens/Settings/Advanced/index.tsx
index c6125500d..8d791694c 100644
--- a/web/screens/Settings/Advanced/index.tsx
+++ b/web/screens/Settings/Advanced/index.tsx
@@ -25,7 +25,7 @@ import { useDebouncedCallback } from 'use-debounce'
import { snackbar, toaster } from '@/containers/Toast'
import { useActiveModel } from '@/hooks/useActiveModel'
-import useModels from '@/hooks/useModels'
+import { useConfigurations } from '@/hooks/useConfigurations'
import { useSettings } from '@/hooks/useSettings'
import DataFolder from './DataFolder'
@@ -68,7 +68,7 @@ const Advanced = () => {
const [dropdownOptions, setDropdownOptions] = useState(
null
)
- const { configurePullOptions } = useModels()
+ const { configurePullOptions } = useConfigurations()
const [toggle, setToggle] = useState(null)