fix: restore per-file file view switching
Adds preview/edit toggle for regular text files Respects default preview setting on first open Keeps per-file view choices during session
This commit is contained in:
@@ -545,10 +545,16 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
document.addEventListener('mousedown', handler);
|
document.addEventListener('mousedown', handler);
|
||||||
return () => document.removeEventListener('mousedown', handler);
|
return () => document.removeEventListener('mousedown', handler);
|
||||||
}, [isClickInsidePortalledMenu, isFloatingToolbarOpen]);
|
}, [isClickInsidePortalledMenu, isFloatingToolbarOpen]);
|
||||||
const [textViewMode, setTextViewMode] = React.useState<'view' | 'edit'>('edit');
|
type TextViewMode = 'view' | 'edit';
|
||||||
const [mdViewMode, setMdViewMode] = React.useState<'preview' | 'edit'>('edit');
|
type PreviewViewMode = 'preview' | 'edit';
|
||||||
|
|
||||||
|
const [textViewMode, setTextViewMode] = React.useState<TextViewMode>('edit');
|
||||||
|
const [mdViewMode, setMdViewMode] = React.useState<PreviewViewMode>('edit');
|
||||||
const [jsonViewMode, setJsonViewMode] = React.useState<'tree' | 'text'>('tree');
|
const [jsonViewMode, setJsonViewMode] = React.useState<'tree' | 'text'>('tree');
|
||||||
const [htmlViewMode, setHtmlViewMode] = React.useState<'preview' | 'edit'>('edit');
|
const [htmlViewMode, setHtmlViewMode] = React.useState<PreviewViewMode>('edit');
|
||||||
|
const textViewModeByPathRef = React.useRef<Record<string, TextViewMode>>({});
|
||||||
|
const mdViewModeByPathRef = React.useRef<Record<string, PreviewViewMode>>({});
|
||||||
|
const htmlViewModeByPathRef = React.useRef<Record<string, PreviewViewMode>>({});
|
||||||
|
|
||||||
const lightTheme = React.useMemo(
|
const lightTheme = React.useMemo(
|
||||||
() => availableThemes.find((theme) => theme.metadata.id === lightThemeId) ?? getDefaultTheme(false),
|
() => availableThemes.find((theme) => theme.metadata.id === lightThemeId) ?? getDefaultTheme(false),
|
||||||
@@ -1876,14 +1882,20 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
|
|
||||||
const MD_VIEWER_MODE_KEY = 'openchamber:files:md-viewer-mode';
|
const MD_VIEWER_MODE_KEY = 'openchamber:files:md-viewer-mode';
|
||||||
const HTML_VIEWER_MODE_KEY = 'openchamber:files:html-viewer-mode';
|
const HTML_VIEWER_MODE_KEY = 'openchamber:files:html-viewer-mode';
|
||||||
|
const JSON_VIEWER_MODE_KEY = 'openchamber:files:json-viewer-mode';
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const defaultMode = settingsDefaultFileViewerPreview ? 'view' : 'edit';
|
const selectedPath = selectedFile?.path;
|
||||||
setTextViewMode(defaultMode);
|
if (!selectedPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultMode: TextViewMode = settingsDefaultFileViewerPreview ? 'view' : 'edit';
|
||||||
|
setTextViewMode(textViewModeByPathRef.current[selectedPath] ?? defaultMode);
|
||||||
|
|
||||||
// Respect per-type localStorage preference when available,
|
// Respect per-type localStorage preference when available,
|
||||||
// falling back to the setting-derived default when nothing is stored.
|
// falling back to the setting-derived default when nothing is stored.
|
||||||
let mdDefault: 'preview' | 'edit' = settingsDefaultFileViewerPreview ? 'preview' : 'edit';
|
let mdDefault: PreviewViewMode = settingsDefaultFileViewerPreview ? 'preview' : 'edit';
|
||||||
try {
|
try {
|
||||||
const stored = localStorage.getItem(MD_VIEWER_MODE_KEY);
|
const stored = localStorage.getItem(MD_VIEWER_MODE_KEY);
|
||||||
if (stored === 'preview' || stored === 'edit') {
|
if (stored === 'preview' || stored === 'edit') {
|
||||||
@@ -1892,9 +1904,9 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
} catch {
|
} catch {
|
||||||
// Ignore localStorage errors
|
// Ignore localStorage errors
|
||||||
}
|
}
|
||||||
setMdViewMode(mdDefault);
|
setMdViewMode(mdViewModeByPathRef.current[selectedPath] ?? mdDefault);
|
||||||
|
|
||||||
let htmlDefault: 'preview' | 'edit' = settingsDefaultFileViewerPreview ? 'preview' : 'edit';
|
let htmlDefault: PreviewViewMode = settingsDefaultFileViewerPreview ? 'preview' : 'edit';
|
||||||
try {
|
try {
|
||||||
const stored = localStorage.getItem(HTML_VIEWER_MODE_KEY);
|
const stored = localStorage.getItem(HTML_VIEWER_MODE_KEY);
|
||||||
if (stored === 'preview' || stored === 'edit') {
|
if (stored === 'preview' || stored === 'edit') {
|
||||||
@@ -1903,37 +1915,45 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
} catch {
|
} catch {
|
||||||
// Ignore localStorage errors
|
// Ignore localStorage errors
|
||||||
}
|
}
|
||||||
setHtmlViewMode(htmlDefault);
|
setHtmlViewMode(htmlViewModeByPathRef.current[selectedPath] ?? htmlDefault);
|
||||||
|
|
||||||
|
let jsonDefault: 'tree' | 'text' = settingsDefaultFileViewerPreview ? 'tree' : 'text';
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(JSON_VIEWER_MODE_KEY);
|
||||||
|
if (stored === 'tree' || stored === 'text') {
|
||||||
|
jsonDefault = stored;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore localStorage errors
|
||||||
|
}
|
||||||
|
setJsonViewMode(jsonDefault);
|
||||||
}, [selectedFile?.path, settingsDefaultFileViewerPreview]);
|
}, [selectedFile?.path, settingsDefaultFileViewerPreview]);
|
||||||
|
|
||||||
const saveMdViewMode = React.useCallback((mode: 'preview' | 'edit') => {
|
const saveTextViewMode = React.useCallback((mode: TextViewMode) => {
|
||||||
|
const selectedPath = selectedFile?.path;
|
||||||
|
if (selectedPath) {
|
||||||
|
textViewModeByPathRef.current[selectedPath] = mode;
|
||||||
|
}
|
||||||
|
setTextViewMode(mode);
|
||||||
|
}, [selectedFile?.path]);
|
||||||
|
|
||||||
|
const saveMdViewMode = React.useCallback((mode: PreviewViewMode) => {
|
||||||
|
const selectedPath = selectedFile?.path;
|
||||||
|
if (selectedPath) {
|
||||||
|
mdViewModeByPathRef.current[selectedPath] = mode;
|
||||||
|
}
|
||||||
setMdViewMode(mode);
|
setMdViewMode(mode);
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(MD_VIEWER_MODE_KEY, mode);
|
localStorage.setItem(MD_VIEWER_MODE_KEY, mode);
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore localStorage errors
|
// Ignore localStorage errors
|
||||||
}
|
}
|
||||||
}, []);
|
}, [selectedFile?.path]);
|
||||||
|
|
||||||
const getMdViewMode = React.useCallback((): 'preview' | 'edit' => {
|
const getMdViewMode = React.useCallback((): PreviewViewMode => {
|
||||||
return mdViewMode;
|
return mdViewMode;
|
||||||
}, [mdViewMode]);
|
}, [mdViewMode]);
|
||||||
|
|
||||||
const JSON_VIEWER_MODE_KEY = 'openchamber:files:json-viewer-mode';
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
try {
|
|
||||||
const stored = localStorage.getItem(JSON_VIEWER_MODE_KEY);
|
|
||||||
if (stored === 'tree') {
|
|
||||||
setJsonViewMode('tree');
|
|
||||||
} else if (stored === 'text') {
|
|
||||||
setJsonViewMode('text');
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Ignore localStorage errors
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const saveJsonViewMode = React.useCallback((mode: 'tree' | 'text') => {
|
const saveJsonViewMode = React.useCallback((mode: 'tree' | 'text') => {
|
||||||
setJsonViewMode(mode);
|
setJsonViewMode(mode);
|
||||||
try {
|
try {
|
||||||
@@ -1943,16 +1963,20 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const saveHtmlViewMode = React.useCallback((mode: 'preview' | 'edit') => {
|
const saveHtmlViewMode = React.useCallback((mode: PreviewViewMode) => {
|
||||||
|
const selectedPath = selectedFile?.path;
|
||||||
|
if (selectedPath) {
|
||||||
|
htmlViewModeByPathRef.current[selectedPath] = mode;
|
||||||
|
}
|
||||||
setHtmlViewMode(mode);
|
setHtmlViewMode(mode);
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(HTML_VIEWER_MODE_KEY, mode);
|
localStorage.setItem(HTML_VIEWER_MODE_KEY, mode);
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore localStorage errors
|
// Ignore localStorage errors
|
||||||
}
|
}
|
||||||
}, []);
|
}, [selectedFile?.path]);
|
||||||
|
|
||||||
const getHtmlViewMode = React.useCallback((): 'preview' | 'edit' => {
|
const getHtmlViewMode = React.useCallback((): PreviewViewMode => {
|
||||||
return htmlViewMode;
|
return htmlViewMode;
|
||||||
}, [htmlViewMode]);
|
}, [htmlViewMode]);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -2119,12 +2143,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canEdit && textViewMode !== 'edit') {
|
if (canEdit && textViewMode === 'edit') {
|
||||||
setTextViewMode('edit');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canEdit) {
|
|
||||||
const view = editorViewRef.current;
|
const view = editorViewRef.current;
|
||||||
if (!view) {
|
if (!view) {
|
||||||
return;
|
return;
|
||||||
@@ -2546,6 +2565,15 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{canUseShikiFileView && canEdit && !isJson && !isHtml && (
|
||||||
|
<PreviewToggleButton
|
||||||
|
currentMode={textViewMode === 'view' ? 'preview' : 'edit'}
|
||||||
|
onToggle={() => {
|
||||||
|
saveTextViewMode(textViewMode === 'view' ? 'edit' : 'view');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{(isMarkdown || isHtmlFile(selectedFile?.path ?? '')) && (
|
{(isMarkdown || isHtmlFile(selectedFile?.path ?? '')) && (
|
||||||
<PreviewToggleButton
|
<PreviewToggleButton
|
||||||
currentMode={isMarkdown ? getMdViewMode() : getHtmlViewMode()}
|
currentMode={isMarkdown ? getMdViewMode() : getHtmlViewMode()}
|
||||||
|
|||||||
Reference in New Issue
Block a user