diff --git a/src/components/chat/ChatAttachmentItems.jsx b/src/components/chat/ChatAttachmentItems.jsx
index 9932626..4337460 100644
--- a/src/components/chat/ChatAttachmentItems.jsx
+++ b/src/components/chat/ChatAttachmentItems.jsx
@@ -9,7 +9,6 @@ import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import IconButton from '@mui/material/IconButton';
import CircularProgress from '@mui/material/CircularProgress';
-import Tooltip from '@mui/material/Tooltip';
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
import PictureAsPdfRoundedIcon from '@mui/icons-material/PictureAsPdfRounded';
import TableChartRoundedIcon from '@mui/icons-material/TableChartRounded';
@@ -399,7 +398,6 @@ export class VfsAttachmentLink extends PureComponent {
target="_blank"
rel="noopener noreferrer"
onClick={this.handleClick}
- title={notFound ? 'Nicht mehr vorhanden (404)' : `VFS Bild im neuen Tab öffnen: ${downloadName}`}
sx={{
display: 'block',
cursor: notFound ? 'not-allowed' : 'pointer',
@@ -437,18 +435,17 @@ export class VfsAttachmentLink extends PureComponent {
}}
>
-
- {downloadName}
+ {downloadName} - {effectiveType || 'image'}
@@ -457,24 +454,22 @@ export class VfsAttachmentLink extends PureComponent {
)}
{!notFound && (
<>
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
>
)}
@@ -483,142 +478,108 @@ export class VfsAttachmentLink extends PureComponent {
);
}
- const titleText = notFound
- ? `Datei nicht mehr im VFS vorhanden (404): ${downloadName}`
- : isOpening
- ? `Wird im neuen Tab geöffnet…`
- : isDownloading
- ? `Wird heruntergeladen…`
- : canView
- ? `In neuem Tab öffnen (${downloadName})`
- : `Herunterladen (${downloadName})`;
-
return (
-
- (theme.palette.mode === 'dark' ? 'background.paper' : 'rgba(0, 0, 0, 0.02)')),
+ textDecoration: notFound ? 'line-through' : 'none',
+ color: notFound
+ ? (isUser ? 'rgba(255,255,255,0.6)' : 'text.disabled')
+ : (isUser ? 'inherit' : 'primary.main'),
+ opacity: notFound ? 0.7 : ((isDownloading || isOpening) ? 0.75 : 1),
+ cursor: notFound ? 'not-allowed' : 'pointer',
+ verticalAlign: 'middle',
+ maxWidth: '100%',
+ transition: 'all 150ms ease-in-out',
+ '&:hover': notFound ? {} : {
+ bgcolor: isUser ? 'rgba(255, 255, 255, 0.28)' : 'action.hover',
+ borderColor: isUser ? '#fff' : 'primary.main',
+ boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
+ },
+ }}
+ >
+ (theme.palette.mode === 'dark' ? 'background.paper' : 'rgba(0, 0, 0, 0.02)')),
+ fontSize: '0.82rem',
+ fontWeight: 500,
+ color: 'inherit',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap',
+ lineHeight: 1.25,
textDecoration: notFound ? 'line-through' : 'none',
- color: notFound
- ? (isUser ? 'rgba(255,255,255,0.6)' : 'text.disabled')
- : (isUser ? 'inherit' : 'primary.main'),
- opacity: notFound ? 0.7 : ((isDownloading || isOpening) ? 0.75 : 1),
- cursor: notFound ? 'not-allowed' : 'pointer',
- verticalAlign: 'middle',
- maxWidth: '100%',
- transition: 'all 150ms ease-in-out',
- '&:hover': notFound ? {} : {
- bgcolor: isUser ? 'rgba(255, 255, 255, 0.28)' : 'action.hover',
- borderColor: isUser ? '#fff' : 'primary.main',
- boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
- },
}}
>
- {isDownloading || isOpening ? (
-
- ) : notFound ? (
-
- ) : (
- getAttachmentIcon(effectiveType, downloadName, isUser)
- )}
-
-
+ {!notFound && (
+ e.stopPropagation()}
+ >
+ {(isDownloading || isOpening) && (
+
+ )}
+ {canView && (
+
+
+
+ )}
+
- {displayName}
-
-
- {notFound
- ? 'nicht mehr vorhanden (404)'
- : (label && label !== downloadName ? `${downloadName} · ${effectiveType || 'VFS'}` : (effectiveType || 'VFS Datei'))}
-
+
+
- {!notFound && (
- e.stopPropagation()}
- >
- {canView && (
-
-
-
-
-
- )}
-
-
-
-
-
-
- )}
-
-
+ )}
+
);
}
}
diff --git a/src/components/chat/HealingMarkdown.jsx b/src/components/chat/HealingMarkdown.jsx
index 7b5611d..4a576a3 100644
--- a/src/components/chat/HealingMarkdown.jsx
+++ b/src/components/chat/HealingMarkdown.jsx
@@ -9,7 +9,7 @@ import {
TABLE_HEADER_PAIR,
byMode,
} from '../../colors';
-import { VfsAttachmentLink, parseAttachmentTag, classifyLinkTarget } from './ChatAttachmentItems';
+import { VfsAttachmentLink, parseAttachmentTag, classifyLinkTarget, resolveVfsUrl } from './ChatAttachmentItems';
/**
* Healing markdown renderer.
@@ -43,16 +43,14 @@ function resolveImageUrl(rawUrl) {
const url = String(rawUrl ?? '').trim();
if (!url) return null;
if (/^data:image\//i.test(url)) return url;
- if (/^https?:\/\//i.test(url) || url.startsWith('/')) return url;
-
- // Everything else is a VFS name: strip a leading `./`, encode each segment, keep subdirs.
- const segments = url
- .replace(/^\.\//, '')
- .split('/')
- .filter((s) => s && s !== '.')
- .map((s) => encodeURIComponent(s));
- if (segments.length === 0 || segments.includes('..')) return null;
- return `/api/vfs/${segments.join('/')}`;
+ const classified = classifyLinkTarget(url);
+ if (classified.kind === 'web') return classified.url;
+ if (classified.kind === 'vfs') {
+ if (classified.path.includes('..')) return null;
+ return resolveVfsUrl(classified.path);
+ }
+ if (classified.kind === 'nav') return classified.url;
+ return null;
}
/**