;
+ selectedCount: number;
+ totalCount: number;
+ onToggleSelection: (fileId: string) => void;
+ onReorder: (fromIndex: number, toIndex: number) => void;
+ }
) => {
const currentFile = activeFiles[currentFileIndex];
const isInViewer = currentView === 'viewer';
@@ -57,20 +67,39 @@ const createViewOptions = (
value: "viewer",
};
+ // Page Editor dropdown logic
+ const isInPageEditor = currentView === 'pageEditor';
+ const hasPageEditorFiles = pageEditorState && pageEditorState.totalCount > 0;
+ const showPageEditorDropdown = isInPageEditor && hasPageEditorFiles;
+
+ let pageEditorDisplayName = 'Page Editor';
+ if (isInPageEditor && pageEditorState) {
+ if (pageEditorState.selectedCount === pageEditorState.totalCount) {
+ pageEditorDisplayName = `${pageEditorState.selectedCount} file${pageEditorState.selectedCount !== 1 ? 's' : ''}`;
+ } else {
+ pageEditorDisplayName = `${pageEditorState.selectedCount}/${pageEditorState.totalCount} selected`;
+ }
+ }
+
const pageEditorOption = {
- label: (
+ label: showPageEditorDropdown ? (
+
+ ) : (
- {currentView === "pageEditor" ? (
- <>
- {switchingTo === "pageEditor" ? : }
- Page Editor
- >
+ {switchingTo === "pageEditor" ? (
+
) : (
- <>
- {switchingTo === "pageEditor" ? : }
- Page Editor
- >
+
)}
+ {pageEditorDisplayName}
),
value: "pageEditor",
@@ -121,6 +150,17 @@ const TopControls = ({
const { isRainbowMode } = useRainbowThemeContext();
const [switchingTo, setSwitchingTo] = useState(null);
+ // Get page editor state for dropdown
+ const {
+ selectedFileIds,
+ toggleFileSelection,
+ reorderFiles: pageEditorReorderFiles,
+ } = usePageEditor();
+
+ // Convert Set to array for counting
+ const selectedCount = selectedFileIds.size;
+ const totalCount = activeFiles.length;
+
const handleViewChange = useCallback((view: string) => {
if (!isValidWorkbench(view)) {
return;
@@ -147,7 +187,21 @@ const TopControls = ({
pageEditorReorderFiles(fromIndex, toIndex, activeFiles.map(f => f.fileId)),
+ }
+ )}
value={currentView}
onChange={handleViewChange}
color="blue"
diff --git a/frontend/src/contexts/PageEditorContext.tsx b/frontend/src/contexts/PageEditorContext.tsx
new file mode 100644
index 000000000..9486b6b98
--- /dev/null
+++ b/frontend/src/contexts/PageEditorContext.tsx
@@ -0,0 +1,111 @@
+import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
+import { FileId } from '../types/file';
+import { useFileActions } from './FileContext';
+
+interface PageEditorContextValue {
+ // Set of selected file IDs (for quick lookup)
+ selectedFileIds: Set;
+
+ // Toggle file selection
+ toggleFileSelection: (fileId: FileId) => void;
+
+ // Select/deselect all files
+ selectAll: (fileIds: FileId[]) => void;
+ deselectAll: () => void;
+
+ // Reorder ALL files in FileContext (maintains selection state)
+ reorderFiles: (fromIndex: number, toIndex: number, allFileIds: FileId[]) => void;
+
+ // Sync with FileContext when files change
+ syncWithFileContext: (allFileIds: FileId[]) => void;
+}
+
+const PageEditorContext = createContext(undefined);
+
+interface PageEditorProviderProps {
+ children: ReactNode;
+ initialFileIds?: FileId[];
+}
+
+export function PageEditorProvider({ children, initialFileIds = [] }: PageEditorProviderProps) {
+ // Use Set for O(1) selection lookup
+ const [selectedFileIds, setSelectedFileIds] = useState>(new Set(initialFileIds));
+ const { actions: fileActions } = useFileActions();
+
+ const toggleFileSelection = useCallback((fileId: FileId) => {
+ setSelectedFileIds(prev => {
+ const next = new Set(prev);
+ if (next.has(fileId)) {
+ next.delete(fileId);
+ } else {
+ next.add(fileId);
+ }
+ return next;
+ });
+ }, []);
+
+ const selectAll = useCallback((fileIds: FileId[]) => {
+ setSelectedFileIds(new Set(fileIds));
+ }, []);
+
+ const deselectAll = useCallback(() => {
+ setSelectedFileIds(new Set());
+ }, []);
+
+ const reorderFiles = useCallback((fromIndex: number, toIndex: number, allFileIds: FileId[]) => {
+ // Reorder the entire file list in FileContext
+ const newOrder = [...allFileIds];
+ const [movedFile] = newOrder.splice(fromIndex, 1);
+ newOrder.splice(toIndex, 0, movedFile);
+
+ // Update global FileContext order
+ fileActions.reorderFiles(newOrder);
+ }, [fileActions]);
+
+ const syncWithFileContext = useCallback((allFileIds: FileId[]) => {
+ setSelectedFileIds(prev => {
+ // Remove IDs that no longer exist in FileContext
+ const next = new Set();
+ allFileIds.forEach(id => {
+ if (prev.has(id)) {
+ next.add(id);
+ }
+ });
+
+ // If no files selected, select all by default
+ if (next.size === 0 && allFileIds.length > 0) {
+ return new Set(allFileIds);
+ }
+
+ // Only update if there's an actual change
+ if (next.size === prev.size && Array.from(next).every(id => prev.has(id))) {
+ return prev; // No change, return same reference
+ }
+
+ return next;
+ });
+ }, []);
+
+ const value: PageEditorContextValue = {
+ selectedFileIds,
+ toggleFileSelection,
+ selectAll,
+ deselectAll,
+ reorderFiles,
+ syncWithFileContext,
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function usePageEditor() {
+ const context = useContext(PageEditorContext);
+ if (!context) {
+ throw new Error('usePageEditor must be used within PageEditorProvider');
+ }
+ return context;
+}