refactor: migrate openSettings and refreshProjects from window.* to PaletteOpsContext

This commit is contained in:
simosmik
2026-04-30 08:03:01 +00:00
parent dc281774b5
commit 5f76051f08
6 changed files with 27 additions and 42 deletions

View File

@@ -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 (
<PaletteOpsProvider>
<AppContentInner />
</PaletteOpsProvider>
);
}
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 (
<PaletteOpsProvider>
<div className="fixed inset-0 flex bg-background" style={{ bottom: 'var(--keyboard-height, 0px)' }}>
{!isMobile ? (
<div className="h-full flex-shrink-0 border-r border-border/50">
@@ -214,6 +204,5 @@ export default function AppContent() {
onShowTab={setActiveTab}
/>
</div>
</PaletteOpsProvider>
);
}

View File

@@ -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<LatestChatMessage | null>(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,
]);
}

View File

@@ -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)}

View File

@@ -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<Set<string>>(new Set());
const [editingProject, setEditingProject] = useState<string | null>(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(

View File

@@ -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 = {

View File

@@ -3,8 +3,6 @@ export {};
declare global {
interface Window {
__ROUTER_BASENAME__?: string;
refreshProjects?: () => void | Promise<void>;
openSettings?: (tab?: string) => void;
}
interface EventSourceEventMap {