This commit is contained in:
seb
2026-10-16 14:32:10 +02:00
parent a592cc30f6
commit 5c57097ad0
4 changed files with 136 additions and 5 deletions

View File

@@ -20,7 +20,12 @@ import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined';
import Tooltip from '@mui/material/Tooltip';
import Badge from '@mui/material/Badge';
import Divider from '@mui/material/Divider';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';
import ContentCopyRoundedIcon from '@mui/icons-material/ContentCopyRounded';
import TuneRoundedIcon from '@mui/icons-material/TuneRounded';
import ElderlyRoundedIcon from '@mui/icons-material/ElderlyRounded';
import DirectionsWalkRoundedIcon from '@mui/icons-material/DirectionsWalkRounded';
@@ -47,6 +52,7 @@ import {
} from '../../colors';
const DEFAULT_DRAWER_WIDTH = 460;
const EXTRACTION_MODEL_INSTRUCTION = "use 'Process Incoming Product and Invoices' SKILL to fill drafts/delivery vfs file";
/** Sort by updated_at desc; subagents (started_by set) directly below their parent. */
function sortConversations(list) {
@@ -122,9 +128,11 @@ export default class ChatDrawer extends PureComponent {
id: `ext-${Date.now()}`,
text: s.pendingExtraction.text,
label: s.pendingExtraction.label || 'parsed invoice',
instruction: s.pendingExtraction.instruction || EXTRACTION_MODEL_INSTRUCTION,
},
]
: [],
previewExtraction: null,
isProcessingFiles: false,
isDraggingFiles: false,
attachmentError: null,
@@ -234,6 +242,7 @@ export default class ChatDrawer extends PureComponent {
id: `ext-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
text: ext.text,
label: ext.label || 'parsed invoice',
instruction: ext.instruction || EXTRACTION_MODEL_INSTRUCTION,
};
patch.stagedExtractions = [...(this.state.stagedExtractions || []), newExt];
queueMicrotask(() => {
@@ -348,6 +357,22 @@ export default class ChatDrawer extends PureComponent {
this.focusInput(50);
};
handlePreviewExtraction = (ext) => {
console.log('[ChatDrawer] Clicked extraction pill:', ext);
this.setState({ previewExtraction: ext });
};
handleCloseExtractionPreview = () => {
this.setState({ previewExtraction: null });
};
handleCopyExtractionText = () => {
const text = this.state.previewExtraction?.text;
if (text && navigator?.clipboard?.writeText) {
navigator.clipboard.writeText(text);
}
};
handleVfsResizerStart = (e) => {
if (e.button !== undefined && e.button !== 0) return;
e.preventDefault();
@@ -534,10 +559,22 @@ export default class ChatDrawer extends PureComponent {
let text = rawText;
if (extractions.length > 0) {
const instructions = extractions
.map((e) => e.instruction)
.filter(Boolean);
const uniqueInstructions = Array.from(new Set(instructions));
const instructionText =
uniqueInstructions.join('\n\n') || EXTRACTION_MODEL_INSTRUCTION;
const extMarkdown = extractions
.map((ext) => `\`\`\`\n${ext.text.trim()}\n\`\`\``)
.join('\n\n');
text = text ? `${extMarkdown}\n\n${text}` : extMarkdown;
if (text) {
text = `${instructionText}\n\n${text}\n\n${extMarkdown}`;
} else {
text = `${instructionText}\n\n${extMarkdown}`;
}
}
if (references.length > 0) {
@@ -1601,6 +1638,10 @@ export default class ChatDrawer extends PureComponent {
{stagedExtractions && stagedExtractions.map((ext) => (
<Box
key={ext.id}
onClick={(e) => {
e.stopPropagation();
this.handlePreviewExtraction(ext);
}}
sx={{
display: 'inline-flex',
alignItems: 'center',
@@ -1622,6 +1663,15 @@ export default class ChatDrawer extends PureComponent {
fontSize: '0.75rem',
lineHeight: 1.2,
userSelect: 'none',
cursor: 'pointer',
transition: 'all 0.15s ease',
'&:hover': {
bgcolor: (theme) =>
theme.palette.mode === 'dark'
? 'rgba(255, 255, 255, 0.14)'
: 'rgba(0, 0, 0, 0.09)',
borderColor: 'primary.light',
},
}}
>
<DescriptionOutlinedIcon sx={{ fontSize: 14, color: 'primary.main' }} />
@@ -1921,6 +1971,71 @@ export default class ChatDrawer extends PureComponent {
{this.renderFooter()}
</Box>
</Drawer>
{/* Extraction Preview Dialog */}
<Dialog
open={Boolean(this.state.previewExtraction)}
onClose={this.handleCloseExtractionPreview}
maxWidth="md"
fullWidth
slotProps={{
paper: {
sx: {
borderRadius: 2.5,
bgcolor: 'background.paper',
backgroundImage: 'none',
},
},
}}
>
<DialogTitle sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', pb: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<DescriptionOutlinedIcon sx={{ color: 'primary.main', fontSize: 22 }} />
<Typography variant="h6" component="span" sx={{ fontSize: '1.05rem', fontWeight: 600 }}>
{this.state.previewExtraction?.label || 'parsed invoice'}
</Typography>
</Box>
<IconButton size="small" onClick={this.handleCloseExtractionPreview} aria-label="Schließen">
<CloseRoundedIcon sx={{ fontSize: 20 }} />
</IconButton>
</DialogTitle>
<DialogContent dividers sx={{ p: 2, bgcolor: (theme) => theme.palette.mode === 'dark' ? 'rgba(0,0,0,0.2)' : 'rgba(0,0,0,0.02)' }}>
<Box
component="pre"
sx={{
m: 0,
p: 1.5,
borderRadius: 1.5,
fontFamily: 'monospace',
fontSize: '0.8rem',
lineHeight: 1.5,
whiteSpace: 'pre-wrap',
wordBreak: 'break-word',
overflow: 'auto',
maxHeight: '60vh',
bgcolor: 'background.paper',
border: '1px solid',
borderColor: 'divider',
}}
>
{this.state.previewExtraction?.text || ''}
</Box>
</DialogContent>
<DialogActions sx={{ px: 2.5, py: 1.5 }}>
<Button
startIcon={<ContentCopyRoundedIcon sx={{ fontSize: 18 }} />}
onClick={this.handleCopyExtractionText}
color="primary"
variant="outlined"
size="small"
>
Kopieren
</Button>
<Button onClick={this.handleCloseExtractionPreview} color="inherit" size="small">
Schließen
</Button>
</DialogActions>
</Dialog>
</>
);
}