| 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> |
| ); |
| } |
|
|