qmd-web / src /components /DocumentManager.tsx
shreyask's picture
feat: align browser demo with qmd pipeline
eb89325 verified
import { useRef, useState } from 'react';
interface DocumentManagerProps {
documents: Array<{ id: string; title: string; filepath: string }>;
onUpload: (files: FileList) => void;
onPaste: (text: string, filename: string) => void;
}
function PasteModal({ onClose, onConfirm }: { onClose: () => void; onConfirm: (text: string, filename: string) => void }) {
const [text, setText] = useState('');
const [filename, setFilename] = useState('pasted-document.md');
function handleConfirm() {
const trimmed = text.trim();
if (!trimmed) return;
onConfirm(trimmed, filename.trim() || 'pasted-document.md');
onClose();
}
return (
<div style={{
position: 'fixed',
inset: 0,
background: 'var(--modal-bg)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 1000,
}}
onClick={e => { if (e.target === e.currentTarget) onClose(); }}
>
<div style={{
background: 'var(--bg-card)',
borderRadius: '10px',
padding: '1.5rem',
width: '90%',
maxWidth: '560px',
boxShadow: '0 8px 32px var(--shadow)',
fontFamily: 'system-ui, -apple-system, sans-serif',
border: '1px solid var(--border)',
}}>
<h3 style={{ margin: '0 0 1rem 0', fontSize: '1rem', color: 'var(--text)' }}>
Paste Document
</h3>
<div style={{ marginBottom: '0.75rem' }}>
<label style={{ fontSize: '0.8rem', color: 'var(--text-secondary)', display: 'block', marginBottom: '0.3rem' }}>
Filename
</label>
<input
type="text"
value={filename}
onChange={e => setFilename(e.target.value)}
style={{
width: '100%',
padding: '0.45rem 0.65rem',
fontSize: '0.85rem',
fontFamily: "'SF Mono', 'Fira Code', 'Cascadia Code', monospace",
border: '1px solid var(--input-border)',
borderRadius: '5px',
boxSizing: 'border-box',
background: 'var(--bg-input)',
color: 'var(--text)',
}}
/>
</div>
<div style={{ marginBottom: '1rem' }}>
<label style={{ fontSize: '0.8rem', color: 'var(--text-secondary)', display: 'block', marginBottom: '0.3rem' }}>
Content (Markdown or plain text)
</label>
<textarea
value={text}
onChange={e => setText(e.target.value)}
rows={12}
placeholder="Paste your document content here\u2026"
style={{
width: '100%',
padding: '0.5rem 0.65rem',
fontSize: '0.8rem',
fontFamily: "'SF Mono', 'Fira Code', 'Cascadia Code', monospace",
border: '1px solid var(--input-border)',
borderRadius: '5px',
resize: 'vertical',
boxSizing: 'border-box',
lineHeight: 1.5,
background: 'var(--bg-input)',
color: 'var(--text)',
}}
/>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '0.5rem' }}>
<button
onClick={onClose}
style={{
padding: '0.5rem 1rem',
fontSize: '0.85rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
background: 'var(--bg-section)',
color: 'var(--text-secondary)',
border: '1px solid var(--border)',
borderRadius: '5px',
cursor: 'pointer',
}}
>
Cancel
</button>
<button
onClick={handleConfirm}
disabled={!text.trim()}
style={{
padding: '0.5rem 1rem',
fontSize: '0.85rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
background: text.trim() ? '#4285F4' : 'var(--border)',
color: '#fff',
border: 'none',
borderRadius: '5px',
cursor: text.trim() ? 'pointer' : 'not-allowed',
fontWeight: 600,
}}
>
Add Document
</button>
</div>
</div>
</div>
);
}
export default function DocumentManager({ documents, onUpload, onPaste }: DocumentManagerProps) {
const fileInputRef = useRef<HTMLInputElement>(null);
const [pasteOpen, setPasteOpen] = useState(false);
function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
const files = e.target.files;
if (files && files.length > 0) {
onUpload(files);
}
e.target.value = '';
}
return (
<div style={{
padding: '1rem',
background: 'var(--bg-section)',
border: '1px solid var(--border)',
borderRadius: '8px',
marginBottom: '1.5rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
}}>
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: '0.6rem',
}}>
<h3 style={{
margin: 0,
fontSize: '0.85rem',
fontWeight: 600,
color: 'var(--text-secondary)',
textTransform: 'uppercase',
letterSpacing: '0.05em',
}}>
Documents
<span style={{
marginLeft: '0.5rem',
fontSize: '0.75rem',
fontWeight: 400,
color: 'var(--text-muted)',
}}>
({documents.length})
</span>
</h3>
<div style={{ display: 'flex', gap: '0.4rem' }}>
<button
onClick={() => fileInputRef.current?.click()}
style={{
padding: '0.3rem 0.7rem',
fontSize: '0.78rem',
background: 'var(--bg-card)',
color: '#4285F4',
border: '1px solid #4285F4',
borderRadius: '5px',
cursor: 'pointer',
fontFamily: 'system-ui, -apple-system, sans-serif',
fontWeight: 500,
}}
>
Upload
</button>
<button
onClick={() => setPasteOpen(true)}
style={{
padding: '0.3rem 0.7rem',
fontSize: '0.78rem',
background: 'var(--bg-card)',
color: '#34a853',
border: '1px solid #34a853',
borderRadius: '5px',
cursor: 'pointer',
fontFamily: 'system-ui, -apple-system, sans-serif',
fontWeight: 500,
}}
>
Paste
</button>
</div>
</div>
<input
ref={fileInputRef}
type="file"
accept=".md,.txt"
multiple
style={{ display: 'none' }}
onChange={handleFileChange}
/>
{documents.length === 0 ? (
<p style={{ fontSize: '0.82rem', color: 'var(--text-muted)', margin: 0 }}>
No documents loaded. Upload .md or .txt files, or paste text. They stay local to this browser session.
</p>
) : (
<div style={{ maxHeight: '180px', overflowY: 'auto' }}>
{documents.map(doc => (
<div key={doc.id} style={{
display: 'flex',
alignItems: 'center',
padding: '0.35rem 0.6rem',
background: 'var(--bg-card)',
border: '1px solid var(--border)',
borderRadius: '5px',
marginBottom: '0.3rem',
gap: '0.5rem',
}}>
<span style={{
fontSize: '0.75rem',
color: 'var(--text-muted)',
flexShrink: 0,
}}>
{'\u25AA'}
</span>
<span style={{
flex: 1,
fontSize: '0.8rem',
fontWeight: 500,
color: 'var(--text)',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}>
{doc.title}
</span>
<span style={{
fontFamily: "'SF Mono', 'Fira Code', 'Cascadia Code', monospace",
fontSize: '0.68rem',
color: 'var(--text-muted)',
flexShrink: 0,
}}>
{doc.filepath}
</span>
</div>
))}
</div>
)}
{pasteOpen && (
<PasteModal
onClose={() => setPasteOpen(false)}
onConfirm={onPaste}
/>
)}
</div>
);
}