fix: stop stale file focus requests from stealing context panel selection
This commit is contained in:
@@ -811,10 +811,10 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
const selectedPath = useFilesViewTabsStore((state) => (root ? (state.byRoot[root]?.selectedPath ?? null) : null));
|
const selectedPath = useFilesViewTabsStore((state) => (root ? (state.byRoot[root]?.selectedPath ?? null) : null));
|
||||||
const expandedPaths = useFilesViewTabsStore((state) => (root ? (state.byRoot[root]?.expandedPaths ?? EMPTY_PATHS) : EMPTY_PATHS));
|
const expandedPaths = useFilesViewTabsStore((state) => (root ? (state.byRoot[root]?.expandedPaths ?? EMPTY_PATHS) : EMPTY_PATHS));
|
||||||
const addOpenPath = useFilesViewTabsStore((state) => state.addOpenPath);
|
const addOpenPath = useFilesViewTabsStore((state) => state.addOpenPath);
|
||||||
const removeOpenPath = useFilesViewTabsStore((state) => state.removeOpenPath);
|
const removeOpenPath = useFilesViewTabsStore((state) => state.removeOpenPath);
|
||||||
const removeOpenPathsByPrefix = useFilesViewTabsStore((state) => state.removeOpenPathsByPrefix);
|
const removeOpenPathsByPrefix = useFilesViewTabsStore((state) => state.removeOpenPathsByPrefix);
|
||||||
const removeExpandedPathsByPrefix = useFilesViewTabsStore((state) => state.removeExpandedPathsByPrefix);
|
const removeExpandedPathsByPrefix = useFilesViewTabsStore((state) => state.removeExpandedPathsByPrefix);
|
||||||
const setSelectedPath = useFilesViewTabsStore((state) => state.setSelectedPath);
|
const setSelectedPath = useFilesViewTabsStore((state) => state.setSelectedPath);
|
||||||
const toggleExpandedPath = useFilesViewTabsStore((state) => state.toggleExpandedPath);
|
const toggleExpandedPath = useFilesViewTabsStore((state) => state.toggleExpandedPath);
|
||||||
const expandPaths = useFilesViewTabsStore((state) => state.expandPaths);
|
const expandPaths = useFilesViewTabsStore((state) => state.expandPaths);
|
||||||
|
|
||||||
@@ -1204,22 +1204,22 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = error instanceof Error ? error.message : String(error ?? '');
|
const message = error instanceof Error ? error.message : String(error ?? '');
|
||||||
if (message === 'Directory not found' && root && normalizedDir !== root) {
|
if (message === 'Directory not found' && root && normalizedDir !== root) {
|
||||||
removeExpandedPathsByPrefix(root, normalizedDir);
|
removeExpandedPathsByPrefix(root, normalizedDir);
|
||||||
setLoadErrorsByDir((prev) => {
|
setLoadErrorsByDir((prev) => {
|
||||||
if (!prev[normalizedDir]) return prev;
|
if (!prev[normalizedDir]) return prev;
|
||||||
const next = { ...prev };
|
const next = { ...prev };
|
||||||
delete next[normalizedDir];
|
delete next[normalizedDir];
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.error('Failed to load files directory:', error);
|
console.error('Failed to load files directory:', error);
|
||||||
setLoadErrorsByDir((prev) => ({
|
setLoadErrorsByDir((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[normalizedDir]: message,
|
[normalizedDir]: message,
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (!isCurrentRequest()) {
|
if (!isCurrentRequest()) {
|
||||||
@@ -1231,7 +1231,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
inFlightDirsRef.current = new Set(inFlightDirsRef.current);
|
inFlightDirsRef.current = new Set(inFlightDirsRef.current);
|
||||||
inFlightDirsRef.current.delete(normalizedDir);
|
inFlightDirsRef.current.delete(normalizedDir);
|
||||||
});
|
});
|
||||||
}, [files, mapDirectoryEntries, removeExpandedPathsByPrefix, root]);
|
}, [files, mapDirectoryEntries, removeExpandedPathsByPrefix, root]);
|
||||||
|
|
||||||
const refreshRoot = React.useCallback(async () => {
|
const refreshRoot = React.useCallback(async () => {
|
||||||
if (!root) {
|
if (!root) {
|
||||||
@@ -2322,14 +2322,14 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
const canEdit = Boolean(selectedFile && !selectedFileIsOutsideWorkspace && !isSelectedImage && !isSelectedPdf && files.writeFile && fileContent.length <= MAX_VIEW_CHARS);
|
const canEdit = Boolean(selectedFile && !selectedFileIsOutsideWorkspace && !isSelectedImage && !isSelectedPdf && files.writeFile && fileContent.length <= MAX_VIEW_CHARS);
|
||||||
const isMarkdown = Boolean(selectedFile?.path && isMarkdownFile(selectedFile.path));
|
const isMarkdown = Boolean(selectedFile?.path && isMarkdownFile(selectedFile.path));
|
||||||
const isJson = Boolean(selectedFile?.path && isJsonFile(selectedFile.path));
|
const isJson = Boolean(selectedFile?.path && isJsonFile(selectedFile.path));
|
||||||
const isHtml = Boolean(selectedFile?.path && isHtmlFile(selectedFile.path));
|
const isHtml = Boolean(selectedFile?.path && isHtmlFile(selectedFile.path));
|
||||||
const isDrawio = Boolean(selectedFile?.path && isDrawioFile(selectedFile.path));
|
const isDrawio = Boolean(selectedFile?.path && isDrawioFile(selectedFile.path));
|
||||||
const isTextFile = Boolean(selectedFile && !isSelectedImage && !isSelectedPdf);
|
const isTextFile = Boolean(selectedFile && !isSelectedImage && !isSelectedPdf);
|
||||||
const canUseShikiFileView = isTextFile && !isMarkdown && !isDrawio && !(isHtml && htmlViewMode === 'preview');
|
const canUseShikiFileView = isTextFile && !isMarkdown && !isDrawio && !(isHtml && htmlViewMode === 'preview');
|
||||||
const isEditingFile = (isMarkdown && mdViewMode === 'edit')
|
const isEditingFile = (isMarkdown && mdViewMode === 'edit')
|
||||||
|| (isHtml && htmlViewMode === 'edit')
|
|| (isHtml && htmlViewMode === 'edit')
|
||||||
|| (isJson && jsonViewMode === 'text')
|
|| (isJson && jsonViewMode === 'text')
|
||||||
|| (!isMarkdown && !isHtml && !isJson && textViewMode === 'edit');
|
|| (!isMarkdown && !isHtml && !isJson && textViewMode === 'edit');
|
||||||
const staticLanguageExtension = React.useMemo(
|
const staticLanguageExtension = React.useMemo(
|
||||||
() => (selectedFilePath ? languageByExtension(selectedFilePath) : null),
|
() => (selectedFilePath ? languageByExtension(selectedFilePath) : null),
|
||||||
[selectedFilePath],
|
[selectedFilePath],
|
||||||
@@ -2373,7 +2373,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTextViewMode(textViewModeByPathRef.current[selectedPath] ?? 'edit');
|
setTextViewMode(textViewModeByPathRef.current[selectedPath] ?? 'edit');
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -2398,7 +2398,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
// Ignore localStorage errors
|
// Ignore localStorage errors
|
||||||
}
|
}
|
||||||
setHtmlViewMode(htmlViewModeByPathRef.current[selectedPath] ?? htmlDefault);
|
setHtmlViewMode(htmlViewModeByPathRef.current[selectedPath] ?? htmlDefault);
|
||||||
setDrawioViewMode(drawioViewModeByPathRef.current[selectedPath] ?? (settingsDefaultFileViewerPreview ? 'preview' : 'edit'));
|
setDrawioViewMode(drawioViewModeByPathRef.current[selectedPath] ?? (settingsDefaultFileViewerPreview ? 'preview' : 'edit'));
|
||||||
|
|
||||||
let jsonDefault: 'tree' | 'text' = settingsDefaultFileViewerPreview ? 'tree' : 'text';
|
let jsonDefault: 'tree' | 'text' = settingsDefaultFileViewerPreview ? 'tree' : 'text';
|
||||||
try {
|
try {
|
||||||
@@ -2562,28 +2562,28 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
}, [htmlViewMode]);
|
}, [htmlViewMode]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const applyDefaultFileViewerMode = (enabled: boolean) => {
|
const applyDefaultFileViewerMode = (enabled: boolean) => {
|
||||||
const previewMode: PreviewViewMode = enabled ? 'preview' : 'edit';
|
const previewMode: PreviewViewMode = enabled ? 'preview' : 'edit';
|
||||||
const nextJsonMode: 'tree' | 'text' = enabled ? 'tree' : 'text';
|
const nextJsonMode: 'tree' | 'text' = enabled ? 'tree' : 'text';
|
||||||
|
|
||||||
for (const path of openPaths) {
|
for (const path of openPaths) {
|
||||||
textViewModeByPathRef.current[path] = 'edit';
|
textViewModeByPathRef.current[path] = 'edit';
|
||||||
if (isMarkdownFile(path)) {
|
if (isMarkdownFile(path)) {
|
||||||
mdViewModeByPathRef.current[path] = previewMode;
|
mdViewModeByPathRef.current[path] = previewMode;
|
||||||
}
|
}
|
||||||
if (isHtmlFile(path)) {
|
if (isHtmlFile(path)) {
|
||||||
htmlViewModeByPathRef.current[path] = previewMode;
|
htmlViewModeByPathRef.current[path] = previewMode;
|
||||||
}
|
}
|
||||||
if (isDrawioFile(path)) {
|
if (isDrawioFile(path)) {
|
||||||
drawioViewModeByPathRef.current[path] = previewMode;
|
drawioViewModeByPathRef.current[path] = previewMode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTextViewMode('edit');
|
setTextViewMode('edit');
|
||||||
setMdViewMode(previewMode);
|
setMdViewMode(previewMode);
|
||||||
setHtmlViewMode(previewMode);
|
setHtmlViewMode(previewMode);
|
||||||
setDrawioViewMode(previewMode);
|
setDrawioViewMode(previewMode);
|
||||||
setJsonViewMode(nextJsonMode);
|
setJsonViewMode(nextJsonMode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(MD_VIEWER_MODE_KEY, previewMode);
|
localStorage.setItem(MD_VIEWER_MODE_KEY, previewMode);
|
||||||
@@ -2762,32 +2762,30 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (selectedFile?.path !== targetPath) {
|
if (selectedFile?.path !== targetPath) {
|
||||||
if (confirmDiscardOpen) {
|
// Selection is owned by the tab sync / user. A pending focus request must
|
||||||
return;
|
// not steal selection back (e.g. after the user switched to another tab
|
||||||
}
|
// while this file was still loading). Wait; clear once it loads or the
|
||||||
void handleSelectFile(toFileNode(targetPath));
|
// request is superseded.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileLoading || loadedFilePath !== targetPath || fileError || isSelectedImage || isSelectedPdf) {
|
if (fileLoading || loadedFilePath !== targetPath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canEdit && textViewMode === 'edit') {
|
// Best-effort focus: preview renderers (markdown/html preview, drawio,
|
||||||
const view = editorViewRef.current;
|
// JSON tree, images, PDFs) never mount a CodeMirror editor, so the request
|
||||||
if (!view) {
|
// must clear regardless — otherwise it lingers and replays on every
|
||||||
return;
|
// dependency change.
|
||||||
}
|
if (!fileError && !isSelectedImage && !isSelectedPdf && canEdit && textViewMode === 'edit') {
|
||||||
view.focus();
|
editorViewRef.current?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
setPendingFileFocusPath(null);
|
setPendingFileFocusPath(null);
|
||||||
}, [
|
}, [
|
||||||
canEdit,
|
canEdit,
|
||||||
confirmDiscardOpen,
|
|
||||||
fileError,
|
fileError,
|
||||||
fileLoading,
|
fileLoading,
|
||||||
handleSelectFile,
|
|
||||||
isSelectedImage,
|
isSelectedImage,
|
||||||
isSelectedPdf,
|
isSelectedPdf,
|
||||||
loadedFilePath,
|
loadedFilePath,
|
||||||
@@ -2796,7 +2794,6 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
selectedFile?.path,
|
selectedFile?.path,
|
||||||
setPendingFileFocusPath,
|
setPendingFileFocusPath,
|
||||||
textViewMode,
|
textViewMode,
|
||||||
toFileNode,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const nudgeEditorSelectionAboveKeyboard = React.useCallback((view: EditorView | null) => {
|
const nudgeEditorSelectionAboveKeyboard = React.useCallback((view: EditorView | null) => {
|
||||||
@@ -3158,7 +3155,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={wrapperCls}>
|
<div className={wrapperCls}>
|
||||||
{canEdit && isEditingFile && (
|
{canEdit && isEditingFile && (
|
||||||
<>
|
<>
|
||||||
{isSaving ? (
|
{isSaving ? (
|
||||||
<span className="flex items-center gap-1 px-1 text-muted-foreground typography-meta">
|
<span className="flex items-center gap-1 px-1 text-muted-foreground typography-meta">
|
||||||
@@ -3308,35 +3305,35 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isMarkdown && (
|
{isMarkdown && (
|
||||||
withTooltip(
|
withTooltip(
|
||||||
t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode'),
|
t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode'),
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => saveMdViewMode(getMdViewMode() === 'preview' ? 'edit' : 'preview')}
|
onClick={() => saveMdViewMode(getMdViewMode() === 'preview' ? 'edit' : 'preview')}
|
||||||
className={cn(
|
className={cn(
|
||||||
'size-6 p-0 transition-colors hover:bg-[var(--interactive-hover)] focus-visible:bg-[var(--interactive-hover)] active:bg-[var(--interactive-hover)]',
|
'size-6 p-0 transition-colors hover:bg-[var(--interactive-hover)] focus-visible:bg-[var(--interactive-hover)] active:bg-[var(--interactive-hover)]',
|
||||||
getMdViewMode() === 'preview'
|
getMdViewMode() === 'preview'
|
||||||
? 'bg-[var(--interactive-selection)] text-[var(--interactive-selection-foreground)] hover:bg-[var(--interactive-selection)] focus-visible:bg-[var(--interactive-selection)] active:bg-[var(--interactive-selection)]'
|
? 'bg-[var(--interactive-selection)] text-[var(--interactive-selection-foreground)] hover:bg-[var(--interactive-selection)] focus-visible:bg-[var(--interactive-selection)] active:bg-[var(--interactive-selection)]'
|
||||||
: 'text-muted-foreground opacity-65 hover:opacity-100'
|
: 'text-muted-foreground opacity-65 hover:opacity-100'
|
||||||
)}
|
)}
|
||||||
title={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
|
title={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
|
||||||
aria-label={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
|
aria-label={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
|
||||||
>
|
>
|
||||||
<Icon name={getMdViewMode() === 'preview' ? 'eye' : 'eye-off'} className="size-4" />
|
<Icon name={getMdViewMode() === 'preview' ? 'eye' : 'eye-off'} className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isHtmlFile(selectedFile?.path ?? '') && (
|
{isHtmlFile(selectedFile?.path ?? '') && (
|
||||||
<PreviewToggleButton
|
<PreviewToggleButton
|
||||||
currentMode={getHtmlViewMode()}
|
currentMode={getHtmlViewMode()}
|
||||||
onToggle={() => {
|
onToggle={() => {
|
||||||
saveHtmlViewMode(getHtmlViewMode() === 'preview' ? 'edit' : 'preview');
|
saveHtmlViewMode(getHtmlViewMode() === 'preview' ? 'edit' : 'preview');
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isMarkdown && getMdViewMode() === 'preview' && showMessageTTSButtons && (
|
{isMarkdown && getMdViewMode() === 'preview' && showMessageTTSButtons && (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
@@ -3736,68 +3733,68 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
|||||||
|
|
||||||
<div className="flex-1 min-h-0 min-w-0 relative">
|
<div className="flex-1 min-h-0 min-w-0 relative">
|
||||||
{selectedFile && !isSearchOpen && !(settingsExpandedEditorToolbar && !isMobile) && (
|
{selectedFile && !isSearchOpen && !(settingsExpandedEditorToolbar && !isMobile) && (
|
||||||
<div
|
<div
|
||||||
ref={floatingToolbarRef}
|
ref={floatingToolbarRef}
|
||||||
className="absolute right-3 top-3 z-30"
|
className="absolute right-3 top-3 z-30"
|
||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
if (toolbarDropdownOpenCountRef.current > 0) return;
|
if (toolbarDropdownOpenCountRef.current > 0) return;
|
||||||
setIsFloatingToolbarOpen(false);
|
setIsFloatingToolbarOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isFloatingToolbarOpen ? (
|
{isFloatingToolbarOpen ? (
|
||||||
renderFloatingFileControls()
|
renderFloatingFileControls()
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{isMarkdown ? (
|
{isMarkdown ? (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<span className="inline-flex">
|
<span className="inline-flex">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => saveMdViewMode(getMdViewMode() === 'preview' ? 'edit' : 'preview')}
|
onClick={() => saveMdViewMode(getMdViewMode() === 'preview' ? 'edit' : 'preview')}
|
||||||
className={cn(
|
className={cn(
|
||||||
'size-8 rounded-lg border border-[var(--interactive-border)] bg-[var(--surface-elevated)] p-0 shadow-sm transition-colors',
|
'size-8 rounded-lg border border-[var(--interactive-border)] bg-[var(--surface-elevated)] p-0 shadow-sm transition-colors',
|
||||||
getMdViewMode() === 'preview'
|
getMdViewMode() === 'preview'
|
||||||
? 'bg-[var(--interactive-selection)] text-[var(--interactive-selection-foreground)] hover:bg-[var(--interactive-selection)]'
|
? 'bg-[var(--interactive-selection)] text-[var(--interactive-selection-foreground)] hover:bg-[var(--interactive-selection)]'
|
||||||
: 'text-muted-foreground hover:text-foreground'
|
: 'text-muted-foreground hover:text-foreground'
|
||||||
)}
|
)}
|
||||||
aria-label={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
|
aria-label={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
|
||||||
title={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
|
title={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
|
||||||
>
|
>
|
||||||
<Icon name={getMdViewMode() === 'preview' ? 'eye' : 'eye-off'} className="size-4" />
|
<Icon name={getMdViewMode() === 'preview' ? 'eye' : 'eye-off'} className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent side="bottom" sideOffset={6}>
|
<TooltipContent side="bottom" sideOffset={6}>
|
||||||
{t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
|
{t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : null}
|
) : null}
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<span
|
<span
|
||||||
className="inline-flex"
|
className="inline-flex"
|
||||||
onMouseEnter={() => setIsFloatingToolbarOpen(true)}
|
onMouseEnter={() => setIsFloatingToolbarOpen(true)}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setIsFloatingToolbarOpen(true)}
|
onClick={() => setIsFloatingToolbarOpen(true)}
|
||||||
className="size-8 rounded-lg border border-[var(--interactive-border)] bg-[var(--surface-elevated)] p-0 text-muted-foreground shadow-sm hover:text-foreground"
|
className="size-8 rounded-lg border border-[var(--interactive-border)] bg-[var(--surface-elevated)] p-0 text-muted-foreground shadow-sm hover:text-foreground"
|
||||||
aria-label={t('filesView.editor.showControlsAria')}
|
aria-label={t('filesView.editor.showControlsAria')}
|
||||||
title={t('filesView.editor.controlsTitle')}
|
title={t('filesView.editor.controlsTitle')}
|
||||||
>
|
>
|
||||||
<Icon name="more-2-fill" className="size-4" />
|
<Icon name="more-2-fill" className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent side="bottom" sideOffset={6}>{t('filesView.editor.controlsTitle')}</TooltipContent>
|
<TooltipContent side="bottom" sideOffset={6}>{t('filesView.editor.controlsTitle')}</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ScrollableOverlay outerClassName="h-full min-w-0" className="h-full min-w-0">
|
<ScrollableOverlay outerClassName="h-full min-w-0" className="h-full min-w-0">
|
||||||
{!selectedFile ? (
|
{!selectedFile ? (
|
||||||
<div className="p-3 typography-ui text-muted-foreground">{t('filesView.editor.pickFileFromTree')}</div>
|
<div className="p-3 typography-ui text-muted-foreground">{t('filesView.editor.pickFileFromTree')}</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user