- {t('chat:welcome')}
+ {isTemporaryChat ? t('chat:temporaryChat') : t('chat:welcome')}
- {t('chat:description')}
+ {isTemporaryChat ? t('chat:temporaryChatDescription') : t('chat:description')}
diff --git a/web-app/src/routes/settings/__tests__/appearance.test.tsx b/web-app/src/routes/settings/__tests__/appearance.test.tsx
index 6b2727588..c7560ad70 100644
--- a/web-app/src/routes/settings/__tests__/appearance.test.tsx
+++ b/web-app/src/routes/settings/__tests__/appearance.test.tsx
@@ -235,4 +235,4 @@ describe('Appearance Settings Route', () => {
const settingsMenu = screen.getByTestId('settings-menu')
expect(settingsMenu).toBeInTheDocument()
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/routes/settings/__tests__/extensions.test.tsx b/web-app/src/routes/settings/__tests__/extensions.test.tsx
index d7dcf22d6..a111ab741 100644
--- a/web-app/src/routes/settings/__tests__/extensions.test.tsx
+++ b/web-app/src/routes/settings/__tests__/extensions.test.tsx
@@ -226,4 +226,4 @@ describe('Extensions Settings Route', () => {
const settingsContent = screen.getByTestId('settings-menu').nextElementSibling
expect(settingsContent).toHaveClass('p-4', 'w-full', 'h-[calc(100%-32px)]', 'overflow-y-auto')
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/routes/settings/__tests__/hardware.test.tsx b/web-app/src/routes/settings/__tests__/hardware.test.tsx
index 604ee639c..a57b5d7c7 100644
--- a/web-app/src/routes/settings/__tests__/hardware.test.tsx
+++ b/web-app/src/routes/settings/__tests__/hardware.test.tsx
@@ -182,4 +182,4 @@ describe('Hardware Settings', () => {
expect(screen.queryByText('GPUs')).not.toBeInTheDocument()
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/routes/settings/__tests__/privacy.test.tsx b/web-app/src/routes/settings/__tests__/privacy.test.tsx
index 57bc4b870..3c46a571c 100644
--- a/web-app/src/routes/settings/__tests__/privacy.test.tsx
+++ b/web-app/src/routes/settings/__tests__/privacy.test.tsx
@@ -184,4 +184,4 @@ describe('Privacy Settings Route', () => {
fireEvent.click(analyticsSwitch)
expect(analyticsSwitch).toBeInTheDocument()
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/routes/settings/__tests__/shortcuts.test.tsx b/web-app/src/routes/settings/__tests__/shortcuts.test.tsx
index 4e9eb7641..d5ac24ab1 100644
--- a/web-app/src/routes/settings/__tests__/shortcuts.test.tsx
+++ b/web-app/src/routes/settings/__tests__/shortcuts.test.tsx
@@ -190,4 +190,4 @@ describe('Shortcuts Settings Route', () => {
const contentArea = screen.getAllByTestId('card')
expect(contentArea.length).toBeGreaterThan(0)
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/routes/settings/providers/$providerName.tsx b/web-app/src/routes/settings/providers/$providerName.tsx
index 0b29c7bd3..2ee868a1c 100644
--- a/web-app/src/routes/settings/providers/$providerName.tsx
+++ b/web-app/src/routes/settings/providers/$providerName.tsx
@@ -3,7 +3,7 @@ import { Card, CardItem } from '@/containers/Card'
import HeaderPage from '@/containers/HeaderPage'
import SettingsMenu from '@/containers/SettingsMenu'
import { useModelProvider } from '@/hooks/useModelProvider'
-import { cn, getProviderTitle } from '@/lib/utils'
+import { cn, getProviderTitle, getModelDisplayName } from '@/lib/utils'
import {
createFileRoute,
Link,
@@ -767,7 +767,7 @@ function ProviderDetail() {
className="font-medium line-clamp-1"
title={model.id}
>
- {model.id}
+ {getModelDisplayName(model)}
diff --git a/web-app/src/routes/settings/providers/__tests__/index.test.tsx b/web-app/src/routes/settings/providers/__tests__/index.test.tsx
index e6a95e2bc..767775b64 100644
--- a/web-app/src/routes/settings/providers/__tests__/index.test.tsx
+++ b/web-app/src/routes/settings/providers/__tests__/index.test.tsx
@@ -303,4 +303,4 @@ describe('Providers Settings Route', () => {
// With empty providers array, should still render the page structure
expect(screen.getByTestId('card')).toBeInTheDocument()
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/routes/threads/$threadId.tsx b/web-app/src/routes/threads/$threadId.tsx
index 4b2c07592..adfa8b140 100644
--- a/web-app/src/routes/threads/$threadId.tsx
+++ b/web-app/src/routes/threads/$threadId.tsx
@@ -1,7 +1,9 @@
import { useEffect, useMemo, useRef } from 'react'
-import { createFileRoute, useParams } from '@tanstack/react-router'
+import { createFileRoute, useParams, redirect, useNavigate } from '@tanstack/react-router'
import cloneDeep from 'lodash.clonedeep'
import { cn } from '@/lib/utils'
+import { toast } from 'sonner'
+import { useTranslation } from '@/i18n/react-i18next-compat'
import HeaderPage from '@/containers/HeaderPage'
import { useThreads } from '@/hooks/useThreads'
@@ -22,15 +24,63 @@ import { PlatformFeature } from '@/lib/platform/types'
import ScrollToBottom from '@/containers/ScrollToBottom'
import { PromptProgress } from '@/components/PromptProgress'
import { ThreadPadding } from '@/containers/ThreadPadding'
+import { TEMPORARY_CHAT_ID, TEMPORARY_CHAT_QUERY_ID } from '@/constants/chat'
+import { useThreadScrolling } from '@/hooks/useThreadScrolling'
+import { IconInfoCircle } from '@tabler/icons-react'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
+
+const CONVERSATION_NOT_FOUND_EVENT = 'conversation-not-found'
+
+const TemporaryChatIndicator = ({ t }: { t: (key: string) => string }) => {
+ return (
+
+
{t('common:temporaryChat')}
+
+
+
+
+
+
+
+ {t('common:temporaryChatTooltip')}
+
+
+
+ )
+}
// as route.threadsDetail
export const Route = createFileRoute('/threads/$threadId')({
+ beforeLoad: ({ params }) => {
+ // Check if this is the temporary chat being accessed directly
+ if (params.threadId === TEMPORARY_CHAT_ID) {
+ // Check if we have the navigation flag in sessionStorage
+ const hasNavigationFlag = sessionStorage.getItem('temp-chat-nav')
+
+ if (!hasNavigationFlag) {
+ // Direct access - redirect to home with query parameter
+ throw redirect({
+ to: '/',
+ search: { [TEMPORARY_CHAT_QUERY_ID]: true },
+ replace: true,
+ })
+ }
+
+ // Clear the flag immediately after checking
+ sessionStorage.removeItem('temp-chat-nav')
+ }
+ },
component: ThreadDetail,
})
function ThreadDetail() {
const serviceHub = useServiceHub()
const { threadId } = useParams({ from: Route.id })
+ const navigate = useNavigate()
+ const { t } = useTranslation()
const setCurrentThreadId = useThreads((state) => state.setCurrentThreadId)
const setCurrentAssistant = useAssistant((state) => state.setCurrentAssistant)
const assistants = useAssistant((state) => state.assistants)
@@ -49,6 +99,33 @@ function ThreadDetail() {
const thread = useThreads(useShallow((state) => state.threads[threadId]))
const scrollContainerRef = useRef
(null)
+
+ // Get padding height for ChatGPT-style message positioning
+ const { paddingHeight } = useThreadScrolling(threadId, scrollContainerRef)
+
+ // Listen for conversation not found events
+ useEffect(() => {
+ const handleConversationNotFound = (event: CustomEvent) => {
+ const { threadId: notFoundThreadId } = event.detail
+ if (notFoundThreadId === threadId) {
+ // Skip error handling for temporary chat - it's expected to not exist on server
+ if (threadId === TEMPORARY_CHAT_ID) {
+ return
+ }
+
+ toast.error(t('common:conversationNotAvailable'), {
+ description: t('common:conversationNotAvailableDescription')
+ })
+ navigate({ to: '/', replace: true })
+ }
+ }
+
+ window.addEventListener(CONVERSATION_NOT_FOUND_EVENT, handleConversationNotFound as EventListener)
+ return () => {
+ window.removeEventListener(CONVERSATION_NOT_FOUND_EVENT, handleConversationNotFound as EventListener)
+ }
+ }, [threadId, navigate])
+
useEffect(() => {
setCurrentThreadId(threadId)
const assistant = assistants.find(
@@ -134,9 +211,15 @@ function ThreadDetail() {
- {PlatformFeatures[PlatformFeature.ASSISTANTS] && (
-
- )}
+
+ {PlatformFeatures[PlatformFeature.ASSISTANTS] && (
+
+ )}
+
+
+ {threadId === TEMPORARY_CHAT_ID && }
+
+
diff --git a/web-app/src/services/__tests__/analytic.test.ts b/web-app/src/services/__tests__/analytic.test.ts
index 94e25610f..56f3b9c67 100644
--- a/web-app/src/services/__tests__/analytic.test.ts
+++ b/web-app/src/services/__tests__/analytic.test.ts
@@ -265,4 +265,4 @@ describe('DefaultAnalyticService', () => {
})
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/services/__tests__/assistants.test.ts b/web-app/src/services/__tests__/assistants.test.ts
index 8c7d96e2c..0fe92f6b7 100644
--- a/web-app/src/services/__tests__/assistants.test.ts
+++ b/web-app/src/services/__tests__/assistants.test.ts
@@ -130,4 +130,4 @@ describe('DefaultAssistantsService', () => {
await expect(assistantsService.deleteAssistant(assistant)).rejects.toThrow('Failed to delete assistant')
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/services/__tests__/events.test.ts b/web-app/src/services/__tests__/events.test.ts
index ab3d597f8..9781cee8f 100644
--- a/web-app/src/services/__tests__/events.test.ts
+++ b/web-app/src/services/__tests__/events.test.ts
@@ -142,4 +142,4 @@ describe('EventEmitter', () => {
expect(handler).toHaveBeenCalledWith(complexData)
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/services/__tests__/hardware.test.ts b/web-app/src/services/__tests__/hardware.test.ts
index f9a16155b..69ccb0423 100644
--- a/web-app/src/services/__tests__/hardware.test.ts
+++ b/web-app/src/services/__tests__/hardware.test.ts
@@ -262,4 +262,4 @@ describe('TauriHardwareService', () => {
expect(vi.mocked(invoke)).toHaveBeenNthCalledWith(2, 'plugin:hardware|get_system_usage')
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/services/__tests__/mcp.test.ts b/web-app/src/services/__tests__/mcp.test.ts
index 0f5e9d073..a2af0cc76 100644
--- a/web-app/src/services/__tests__/mcp.test.ts
+++ b/web-app/src/services/__tests__/mcp.test.ts
@@ -430,4 +430,4 @@ describe('TauriMCPService', () => {
expect(result).toEqual(toolResult)
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/services/__tests__/messages.test.ts b/web-app/src/services/__tests__/messages.test.ts
index 445a9e53a..21032bb76 100644
--- a/web-app/src/services/__tests__/messages.test.ts
+++ b/web-app/src/services/__tests__/messages.test.ts
@@ -158,4 +158,4 @@ describe('DefaultMessagesService', () => {
await expect(messagesService.deleteMessage(threadId, messageId)).rejects.toThrow('Failed to delete message')
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/services/__tests__/models.test.ts b/web-app/src/services/__tests__/models.test.ts
index 4322cfe40..d0c9daa2d 100644
--- a/web-app/src/services/__tests__/models.test.ts
+++ b/web-app/src/services/__tests__/models.test.ts
@@ -1,7 +1,7 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { DefaultModelsService } from '../models/default'
import type { HuggingFaceRepo, CatalogModel } from '../models/types'
-import { EngineManager, Model } from '@janhq/core'
+import { EngineManager } from '@janhq/core'
// Mock EngineManager
vi.mock('@janhq/core', () => ({
@@ -131,18 +131,19 @@ describe('DefaultModelsService', () => {
expect(mockEngine.update).not.toHaveBeenCalled()
})
- it('should update model when modelId differs from model.id', async () => {
+ it('should handle model when modelId differs from model.id', async () => {
const modelId = 'old-model-id'
const model = {
id: 'new-model-id',
settings: [{ key: 'temperature', value: 0.7 }],
}
- mockEngine.update.mockResolvedValue(undefined)
await modelsService.updateModel(modelId, model as any)
expect(mockEngine.updateSettings).toHaveBeenCalledWith(model.settings)
- expect(mockEngine.update).toHaveBeenCalledWith(modelId, model)
+ // Note: Model ID updates are now handled at the provider level in the frontend
+ // The engine no longer has an update method for model metadata
+ expect(mockEngine.update).not.toHaveBeenCalled()
})
})
diff --git a/web-app/src/services/__tests__/serviceHub.integration.test.ts b/web-app/src/services/__tests__/serviceHub.integration.test.ts
index 5b89347a1..8a8a10344 100644
--- a/web-app/src/services/__tests__/serviceHub.integration.test.ts
+++ b/web-app/src/services/__tests__/serviceHub.integration.test.ts
@@ -213,4 +213,4 @@ describe('ServiceHub Integration Tests', () => {
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/services/__tests__/web-specific.test.ts b/web-app/src/services/__tests__/web-specific.test.ts
index 135b48c1c..52e544bf5 100644
--- a/web-app/src/services/__tests__/web-specific.test.ts
+++ b/web-app/src/services/__tests__/web-specific.test.ts
@@ -99,4 +99,4 @@ describe('Web-Specific Service Tests', () => {
expect(typeof service.factoryReset).toBe('function')
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/services/analytic/default.ts b/web-app/src/services/analytic/default.ts
index eff3a14c3..e0ae67224 100644
--- a/web-app/src/services/analytic/default.ts
+++ b/web-app/src/services/analytic/default.ts
@@ -20,4 +20,4 @@ export class DefaultAnalyticService implements AnalyticService {
await window.core?.api?.getAppConfigurations()
return appConfiguration.distinct_id
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/analytic/types.ts b/web-app/src/services/analytic/types.ts
index e54e74424..b8f2fc6d7 100644
--- a/web-app/src/services/analytic/types.ts
+++ b/web-app/src/services/analytic/types.ts
@@ -5,4 +5,4 @@
export interface AnalyticService {
updateDistinctId(id: string): Promise
getAppDistinctId(): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/app/default.ts b/web-app/src/services/app/default.ts
index 9e54c6791..68fe1f0ea 100644
--- a/web-app/src/services/app/default.ts
+++ b/web-app/src/services/app/default.ts
@@ -39,4 +39,4 @@ export class DefaultAppService implements AppService {
console.log('readYaml called with path:', path)
throw new Error('readYaml not implemented in default app service')
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/app/tauri.ts b/web-app/src/services/app/tauri.ts
index b59a9f676..af770e93d 100644
--- a/web-app/src/services/app/tauri.ts
+++ b/web-app/src/services/app/tauri.ts
@@ -75,4 +75,4 @@ export class TauriAppService extends DefaultAppService {
async readYaml(path: string): Promise {
return await invoke('read_yaml', { path })
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/app/types.ts b/web-app/src/services/app/types.ts
index 9b0c25b7e..4be49c584 100644
--- a/web-app/src/services/app/types.ts
+++ b/web-app/src/services/app/types.ts
@@ -17,4 +17,4 @@ export interface AppService {
relocateJanDataFolder(path: string): Promise
getServerStatus(): Promise
readYaml(path: string): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/app/web.ts b/web-app/src/services/app/web.ts
index 06ba65080..5702079d1 100644
--- a/web-app/src/services/app/web.ts
+++ b/web-app/src/services/app/web.ts
@@ -45,4 +45,4 @@ export class WebAppService implements AppService {
console.log('YAML reading not available in web mode')
throw new Error('readYaml not implemented in web app service')
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/assistants/default.ts b/web-app/src/services/assistants/default.ts
index 65d3cc58f..b74582510 100644
--- a/web-app/src/services/assistants/default.ts
+++ b/web-app/src/services/assistants/default.ts
@@ -31,4 +31,4 @@ export class DefaultAssistantsService implements AssistantsService {
.get(ExtensionTypeEnum.Assistant)
?.deleteAssistant(assistant)
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/assistants/types.ts b/web-app/src/services/assistants/types.ts
index 1be730fe2..be43cfb49 100644
--- a/web-app/src/services/assistants/types.ts
+++ b/web-app/src/services/assistants/types.ts
@@ -8,4 +8,4 @@ export interface AssistantsService {
getAssistants(): Promise
createAssistant(assistant: Assistant): Promise
deleteAssistant(assistant: Assistant): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/core/default.ts b/web-app/src/services/core/default.ts
index 235e38294..f019e235d 100644
--- a/web-app/src/services/core/default.ts
+++ b/web-app/src/services/core/default.ts
@@ -38,4 +38,4 @@ export class DefaultCoreService implements CoreService {
async getAppToken(): Promise {
return null
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/core/tauri.ts b/web-app/src/services/core/tauri.ts
index 8f83b0b2c..8b968510d 100644
--- a/web-app/src/services/core/tauri.ts
+++ b/web-app/src/services/core/tauri.ts
@@ -73,4 +73,4 @@ export class TauriCoreService extends DefaultCoreService {
return null
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/core/types.ts b/web-app/src/services/core/types.ts
index 8f518ffa4..6bcfb2983 100644
--- a/web-app/src/services/core/types.ts
+++ b/web-app/src/services/core/types.ts
@@ -21,4 +21,4 @@ export interface CoreService {
// App token
getAppToken(): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/core/web.ts b/web-app/src/services/core/web.ts
index 39a248611..abb2c6330 100644
--- a/web-app/src/services/core/web.ts
+++ b/web-app/src/services/core/web.ts
@@ -89,4 +89,4 @@ export class WebCoreService implements CoreService {
console.warn('App token not available in web environment')
return null
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/deeplink/default.ts b/web-app/src/services/deeplink/default.ts
index a7f8cf5da..27971ce8a 100644
--- a/web-app/src/services/deeplink/default.ts
+++ b/web-app/src/services/deeplink/default.ts
@@ -15,4 +15,4 @@ export class DefaultDeepLinkService implements DeepLinkService {
async getCurrent(): Promise {
return []
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/deeplink/tauri.ts b/web-app/src/services/deeplink/tauri.ts
index cab694353..722e5d209 100644
--- a/web-app/src/services/deeplink/tauri.ts
+++ b/web-app/src/services/deeplink/tauri.ts
@@ -24,4 +24,4 @@ export class TauriDeepLinkService extends DefaultDeepLinkService {
return []
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/deeplink/types.ts b/web-app/src/services/deeplink/types.ts
index 19b3ff517..22ecd5426 100644
--- a/web-app/src/services/deeplink/types.ts
+++ b/web-app/src/services/deeplink/types.ts
@@ -6,4 +6,4 @@
export interface DeepLinkService {
onOpenUrl(handler: (urls: string[]) => void): Promise<() => void>
getCurrent(): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/deeplink/web.ts b/web-app/src/services/deeplink/web.ts
index bba92c43c..1c8df40be 100644
--- a/web-app/src/services/deeplink/web.ts
+++ b/web-app/src/services/deeplink/web.ts
@@ -24,4 +24,4 @@ export class WebDeepLinkService implements DeepLinkService {
// Return current URL
return [window.location.href]
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/dialog/default.ts b/web-app/src/services/dialog/default.ts
index 3232fd638..f2ec6d1d5 100644
--- a/web-app/src/services/dialog/default.ts
+++ b/web-app/src/services/dialog/default.ts
@@ -14,4 +14,4 @@ export class DefaultDialogService implements DialogService {
console.log('dialog.save called with options:', options)
return null
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/dialog/tauri.ts b/web-app/src/services/dialog/tauri.ts
index 1b4efad28..d6d5fc2f2 100644
--- a/web-app/src/services/dialog/tauri.ts
+++ b/web-app/src/services/dialog/tauri.ts
@@ -33,4 +33,4 @@ export class TauriDialogService extends DefaultDialogService {
return null
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/dialog/types.ts b/web-app/src/services/dialog/types.ts
index 245155c36..8f3c4e3ab 100644
--- a/web-app/src/services/dialog/types.ts
+++ b/web-app/src/services/dialog/types.ts
@@ -16,4 +16,4 @@ export interface DialogOpenOptions {
export interface DialogService {
open(options?: DialogOpenOptions): Promise
save(options?: DialogOpenOptions): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/dialog/web.ts b/web-app/src/services/dialog/web.ts
index c735cbf41..9dbfd503d 100644
--- a/web-app/src/services/dialog/web.ts
+++ b/web-app/src/services/dialog/web.ts
@@ -54,4 +54,4 @@ export class WebDialogService implements DialogService {
console.warn('Save dialog not supported in web environment')
return null
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/events/EventEmitter.ts b/web-app/src/services/events/EventEmitter.ts
index bb9e57ebb..762ca5018 100644
--- a/web-app/src/services/events/EventEmitter.ts
+++ b/web-app/src/services/events/EventEmitter.ts
@@ -44,4 +44,4 @@ export class EventEmitter {
handler(args)
})
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/events/default.ts b/web-app/src/services/events/default.ts
index 5b5a67492..f7f9aa83d 100644
--- a/web-app/src/services/events/default.ts
+++ b/web-app/src/services/events/default.ts
@@ -16,4 +16,4 @@ export class DefaultEventsService implements EventsService {
// No-op unlisten function
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/events/tauri.ts b/web-app/src/services/events/tauri.ts
index b15e1e338..c58b03781 100644
--- a/web-app/src/services/events/tauri.ts
+++ b/web-app/src/services/events/tauri.ts
@@ -27,4 +27,4 @@ export class TauriEventsService extends DefaultEventsService {
return () => {}
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/events/types.ts b/web-app/src/services/events/types.ts
index e57641114..0b033efdc 100644
--- a/web-app/src/services/events/types.ts
+++ b/web-app/src/services/events/types.ts
@@ -13,4 +13,4 @@ export interface UnlistenFn {
export interface EventsService {
emit(event: string, payload?: T, options?: EventOptions): Promise
listen(event: string, handler: (event: { payload: T }) => void, options?: EventOptions): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/events/web.ts b/web-app/src/services/events/web.ts
index a14a6fe0d..055fd4571 100644
--- a/web-app/src/services/events/web.ts
+++ b/web-app/src/services/events/web.ts
@@ -32,4 +32,4 @@ export class WebEventsService implements EventsService {
this.eventTarget.removeEventListener(event, eventListener)
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/hardware/default.ts b/web-app/src/services/hardware/default.ts
index 250e56de9..5f4df438c 100644
--- a/web-app/src/services/hardware/default.ts
+++ b/web-app/src/services/hardware/default.ts
@@ -21,4 +21,4 @@ export class DefaultHardwareService implements HardwareService {
console.log('setActiveGpus called with data:', data)
// No-op - not implemented in default service
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/hardware/tauri.ts b/web-app/src/services/hardware/tauri.ts
index 458b3037b..efcd23c37 100644
--- a/web-app/src/services/hardware/tauri.ts
+++ b/web-app/src/services/hardware/tauri.ts
@@ -30,4 +30,4 @@ export class TauriHardwareService extends DefaultHardwareService {
// TODO: llama.cpp extension should handle this
console.log(data)
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/hardware/types.ts b/web-app/src/services/hardware/types.ts
index 026d616c6..3307298f6 100644
--- a/web-app/src/services/hardware/types.ts
+++ b/web-app/src/services/hardware/types.ts
@@ -21,4 +21,4 @@ export interface HardwareService {
}
// Re-export hardware types for convenience
-export type { HardwareData, SystemUsage }
\ No newline at end of file
+export type { HardwareData, SystemUsage }
diff --git a/web-app/src/services/mcp/default.ts b/web-app/src/services/mcp/default.ts
index 1bcce285a..b9a2f832e 100644
--- a/web-app/src/services/mcp/default.ts
+++ b/web-app/src/services/mcp/default.ts
@@ -66,4 +66,4 @@ export class DefaultMCPService implements MCPService {
console.log('deactivateMCPServer called with name:', name)
// No-op - not implemented in default service
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/mcp/tauri.ts b/web-app/src/services/mcp/tauri.ts
index 697bbc500..244e002c3 100644
--- a/web-app/src/services/mcp/tauri.ts
+++ b/web-app/src/services/mcp/tauri.ts
@@ -75,4 +75,4 @@ export class TauriMCPService extends DefaultMCPService {
async deactivateMCPServer(name: string): Promise {
return await invoke('deactivate_mcp_server', { name })
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/mcp/types.ts b/web-app/src/services/mcp/types.ts
index c551c61c5..eef8e1bf3 100644
--- a/web-app/src/services/mcp/types.ts
+++ b/web-app/src/services/mcp/types.ts
@@ -32,4 +32,4 @@ export interface MCPService {
// MCP Server lifecycle management
activateMCPServer(name: string, config: MCPServerConfig): Promise
deactivateMCPServer(name: string): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/mcp/web.ts b/web-app/src/services/mcp/web.ts
index 124de93fd..c23283951 100644
--- a/web-app/src/services/mcp/web.ts
+++ b/web-app/src/services/mcp/web.ts
@@ -276,4 +276,4 @@ export class WebMCPService implements MCPService {
private generateCancellationToken(): string {
return `mcp_cancel_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/messages/default.ts b/web-app/src/services/messages/default.ts
index 9f3ca69c6..8dbd63181 100644
--- a/web-app/src/services/messages/default.ts
+++ b/web-app/src/services/messages/default.ts
@@ -8,10 +8,16 @@ import {
ExtensionTypeEnum,
ThreadMessage,
} from '@janhq/core'
+import { TEMPORARY_CHAT_ID } from '@/constants/chat'
import type { MessagesService } from './types'
export class DefaultMessagesService implements MessagesService {
async fetchMessages(threadId: string): Promise {
+ // Don't fetch messages from server for temporary chat - it's local only
+ if (threadId === TEMPORARY_CHAT_ID) {
+ return []
+ }
+
return (
ExtensionManager.getInstance()
.get(ExtensionTypeEnum.Conversational)
@@ -21,6 +27,11 @@ export class DefaultMessagesService implements MessagesService {
}
async createMessage(message: ThreadMessage): Promise {
+ // Don't create messages on server for temporary chat - it's local only
+ if (message.thread_id === TEMPORARY_CHAT_ID) {
+ return message
+ }
+
return (
ExtensionManager.getInstance()
.get(ExtensionTypeEnum.Conversational)
@@ -30,8 +41,13 @@ export class DefaultMessagesService implements MessagesService {
}
async deleteMessage(threadId: string, messageId: string): Promise {
+ // Don't delete messages on server for temporary chat - it's local only
+ if (threadId === TEMPORARY_CHAT_ID) {
+ return
+ }
+
await ExtensionManager.getInstance()
.get(ExtensionTypeEnum.Conversational)
?.deleteMessage(threadId, messageId)
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/messages/types.ts b/web-app/src/services/messages/types.ts
index ad5ae72c8..731de06d0 100644
--- a/web-app/src/services/messages/types.ts
+++ b/web-app/src/services/messages/types.ts
@@ -8,4 +8,4 @@ export interface MessagesService {
fetchMessages(threadId: string): Promise
createMessage(message: ThreadMessage): Promise
deleteMessage(threadId: string, messageId: string): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/models/default.ts b/web-app/src/services/models/default.ts
index 39c80f551..746f869d1 100644
--- a/web-app/src/services/models/default.ts
+++ b/web-app/src/services/models/default.ts
@@ -163,15 +163,14 @@ export class DefaultModelsService implements ModelsService {
}
async updateModel(modelId: string, model: Partial): Promise {
- if (model.settings)
+ if (model.settings) {
this.getEngine()?.updateSettings(
model.settings as SettingComponentProps[]
)
- if (modelId !== model.id) {
- await this.getEngine()
- ?.update(modelId, model)
- .then(() => console.log('Model updated successfully'))
}
+ // Note: Model name/ID updates are handled at the provider level in the frontend
+ // The engine doesn't have an update method for model metadata
+ console.log('Model update request processed for modelId:', modelId)
}
async pullModel(
diff --git a/web-app/src/services/opener/default.ts b/web-app/src/services/opener/default.ts
index 287e927b8..d7f6f2722 100644
--- a/web-app/src/services/opener/default.ts
+++ b/web-app/src/services/opener/default.ts
@@ -9,4 +9,4 @@ export class DefaultOpenerService implements OpenerService {
console.log('revealItemInDir called with path:', path)
// No-op - not implemented in default service
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/opener/tauri.ts b/web-app/src/services/opener/tauri.ts
index 9c465e521..c47bf687a 100644
--- a/web-app/src/services/opener/tauri.ts
+++ b/web-app/src/services/opener/tauri.ts
@@ -14,4 +14,4 @@ export class TauriOpenerService extends DefaultOpenerService {
throw error
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/opener/types.ts b/web-app/src/services/opener/types.ts
index 21e0d17f0..1dd7bee87 100644
--- a/web-app/src/services/opener/types.ts
+++ b/web-app/src/services/opener/types.ts
@@ -5,4 +5,4 @@
export interface OpenerService {
revealItemInDir(path: string): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/path/default.ts b/web-app/src/services/path/default.ts
index 90ed46e82..cde56bf4a 100644
--- a/web-app/src/services/path/default.ts
+++ b/web-app/src/services/path/default.ts
@@ -28,4 +28,4 @@ export class DefaultPathService implements PathService {
console.log('path.extname called with path:', path)
return ''
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/path/tauri.ts b/web-app/src/services/path/tauri.ts
index 80b5808c9..618c15abd 100644
--- a/web-app/src/services/path/tauri.ts
+++ b/web-app/src/services/path/tauri.ts
@@ -55,4 +55,4 @@ export class TauriPathService extends DefaultPathService {
return lastDot > lastSlash ? path.substring(lastDot) : ''
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/path/types.ts b/web-app/src/services/path/types.ts
index 269adc1c9..5f5c9af13 100644
--- a/web-app/src/services/path/types.ts
+++ b/web-app/src/services/path/types.ts
@@ -9,4 +9,4 @@ export interface PathService {
dirname(path: string): Promise
basename(path: string): Promise
extname(path: string): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/path/web.ts b/web-app/src/services/path/web.ts
index 724acb130..e207fa01b 100644
--- a/web-app/src/services/path/web.ts
+++ b/web-app/src/services/path/web.ts
@@ -37,4 +37,4 @@ export class WebPathService implements PathService {
if (lastDot === -1 || lastDot === 0) return ''
return basename.substring(lastDot)
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/providers/default.ts b/web-app/src/services/providers/default.ts
index 241138d28..3143d203f 100644
--- a/web-app/src/services/providers/default.ts
+++ b/web-app/src/services/providers/default.ts
@@ -22,4 +22,4 @@ export class DefaultProvidersService implements ProvidersService {
fetch(): typeof fetch {
return fetch
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/providers/types.ts b/web-app/src/services/providers/types.ts
index 1c6d81d90..324058f78 100644
--- a/web-app/src/services/providers/types.ts
+++ b/web-app/src/services/providers/types.ts
@@ -7,4 +7,4 @@ export interface ProvidersService {
fetchModelsFromProvider(provider: ModelProvider): Promise
updateSettings(providerName: string, settings: ProviderSetting[]): Promise
fetch(): typeof fetch
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/theme/default.ts b/web-app/src/services/theme/default.ts
index 421cc102e..8dc131be5 100644
--- a/web-app/src/services/theme/default.ts
+++ b/web-app/src/services/theme/default.ts
@@ -18,4 +18,4 @@ export class DefaultThemeService implements ThemeService {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/theme/tauri.ts b/web-app/src/services/theme/tauri.ts
index 0f2f1f64d..e05d60c74 100644
--- a/web-app/src/services/theme/tauri.ts
+++ b/web-app/src/services/theme/tauri.ts
@@ -24,4 +24,4 @@ export class TauriThemeService extends DefaultThemeService {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/theme/types.ts b/web-app/src/services/theme/types.ts
index abcf8fc44..21c208072 100644
--- a/web-app/src/services/theme/types.ts
+++ b/web-app/src/services/theme/types.ts
@@ -7,4 +7,4 @@ export type ThemeMode = 'light' | 'dark' | null
export interface ThemeService {
setTheme(theme: ThemeMode): Promise
getCurrentWindow(): { setTheme: (theme: ThemeMode) => Promise }
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/theme/web.ts b/web-app/src/services/theme/web.ts
index 39b1ff903..cf6ce01cf 100644
--- a/web-app/src/services/theme/web.ts
+++ b/web-app/src/services/theme/web.ts
@@ -22,4 +22,4 @@ export class WebThemeService implements ThemeService {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/threads/default.ts b/web-app/src/services/threads/default.ts
index 72c66841a..4c10af26f 100644
--- a/web-app/src/services/threads/default.ts
+++ b/web-app/src/services/threads/default.ts
@@ -6,6 +6,7 @@ import { defaultAssistant } from '@/hooks/useAssistant'
import { ExtensionManager } from '@/lib/extension'
import { ConversationalExtension, ExtensionTypeEnum } from '@janhq/core'
import type { ThreadsService } from './types'
+import { TEMPORARY_CHAT_ID } from '@/constants/chat'
export class DefaultThreadsService implements ThreadsService {
async fetchThreads(): Promise {
@@ -16,7 +17,10 @@ export class DefaultThreadsService implements ThreadsService {
.then((threads) => {
if (!Array.isArray(threads)) return []
- return threads.map((e) => {
+ // Filter out temporary threads from the list
+ const filteredThreads = threads.filter((e) => e.id !== TEMPORARY_CHAT_ID)
+
+ return filteredThreads.map((e) => {
return {
...e,
updated:
@@ -47,6 +51,11 @@ export class DefaultThreadsService implements ThreadsService {
}
async createThread(thread: Thread): Promise {
+ // For temporary threads, bypass the conversational extension (in-memory only)
+ if (thread.id === TEMPORARY_CHAT_ID) {
+ return thread
+ }
+
return (
ExtensionManager.getInstance()
.get(ExtensionTypeEnum.Conversational)
@@ -82,6 +91,11 @@ export class DefaultThreadsService implements ThreadsService {
}
async updateThread(thread: Thread): Promise {
+ // For temporary threads, skip updating via conversational extension
+ if (thread.id === TEMPORARY_CHAT_ID) {
+ return
+ }
+
await ExtensionManager.getInstance()
.get(ExtensionTypeEnum.Conversational)
?.modifyThread({
@@ -118,6 +132,11 @@ export class DefaultThreadsService implements ThreadsService {
}
async deleteThread(threadId: string): Promise {
+ // For temporary threads, skip deleting via conversational extension
+ if (threadId === TEMPORARY_CHAT_ID) {
+ return
+ }
+
await ExtensionManager.getInstance()
.get(ExtensionTypeEnum.Conversational)
?.deleteThread(threadId)
diff --git a/web-app/src/services/threads/types.ts b/web-app/src/services/threads/types.ts
index d0ce195cc..9f739624f 100644
--- a/web-app/src/services/threads/types.ts
+++ b/web-app/src/services/threads/types.ts
@@ -7,4 +7,4 @@ export interface ThreadsService {
createThread(thread: Thread): Promise
updateThread(thread: Thread): Promise
deleteThread(threadId: string): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/updater/default.ts b/web-app/src/services/updater/default.ts
index b648c5622..c0ba14aa2 100644
--- a/web-app/src/services/updater/default.ts
+++ b/web-app/src/services/updater/default.ts
@@ -19,4 +19,4 @@ export class DefaultUpdaterService implements UpdaterService {
console.log('downloadAndInstallWithProgress called with callback:', typeof progressCallback)
// No-op for non-Tauri platforms
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/updater/tauri.ts b/web-app/src/services/updater/tauri.ts
index 1db1ad294..a84951e7e 100644
--- a/web-app/src/services/updater/tauri.ts
+++ b/web-app/src/services/updater/tauri.ts
@@ -60,4 +60,4 @@ export class TauriUpdaterService extends DefaultUpdaterService {
throw error
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/updater/types.ts b/web-app/src/services/updater/types.ts
index c61642666..5dceee89e 100644
--- a/web-app/src/services/updater/types.ts
+++ b/web-app/src/services/updater/types.ts
@@ -24,4 +24,4 @@ export interface UpdaterService {
downloadAndInstallWithProgress(
progressCallback: (event: UpdateProgressEvent) => void
): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/window/default.ts b/web-app/src/services/window/default.ts
index 08483743c..ea48a0173 100644
--- a/web-app/src/services/window/default.ts
+++ b/web-app/src/services/window/default.ts
@@ -40,4 +40,4 @@ export class DefaultWindowService implements WindowService {
async openLocalApiServerLogsWindow(): Promise {
// No-op
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/window/tauri.ts b/web-app/src/services/window/tauri.ts
index 56c038425..a6dd643c7 100644
--- a/web-app/src/services/window/tauri.ts
+++ b/web-app/src/services/window/tauri.ts
@@ -139,4 +139,4 @@ export class TauriWindowService extends DefaultWindowService {
throw error
}
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/window/types.ts b/web-app/src/services/window/types.ts
index 029f008aa..2f109427c 100644
--- a/web-app/src/services/window/types.ts
+++ b/web-app/src/services/window/types.ts
@@ -32,4 +32,4 @@ export interface WindowService {
openLogsWindow(): Promise
openSystemMonitorWindow(): Promise
openLocalApiServerLogsWindow(): Promise
-}
\ No newline at end of file
+}
diff --git a/web-app/src/services/window/web.ts b/web-app/src/services/window/web.ts
index 8cc01b8cb..da49077a1 100644
--- a/web-app/src/services/window/web.ts
+++ b/web-app/src/services/window/web.ts
@@ -61,4 +61,4 @@ export class WebWindowService implements WindowService {
async openLocalApiServerLogsWindow(): Promise {
console.warn('Cannot open local API server logs window in web environment')
}
-}
\ No newline at end of file
+}
diff --git a/web-app/src/test/mocks/extensions-web.ts b/web-app/src/test/mocks/extensions-web.ts
index 908f56c90..0748e905f 100644
--- a/web-app/src/test/mocks/extensions-web.ts
+++ b/web-app/src/test/mocks/extensions-web.ts
@@ -18,4 +18,4 @@ export class ConversationalExtensionWeb {
export default {}
// Export registry type for TypeScript compatibility
-export type WebExtensionRegistry = Record
\ No newline at end of file
+export type WebExtensionRegistry = Record
diff --git a/web-app/src/test/setup.ts b/web-app/src/test/setup.ts
index 9fde8b66b..b2286c2f3 100644
--- a/web-app/src/test/setup.ts
+++ b/web-app/src/test/setup.ts
@@ -207,4 +207,4 @@ Object.defineProperty(window, 'matchMedia', {
// runs a cleanup after each test case (e.g. clearing jsdom)
afterEach(() => {
cleanup()
-})
\ No newline at end of file
+})
diff --git a/web-app/src/types/modelProviders.d.ts b/web-app/src/types/modelProviders.d.ts
index eb035e471..93cdd0df2 100644
--- a/web-app/src/types/modelProviders.d.ts
+++ b/web-app/src/types/modelProviders.d.ts
@@ -28,6 +28,7 @@ type Model = {
id: string
model?: string
name?: string
+ displayName?: string
version?: number | string
description?: string
format?: string
diff --git a/web-app/src/utils/__tests__/error.test.ts b/web-app/src/utils/__tests__/error.test.ts
index e6286060c..07efd387c 100644
--- a/web-app/src/utils/__tests__/error.test.ts
+++ b/web-app/src/utils/__tests__/error.test.ts
@@ -11,4 +11,4 @@ describe('error utilities', () => {
expect(typeof OUT_OF_CONTEXT_SIZE).toBe('string')
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/utils/__tests__/formatDate.test.ts b/web-app/src/utils/__tests__/formatDate.test.ts
index 296d95da6..9d559f66f 100644
--- a/web-app/src/utils/__tests__/formatDate.test.ts
+++ b/web-app/src/utils/__tests__/formatDate.test.ts
@@ -100,4 +100,4 @@ describe('formatDate', () => {
expect(formatted).not.toMatch(/\d{1,2}:\d{2}/i)
expect(formatted).not.toMatch(/(AM|PM)/i)
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/utils/__tests__/highlight.test.ts b/web-app/src/utils/__tests__/highlight.test.ts
index 0277ba41a..0379c60a1 100644
--- a/web-app/src/utils/__tests__/highlight.test.ts
+++ b/web-app/src/utils/__tests__/highlight.test.ts
@@ -68,4 +68,4 @@ describe('highlight utility', () => {
expect(result2).toBe('Hello World')
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/utils/__tests__/number.test.ts b/web-app/src/utils/__tests__/number.test.ts
index ad5848f3c..c31ba9d09 100644
--- a/web-app/src/utils/__tests__/number.test.ts
+++ b/web-app/src/utils/__tests__/number.test.ts
@@ -66,4 +66,4 @@ describe('toNumber', () => {
expect(toNumber('\t42\n')).toBe(42)
expect(toNumber('\r\n -5.5 \t')).toBe(-5.5)
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/utils/__tests__/reasoning.test.ts b/web-app/src/utils/__tests__/reasoning.test.ts
index f9717b753..76c7853d9 100644
--- a/web-app/src/utils/__tests__/reasoning.test.ts
+++ b/web-app/src/utils/__tests__/reasoning.test.ts
@@ -378,4 +378,4 @@ describe('ReasoningProcessor', () => {
expect(result3).toBe('Second thought')
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/utils/__tests__/teamEmoji.test.ts b/web-app/src/utils/__tests__/teamEmoji.test.ts
index eda023c01..2163f9cdf 100644
--- a/web-app/src/utils/__tests__/teamEmoji.test.ts
+++ b/web-app/src/utils/__tests__/teamEmoji.test.ts
@@ -39,4 +39,4 @@ describe('teamEmoji utility', () => {
})
})
})
-})
\ No newline at end of file
+})
diff --git a/web-app/src/utils/highlight.ts b/web-app/src/utils/highlight.ts
index fe7cedcef..282ca9aa6 100644
--- a/web-app/src/utils/highlight.ts
+++ b/web-app/src/utils/highlight.ts
@@ -38,4 +38,4 @@ export function highlightFzfMatch(text: string, positions: number[], highlightCl
: part.text
)
.join('');
-}
\ No newline at end of file
+}
diff --git a/yarn.lock b/yarn.lock
index 2cb509689..ce900004b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3453,45 +3453,40 @@ __metadata:
languageName: node
linkType: hard
-"@jan/extensions-web@link:../extensions-web::locator=%40janhq%2Fweb-app%40workspace%3Aweb-app":
- version: 0.0.0-use.local
- resolution: "@jan/extensions-web@link:../extensions-web::locator=%40janhq%2Fweb-app%40workspace%3Aweb-app"
- languageName: node
- linkType: soft
-
-"@jan/extensions-web@workspace:extensions-web":
+"@jan/extensions-web@workspace:*, @jan/extensions-web@workspace:extensions-web":
version: 0.0.0-use.local
resolution: "@jan/extensions-web@workspace:extensions-web"
dependencies:
"@janhq/core": "workspace:*"
"@modelcontextprotocol/sdk": "npm:1.17.5"
+ "@tabler/icons-react": "npm:^3.34.0"
+ "@types/react": "npm:19.1.2"
+ react: "npm:19.0.0"
typescript: "npm:5.9.2"
vite: "npm:5.4.20"
vitest: "npm:2.1.9"
zustand: "npm:5.0.8"
peerDependencies:
"@janhq/core": "*"
+ "@tabler/icons-react": "*"
+ react: 19.0.0
zustand: 5.0.3
languageName: unknown
linkType: soft
-"@janhq/core@link:../core::locator=%40janhq%2Fweb-app%40workspace%3Aweb-app":
- version: 0.0.0-use.local
- resolution: "@janhq/core@link:../core::locator=%40janhq%2Fweb-app%40workspace%3Aweb-app"
- languageName: node
- linkType: soft
-
"@janhq/core@workspace:*, @janhq/core@workspace:core":
version: 0.0.0-use.local
resolution: "@janhq/core@workspace:core"
dependencies:
"@npmcli/arborist": "npm:^7.1.0"
"@types/node": "npm:^22.10.0"
+ "@types/react": "npm:19.1.2"
"@vitest/coverage-v8": "npm:^2.1.8"
"@vitest/ui": "npm:^2.1.8"
eslint: "npm:8.57.0"
happy-dom: "npm:^15.11.6"
pacote: "npm:^21.0.0"
+ react: "npm:19.0.0"
request: "npm:^2.88.2"
request-progress: "npm:^3.0.0"
rimraf: "npm:^6.0.1"
@@ -3501,6 +3496,8 @@ __metadata:
typescript: "npm:^5.8.3"
ulidx: "npm:^2.3.0"
vitest: "npm:^2.1.8"
+ peerDependencies:
+ react: 19.0.0
languageName: unknown
linkType: soft
@@ -3512,8 +3509,8 @@ __metadata:
"@dnd-kit/modifiers": "npm:9.0.0"
"@dnd-kit/sortable": "npm:10.0.0"
"@eslint/js": "npm:8.57.0"
- "@jan/extensions-web": "link:../extensions-web"
- "@janhq/core": "link:../core"
+ "@jan/extensions-web": "workspace:*"
+ "@janhq/core": "workspace:*"
"@radix-ui/react-accordion": "npm:1.2.11"
"@radix-ui/react-avatar": "npm:1.1.10"
"@radix-ui/react-dialog": "npm:1.1.15"
@@ -7022,6 +7019,17 @@ __metadata:
languageName: node
linkType: hard
+"@tabler/icons-react@npm:^3.34.0":
+ version: 3.35.0
+ resolution: "@tabler/icons-react@npm:3.35.0"
+ dependencies:
+ "@tabler/icons": "npm:3.35.0"
+ peerDependencies:
+ react: ">= 16"
+ checksum: 10c0/8d280fcdae00916b001142ba0800ea05d8fa2acdcbd82f88a299b4141fb941237be2e826b86b1af710e038b4f8bb6f76f452c3309c29fd62398b4d5789c2b3e0
+ languageName: node
+ linkType: hard
+
"@tabler/icons@npm:3.34.0":
version: 3.34.0
resolution: "@tabler/icons@npm:3.34.0"
@@ -7029,6 +7037,13 @@ __metadata:
languageName: node
linkType: hard
+"@tabler/icons@npm:3.35.0":
+ version: 3.35.0
+ resolution: "@tabler/icons@npm:3.35.0"
+ checksum: 10c0/93098828128ffed2cf412b39bd78992f93f25b22349a4e04523d2a018b7fe376ddeff105babcc3efedd707aa00b705425c7d9f598d6987552a563c62125795a2
+ languageName: node
+ linkType: hard
+
"@tailwindcss/node@npm:4.1.4":
version: 4.1.4
resolution: "@tailwindcss/node@npm:4.1.4"