Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"dotenv": "^17.0.0",
"electron": "39.0.0",
"eslint": "^9.25.0",
"eslint-plugin-react-hooks": "^6.0.0",
"eslint-plugin-react-hooks": "^7.0.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"husky": "^9.1.7",
Expand Down
31 changes: 22 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 4 additions & 9 deletions renderer/src/common/components/settings/tabs/general-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from '@/common/hooks/use-auto-launch'
import { useTheme } from '@/common/hooks/use-theme'
import { useMutation, useQuery } from '@tanstack/react-query'
import { useState, useEffect } from 'react'
import { useState } from 'react'
import { Sun, Moon, Monitor } from 'lucide-react'
import log from 'electron-log/renderer'
import { trackEvent } from '@/common/lib/analytics'
Expand Down Expand Up @@ -138,14 +138,9 @@ function AutoLaunchField() {
}

function QuitConfirmationField() {
const [skipQuitConfirmation, setSkipQuitConfirmation] =
useState<boolean>(false)

useEffect(() => {
const quitConfirmationDisabled =
localStorage.getItem(CONFIRM_QUIT_STORAGE_KEY) === 'true'
setSkipQuitConfirmation(quitConfirmationDisabled)
}, [])
const [skipQuitConfirmation, setSkipQuitConfirmation] = useState<boolean>(
() => localStorage.getItem(CONFIRM_QUIT_STORAGE_KEY) === 'true'
)

const handleQuitConfirmationToggle = () => {
const newValue = !skipQuitConfirmation
Expand Down
5 changes: 4 additions & 1 deletion renderer/src/common/components/ui/tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ const TooltipTrigger = React.forwardRef<
const setRefs = React.useCallback(
(node: HTMLButtonElement | null) => {
// internal: keep track for truncation measurement
if (ctx) ctx.triggerRef.current = node
if (ctx) {
const triggerRef = ctx.triggerRef
triggerRef.current = node
}
// forward to consumer
if (typeof forwardedRef === 'function') {
forwardedRef(node)
Expand Down
2 changes: 1 addition & 1 deletion renderer/src/common/hooks/use-check-server-status.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { META_MCP_SERVER_NAME } from '../lib/constants'
* Returns a function that polls server status and invalidates queries when ready.
*/
export function useCheckServerStatus() {
const toastIdRef = useRef(new Date(Date.now()).toISOString())
const toastIdRef = useRef(new Date().getTime())

const queryClient = useQueryClient()

Expand Down
17 changes: 10 additions & 7 deletions renderer/src/common/hooks/use-cleanup-meta-optimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,16 @@ export function useCleanupMetaOptimizer() {
enabled: isMetaOptimizerEnabled,
})

const removeClientsFromGroup = async (clients: string[]) => {
for (const clientType of clients) {
await unregisterClients({
clientType,
})
}
}
const removeClientsFromGroup = useCallback(
async (clients: string[]) => {
for (const clientType of clients) {
await unregisterClients({
clientType,
})
}
},
[unregisterClients]
)

const mcpOptimizerGroup = groupsList?.groups?.find(
(g) => g.name === MCP_OPTIMIZER_GROUP_NAME
Expand Down
2 changes: 1 addition & 1 deletion renderer/src/common/hooks/use-experimental-features.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function formatFeatureFlagDescription(key: string): React.ReactNode {
}

export function useExperimentalFeatures() {
const toastIdRef = useRef(new Date(Date.now()).toISOString())
const toastIdRef = useRef(new Date().getTime())
const { handleCreateOptimizerGroup, isCreatingOptimizerGroup } =
useCreateOptimizerGroup()
const { cleanupMetaOptimizer } = useCleanupMetaOptimizer()
Expand Down
16 changes: 5 additions & 11 deletions renderer/src/features/chat/components/error-alert.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'
import { useState } from 'react'
import { X, AlertTriangle } from 'lucide-react'
import {
Alert,
Expand All @@ -14,16 +14,10 @@ interface ErrorAlertProps {
}

export function ErrorAlert({ error, className = '' }: ErrorAlertProps) {
const [isDismissed, setIsDismissed] = useState(false)
const [dismissedError, setDismissedError] = useState<string | null>(null)

// Reset dismissed state when a new error occurs
useEffect(() => {
if (error) {
setIsDismissed(false)
}
}, [error])

if (!error || isDismissed) {
// Show alert only if there's an error and it hasn't been dismissed
if (!error || error === dismissedError) {
return null
}

Expand All @@ -50,7 +44,7 @@ export function ErrorAlert({ error, className = '' }: ErrorAlertProps) {
variant="ghost"
size="sm"
className="hover:bg-destructive/20 absolute top-2 right-2 h-6 w-6 p-0"
onClick={() => setIsDismissed(true)}
onClick={() => setDismissedError(error)}
>
<X className="h-3 w-3" />
<span className="sr-only">Dismiss error</span>
Expand Down
23 changes: 12 additions & 11 deletions renderer/src/features/chat/components/mcp-tools-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'
import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import {
Dialog,
Expand Down Expand Up @@ -48,6 +48,9 @@ export function McpToolsModal({
}: McpToolsModalProps) {
const [searchQuery, setSearchQuery] = useState('')
const [localEnabledTools, setLocalEnabledTools] = useState<string[]>([])
const [lastInitializedServer, setLastInitializedServer] = useState<
string | null
>(null)
const queryClient = useQueryClient()

// Fetch tools for the specific server
Expand All @@ -66,16 +69,14 @@ export function McpToolsModal({
refetchOnMount: true, // Always refetch when component mounts
})

// Initialize local state when data loads
useEffect(() => {
if (serverTools?.tools) {
const enabledTools = serverTools.tools
.filter((tool) => tool.enabled)
.map((tool) => tool.name)

setLocalEnabledTools(enabledTools)
}
}, [serverTools, serverName])
// Initialize local state when server or tools change
if (serverTools?.tools && lastInitializedServer !== serverName) {
const enabledTools = serverTools.tools
.filter((tool) => tool.enabled)
.map((tool) => tool.name)
setLocalEnabledTools(enabledTools)
setLastInitializedServer(serverName)
}

// Save tools mutation
const saveToolsMutation = useMutation({
Expand Down
12 changes: 3 additions & 9 deletions renderer/src/features/chat/hooks/use-chat-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,7 @@ export function useChatSettings() {
enabledTools: allEnabledTools,
}
}
}, [
selectedModel?.provider,
selectedModel?.model,
providerSettings,
enabledMcpTools,
enabledMcpServers,
])
}, [selectedModel, providerSettings, enabledMcpTools, enabledMcpServers])

// Mutation to update selected model
const updateSelectedModelMutation = useMutation({
Expand Down Expand Up @@ -285,7 +279,7 @@ export function useChatSettings() {
throw error
}
},
[selectedModel?.provider, selectedModel?.model, updateSelectedModelMutation]
[selectedModel, updateSelectedModelMutation]
)

// Update only enabled tools
Expand Down Expand Up @@ -316,7 +310,7 @@ export function useChatSettings() {
throw error
}
},
[selectedModel?.provider, updateProviderSettingsMutation]
[selectedModel, updateProviderSettingsMutation]
)

// Load persisted settings for a provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useSearch } from '@tanstack/react-router'
import { useEffect, useRef, useState } from 'react'
import { twMerge } from 'tailwind-merge'
import { trackEvent } from '@/common/lib/analytics'
import { delay } from '@utils/delay'
import {
Tooltip,
TooltipTrigger,
Expand Down Expand Up @@ -110,42 +111,62 @@ export function CardMcpServer({
strict: false,
})
const [isNewServer, setIsNewServer] = useState(false)
const searchNewServerName =
'newServerName' in search ? search.newServerName : null

useEffect(() => {
// Check if the server is new by looking for a specific search parameter
// This could be a query parameter or any other condition that indicates a new server
if ('newServerName' in search && search.newServerName === name) {
setIsNewServer(true)
// clear state after 2 seconds
setTimeout(() => {
setIsNewServer(false)
}, 2000)
} else {
setIsNewServer(false)
}
if (searchNewServerName === name) {
let cancelled = false

const showNewServerAnimation = async () => {
setIsNewServer(true)
await delay(2000)
if (!cancelled) {
setIsNewServer(false)
}
}

showNewServerAnimation()

return () => {
setIsNewServer(false)
return () => {
cancelled = true
}
}
}, [name, search])
}, [name, searchNewServerName])

// Check if the server is in deleting state
const isDeleting = status === 'deleting'
const isTransitioning =
status === 'starting' || status === 'stopping' || status === 'restarting'
const isStopped = status === 'stopped' || status === 'stopping'
const [hadRecentStatusChange, setHadRecentStatusChange] = useState(false)
const [prevStatus, setPrevStatus] = useState<CoreWorkload['status']>(status)
const prevStatusRef = useRef<CoreWorkload['status']>(status)

useEffect(() => {
// show a brief animation for status transitions that are immediate
if (prevStatus !== status && ['running'].includes(status ?? '')) {
setHadRecentStatusChange(true)
const timeout = setTimeout(() => setHadRecentStatusChange(false), 2500)
return () => clearTimeout(timeout)
// Show a brief animation for status transitions
if (
prevStatusRef.current !== status &&
['running'].includes(status ?? '')
) {
let cancelled = false

const showStatusChangeAnimation = async () => {
setHadRecentStatusChange(true)
await delay(2500)
if (!cancelled) {
setHadRecentStatusChange(false)
}
}

showStatusChangeAnimation()

return () => {
cancelled = true
}
}
setPrevStatus(status)
}, [status, prevStatus])
prevStatusRef.current = status
}, [status])

return (
<Card
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ export function CustomizeToolsTable({
toolsOverride,
overrideTools,
expandableText,
handleToolToggle,
handleEditTool,
]
)

Expand Down
Loading
Loading