From 5f76051f086d4bf4b81f06c39f06e199069106d5 Mon Sep 17 00:00:00 2001 From: simosmik Date: Thu, 30 Apr 2026 08:03:01 +0000 Subject: [PATCH] refactor: migrate openSettings and refreshProjects from window.* to PaletteOpsContext --- src/components/app/AppContent.tsx | 37 +++++++------------ .../chat/hooks/useChatRealtimeHandlers.ts | 7 ++-- .../code-editor/view/CodeEditor.tsx | 4 +- .../sidebar/hooks/useSidebarController.ts | 10 ++--- src/components/sidebar/view/Sidebar.tsx | 9 ++--- src/types/global.d.ts | 2 - 6 files changed, 27 insertions(+), 42 deletions(-) diff --git a/src/components/app/AppContent.tsx b/src/components/app/AppContent.tsx index 42005c18..432447e4 100644 --- a/src/components/app/AppContent.tsx +++ b/src/components/app/AppContent.tsx @@ -6,12 +6,20 @@ import Sidebar from '../sidebar/view/Sidebar'; import MainContent from '../main-content/view/MainContent'; import CommandPalette from '../command-palette/CommandPalette'; import { useWebSocket } from '../../contexts/WebSocketContext'; -import { PaletteOpsProvider } from '../../contexts/PaletteOpsContext'; +import { PaletteOpsProvider, usePaletteOpsRegister } from '../../contexts/PaletteOpsContext'; import { useDeviceSettings } from '../../hooks/useDeviceSettings'; import { useSessionProtection } from '../../hooks/useSessionProtection'; import { useProjectsState } from '../../hooks/useProjectsState'; export default function AppContent() { + return ( + + + + ); +} + +function AppContentInner() { const navigate = useNavigate(); const { sessionId } = useParams<{ sessionId?: string }>(); const { t } = useTranslation('common'); @@ -52,27 +60,10 @@ export default function AppContent() { activeSessions, }); - useEffect(() => { - // Expose a non-blocking refresh for chat/session flows. - // Full loading refreshes are still available through direct fetchProjects calls. - window.refreshProjects = refreshProjectsSilently; - - return () => { - if (window.refreshProjects === refreshProjectsSilently) { - delete window.refreshProjects; - } - }; - }, [refreshProjectsSilently]); - - useEffect(() => { - window.openSettings = openSettings; - - return () => { - if (window.openSettings === openSettings) { - delete window.openSettings; - } - }; - }, [openSettings]); + usePaletteOpsRegister({ + openSettings, + refreshProjects: refreshProjectsSilently, + }); useEffect(() => { if (typeof navigator === 'undefined' || !('serviceWorker' in navigator)) { @@ -147,7 +138,6 @@ export default function AppContent() { }, []); return ( -
{!isMobile ? (
@@ -214,6 +204,5 @@ export default function AppContent() { onShowTab={setActiveTab} />
- ); } diff --git a/src/components/chat/hooks/useChatRealtimeHandlers.ts b/src/components/chat/hooks/useChatRealtimeHandlers.ts index 73d1a5e7..855ee788 100644 --- a/src/components/chat/hooks/useChatRealtimeHandlers.ts +++ b/src/components/chat/hooks/useChatRealtimeHandlers.ts @@ -1,5 +1,6 @@ import { useEffect, useRef } from 'react'; import type { Dispatch, MutableRefObject, SetStateAction } from 'react'; +import { usePaletteOps } from '../../../contexts/PaletteOpsContext'; import type { PendingPermissionRequest } from '../types/types'; import type { Project, ProjectSession, LLMProvider } from '../../../types/app'; import type { SessionStore, NormalizedMessage } from '../../../stores/useSessionStore'; @@ -99,6 +100,7 @@ export function useChatRealtimeHandlers({ onWebSocketReconnect, sessionStore, }: UseChatRealtimeHandlersArgs) { + const paletteOps = usePaletteOps(); const lastProcessedMessageRef = useRef(null); useEffect(() => { @@ -280,9 +282,7 @@ export function useChatRealtimeHandlers({ onNavigateToSession?.(actualId); } sessionStorage.removeItem('pendingSessionId'); - if (window.refreshProjects) { - setTimeout(() => window.refreshProjects?.(), 500); - } + setTimeout(() => { void paletteOps.refreshProjects(); }, 500); } break; } @@ -365,5 +365,6 @@ export function useChatRealtimeHandlers({ onNavigateToSession, onWebSocketReconnect, sessionStore, + paletteOps, ]); } diff --git a/src/components/code-editor/view/CodeEditor.tsx b/src/components/code-editor/view/CodeEditor.tsx index e68a73d3..5861ce71 100644 --- a/src/components/code-editor/view/CodeEditor.tsx +++ b/src/components/code-editor/view/CodeEditor.tsx @@ -3,6 +3,7 @@ import { unifiedMergeView } from '@codemirror/merge'; import type { Extension } from '@codemirror/state'; import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { usePaletteOps } from '../../../contexts/PaletteOpsContext'; import { useCodeEditorDocument } from '../hooks/useCodeEditorDocument'; import { useCodeEditorSettings } from '../hooks/useCodeEditorSettings'; import { useEditorKeyboardShortcuts } from '../hooks/useEditorKeyboardShortcuts'; @@ -36,6 +37,7 @@ export default function CodeEditor({ onPopOut = null, }: CodeEditorProps) { const { t } = useTranslation('codeEditor'); + const paletteOps = usePaletteOps(); const [isFullscreen, setIsFullscreen] = useState(false); const [showDiff, setShowDiff] = useState(Boolean(file.diffInfo)); const [markdownPreview, setMarkdownPreview] = useState(false); @@ -199,7 +201,7 @@ export default function CodeEditor({ saving={saving} saveSuccess={saveSuccess} onToggleMarkdownPreview={() => setMarkdownPreview((previous) => !previous)} - onOpenSettings={() => window.openSettings?.('appearance')} + onOpenSettings={() => paletteOps.openSettings('appearance')} onDownload={handleDownload} onSave={handleSave} onToggleFullscreen={() => setIsFullscreen((previous) => !previous)} diff --git a/src/components/sidebar/hooks/useSidebarController.ts b/src/components/sidebar/hooks/useSidebarController.ts index 514cf91b..d950bc43 100644 --- a/src/components/sidebar/hooks/useSidebarController.ts +++ b/src/components/sidebar/hooks/useSidebarController.ts @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import type { TFunction } from 'i18next'; import { api } from '../../../utils/api'; +import { usePaletteOps } from '../../../contexts/PaletteOpsContext'; import type { Project, ProjectSession, LLMProvider } from '../../../types/app'; import type { DeleteProjectConfirmation, @@ -95,6 +96,7 @@ export function useSidebarController({ setSidebarVisible, sidebarVisible, }: UseSidebarControllerArgs) { + const paletteOps = usePaletteOps(); const [expandedProjects, setExpandedProjects] = useState>(new Set()); const [editingProject, setEditingProject] = useState(null); const [showNewProject, setShowNewProject] = useState(false); @@ -536,11 +538,7 @@ export function useSidebarController({ try { const response = await api.renameProject(projectId, editingName); if (response.ok) { - if (window.refreshProjects) { - await window.refreshProjects(); - } else { - window.location.reload(); - } + await paletteOps.refreshProjects(); } else { console.error('Failed to rename project'); } @@ -551,7 +549,7 @@ export function useSidebarController({ setEditingName(''); } }, - [editingName], + [editingName, paletteOps], ); const showDeleteSessionConfirmation = useCallback( diff --git a/src/components/sidebar/view/Sidebar.tsx b/src/components/sidebar/view/Sidebar.tsx index 11a7dbe7..97484b01 100644 --- a/src/components/sidebar/view/Sidebar.tsx +++ b/src/components/sidebar/view/Sidebar.tsx @@ -6,6 +6,7 @@ import { useVersionCheck } from '../../../hooks/useVersionCheck'; import { useUiPreferences } from '../../../hooks/useUiPreferences'; import { useSidebarController } from '../hooks/useSidebarController'; import { useTaskMaster } from '../../../contexts/TaskMasterContext'; +import { usePaletteOps } from '../../../contexts/PaletteOpsContext'; import { useTasksSettings } from '../../../contexts/TasksSettingsContext'; import type { Project, LLMProvider } from '../../../types/app'; import type { MCPServerStatus, SidebarProps } from '../types/types'; @@ -49,6 +50,7 @@ function Sidebar({ const { sidebarVisible } = preferences; const { setCurrentProject, mcpServerStatus } = useTaskMaster() as TaskMasterSidebarContext; const { tasksEnabled } = useTasksSettings(); + const paletteOps = usePaletteOps(); const { isSidebarCollapsed, @@ -128,12 +130,7 @@ function Sidebar({ }, [isPWA]); const handleProjectCreated = () => { - if (window.refreshProjects) { - void window.refreshProjects(); - return; - } - - window.location.reload(); + void paletteOps.refreshProjects(); }; const projectListProps: SidebarProjectListProps = { diff --git a/src/types/global.d.ts b/src/types/global.d.ts index ba368e4b..7ed066a4 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -3,8 +3,6 @@ export {}; declare global { interface Window { __ROUTER_BASENAME__?: string; - refreshProjects?: () => void | Promise; - openSettings?: (tab?: string) => void; } interface EventSourceEventMap {