diff --git a/src/components/chat/ChatDrawer.jsx b/src/components/chat/ChatDrawer.jsx index d25e770..a94214c 100644 --- a/src/components/chat/ChatDrawer.jsx +++ b/src/components/chat/ChatDrawer.jsx @@ -797,11 +797,11 @@ export default class ChatDrawer extends PureComponent { )} - + vfsAutomat.actions.toggleVfs()} - aria-label="VFS Dateisystem" + aria-label="Dateien" sx={{ p: 0.5, color: isVfsOpen ? 'primary.main' : 'text.secondary', @@ -1322,36 +1322,54 @@ export default class ChatDrawer extends PureComponent { )} - {/* Vertical Split Resizer between VFS and Chat */} + {/* Vertical Split Resizer between VFS and Chat (0px width so no layout shift or grey bar) */} {isVfsOpen && ( + {/* Invisible wide hit area (16px wide, centered on the 1px border) */} + {/* Visual handle indicator on hover / active */} + - theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.35)' : 'rgba(0,0,0,0.25)', + bgcolor: 'divider', + opacity: 0.5, + transition: 'background-color 0.15s, height 0.15s, width 0.15s, opacity 0.15s, box-shadow 0.15s', + pointerEvents: 'none', }} /> diff --git a/src/components/vfs/VfsEditorPanel.jsx b/src/components/vfs/VfsEditorPanel.jsx index 0bb9551..5ecce1f 100644 --- a/src/components/vfs/VfsEditorPanel.jsx +++ b/src/components/vfs/VfsEditorPanel.jsx @@ -2,7 +2,6 @@ import React, { PureComponent } from 'react'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import IconButton from '@mui/material/IconButton'; -import Tooltip from '@mui/material/Tooltip'; import FolderOpenRoundedIcon from '@mui/icons-material/FolderOpenRounded'; import CloseRoundedIcon from '@mui/icons-material/CloseRounded'; @@ -61,13 +60,20 @@ export default class VfsEditorPanel extends PureComponent { this.unsub?.(); } - handleMentionInChat = (file) => { + handleMentionInChat = (file, lineInfo = null) => { if (!file) return; if (this.props.onInsertToChat) { const isImg = file.isImage; - const tag = isImg - ? `![${file.name}](${file.name})` - : `[${file.name}](${file.name})`; + let tag = ''; + if (lineInfo?.startLine) { + const lineRef = + lineInfo.startLine === lineInfo.endLine + ? `L${lineInfo.startLine}` + : `L${lineInfo.startLine}-L${lineInfo.endLine}`; + tag = `[${file.name}#${lineRef}](${file.name}#${lineRef})`; + } else { + tag = isImg ? `![${file.name}](${file.name})` : `[${file.name}](${file.name})`; + } this.props.onInsertToChat(tag); } }; @@ -116,7 +122,7 @@ export default class VfsEditorPanel extends PureComponent { onSave={() => vfsAutomat.actions.saveCurrentFile()} onClose={() => vfsAutomat.actions.selectFile(null)} onDownload={() => vfsAutomat.actions.downloadFile(selectedFileName)} - onMentionInChat={() => this.handleMentionInChat(selectedFile)} + onMentionInChat={(lineInfo) => this.handleMentionInChat(selectedFile, lineInfo)} /> ) : selectedFileName && !selectedFile?.isText ? ( /* Single Column Mode: Preview (Image, PDF, Binary) */ @@ -147,19 +153,17 @@ export default class VfsEditorPanel extends PureComponent { - VFS Dateisystem + Dateien - - vfsAutomat.actions.closeVfs()} - aria-label="VFS schließen" - > - - - + vfsAutomat.actions.closeVfs()} + aria-label="VFS schließen" + > + + diff --git a/src/components/vfs/VfsFileExplorer.jsx b/src/components/vfs/VfsFileExplorer.jsx index f582dc2..ada6e12 100644 --- a/src/components/vfs/VfsFileExplorer.jsx +++ b/src/components/vfs/VfsFileExplorer.jsx @@ -2,18 +2,14 @@ import React, { PureComponent } from 'react'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import IconButton from '@mui/material/IconButton'; -import Button from '@mui/material/Button'; import InputBase from '@mui/material/InputBase'; -import Tooltip from '@mui/material/Tooltip'; import CircularProgress from '@mui/material/CircularProgress'; -import Chip from '@mui/material/Chip'; import Menu from '@mui/material/Menu'; import MenuItem from '@mui/material/MenuItem'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import SearchRoundedIcon from '@mui/icons-material/SearchRounded'; import ClearRoundedIcon from '@mui/icons-material/ClearRounded'; -import RefreshRoundedIcon from '@mui/icons-material/RefreshRounded'; import NoteAddRoundedIcon from '@mui/icons-material/NoteAddRounded'; import CloudUploadRoundedIcon from '@mui/icons-material/CloudUploadRounded'; import DownloadRoundedIcon from '@mui/icons-material/DownloadRounded'; @@ -28,7 +24,7 @@ import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRigh import ContentCopyRoundedIcon from '@mui/icons-material/ContentCopyRounded'; import ChatBubbleOutlineRoundedIcon from '@mui/icons-material/ChatBubbleOutlineRounded'; -import VfsFileIcon, { getCategoryBadge } from './VfsFileIcon'; +import VfsFileIcon from './VfsFileIcon'; import { formatVfsSize, formatVfsDate } from './vfsUtils'; /** @@ -336,7 +332,6 @@ export default class VfsFileExplorer extends PureComponent { renderFileNode = (file, depth) => { const { selectedFileName, onSelectFile } = this.props; const isSelected = file.path === selectedFileName; - const badge = getCategoryBadge(file.path, file.fileData.mime); return ( - - - {file.name} - - - theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)', - color: badge.color, - }} - /> - + + {file.name} + @@ -417,24 +397,14 @@ export default class VfsFileExplorer extends PureComponent { sx={{ display: 'flex', alignItems: 'center', - gap: 0.2, opacity: isSelected ? 1 : 0.7, '&:hover': { opacity: 1 }, }} > - - this.props.onDownload(file.path)} - sx={{ p: 0.5 }} - > - - - - this.handleOpenMenu(e, file.fileData)} + aria-label="Weitere Optionen" sx={{ p: 0.5 }} > @@ -561,62 +531,22 @@ export default class VfsFileExplorer extends PureComponent { )} - {/* Explorer Header / Actions */} + {/* Explorer Header / Actions Row */} - {/* Top Bar with Action Buttons */} - - - {loading && } - - - - - - - - - - - - - - - - - - - - - {/* Search Filter Bar */} ({ + flexGrow: 1, display: 'flex', alignItems: 'center', px: 1, @@ -638,12 +568,33 @@ export default class VfsFileExplorer extends PureComponent { onChange={(e) => onSearchChange(e.target.value)} sx={{ fontSize: '0.82rem' }} /> + {loading && } {searchQuery && ( onSearchChange('')} sx={{ p: 0.2 }}> )} + + {/* Upload Icon Button (no word) */} + + + + + {/* New File Icon Button (no word) */} + + + {/* Scrollable File List */} diff --git a/src/components/vfs/VfsFilePreview.jsx b/src/components/vfs/VfsFilePreview.jsx index e1f509b..fc38916 100644 --- a/src/components/vfs/VfsFilePreview.jsx +++ b/src/components/vfs/VfsFilePreview.jsx @@ -3,15 +3,13 @@ import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import Button from '@mui/material/Button'; import IconButton from '@mui/material/IconButton'; -import Tooltip from '@mui/material/Tooltip'; -import Chip from '@mui/material/Chip'; import OpenInNewRoundedIcon from '@mui/icons-material/OpenInNewRounded'; import DownloadRoundedIcon from '@mui/icons-material/DownloadRounded'; import CloseRoundedIcon from '@mui/icons-material/CloseRounded'; import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'; import ChatBubbleOutlineRoundedIcon from '@mui/icons-material/ChatBubbleOutlineRounded'; -import VfsFileIcon, { getCategoryBadge } from './VfsFileIcon'; +import VfsFileIcon from './VfsFileIcon'; import { formatVfsSize, formatVfsDate } from './vfsUtils'; export default class VfsFilePreview extends PureComponent { @@ -128,7 +126,6 @@ export default class VfsFilePreview extends PureComponent { render() { const { fileName, file, onClose, onDownload, onMentionInChat } = this.props; - const badge = getCategoryBadge(fileName, file?.mime); const vfsUrl = `/api/vfs/${encodeURIComponent(fileName).replace(/%2F/g, '/')}?raw=1`; return ( @@ -149,11 +146,9 @@ export default class VfsFilePreview extends PureComponent { }} > - - - - - + + + {fileName} - - theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)', - color: badge.color, - }} - /> {onMentionInChat && ( - - - - - + + + )} - - - - - - - - - - - + + + diff --git a/src/components/vfs/VfsTextEditor.jsx b/src/components/vfs/VfsTextEditor.jsx index e397c8c..a6f2d1a 100644 --- a/src/components/vfs/VfsTextEditor.jsx +++ b/src/components/vfs/VfsTextEditor.jsx @@ -3,16 +3,13 @@ import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import IconButton from '@mui/material/IconButton'; import Button from '@mui/material/Button'; -import Tooltip from '@mui/material/Tooltip'; import CircularProgress from '@mui/material/CircularProgress'; import Chip from '@mui/material/Chip'; import SaveRoundedIcon from '@mui/icons-material/SaveRounded'; import UndoRoundedIcon from '@mui/icons-material/UndoRounded'; -import DownloadRoundedIcon from '@mui/icons-material/DownloadRounded'; import CloseRoundedIcon from '@mui/icons-material/CloseRounded'; import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'; import ChatBubbleOutlineRoundedIcon from '@mui/icons-material/ChatBubbleOutlineRounded'; -import WrapTextRoundedIcon from '@mui/icons-material/WrapTextRounded'; import Alert from '@mui/material/Alert'; import VfsFileIcon from './VfsFileIcon'; @@ -69,6 +66,37 @@ export default class VfsTextEditor extends PureComponent { } }; + handleMentionInChat = () => { + const ta = this.textareaRef.current; + let lineInfo = null; + if (ta) { + const start = ta.selectionStart; + const end = ta.selectionEnd; + if (typeof start === 'number' && typeof end === 'number' && end > start) { + const content = this.props.content || ''; + const beforeStart = content.substring(0, start); + const startLine = beforeStart.split('\n').length; + + let effectiveEnd = end; + if (effectiveEnd > start && content[effectiveEnd - 1] === '\n') { + effectiveEnd -= 1; + } + const beforeEnd = content.substring(0, effectiveEnd); + const endLine = Math.max(startLine, beforeEnd.split('\n').length); + + lineInfo = { + startLine, + endLine, + selectedText: content.substring(start, end), + }; + } + } + + if (this.props.onMentionInChat) { + this.props.onMentionInChat(lineInfo); + } + }; + handleKeyDown = (e) => { // Ctrl+S or Cmd+S -> Save if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 's') { @@ -159,11 +187,9 @@ export default class VfsTextEditor extends PureComponent { > {/* File Name & Status */} - - - - - + + + - {isDirty ? ( + {isDirty && ( - ) : ( - )} {/* Action Buttons */} {onMentionInChat && ( - - - - - - )} - - - - + + - + )} {isDirty && ( - - - - - - )} - - - - + + - + )}