Add expandable full-screen editor modal to ResearchInspector note body

This commit is contained in:
2026-04-27 23:06:27 -04:00
parent 682b6509f4
commit d935ac80ef

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Pin, Archive, RefreshCw, Sparkles } from 'lucide-react'; import { createPortal } from 'react-dom';
import { Pin, Archive, RefreshCw, Sparkles, Maximize2, Minimize2 } from 'lucide-react';
import type { import type {
GhostNote, GhostNote,
NoteAuditTrail, NoteAuditTrail,
@@ -57,6 +58,8 @@ export const ResearchInspector: React.FC<ResearchInspectorProps> = ({
const [draftTitle, setDraftTitle] = useState(''); const [draftTitle, setDraftTitle] = useState('');
const [draftBody, setDraftBody] = useState(''); const [draftBody, setDraftBody] = useState('');
const [draftType, setDraftType] = useState<NoteType>('claim'); const [draftType, setDraftType] = useState<NoteType>('claim');
const [isEditorExpanded, setIsEditorExpanded] = useState(false);
const expandedEditorRef = useRef<HTMLTextAreaElement>(null);
useEffect(() => { useEffect(() => {
if (!note) { if (!note) {
@@ -68,6 +71,28 @@ export const ResearchInspector: React.FC<ResearchInspectorProps> = ({
setDraftType(note.noteType); setDraftType(note.noteType);
}, [note]); }, [note]);
useEffect(() => {
if (isEditorExpanded && expandedEditorRef.current) {
expandedEditorRef.current.focus();
expandedEditorRef.current.setSelectionRange(
expandedEditorRef.current.value.length,
expandedEditorRef.current.value.length,
);
}
}, [isEditorExpanded]);
const handleExpandKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (e.key === 'Escape') {
setIsEditorExpanded(false);
} else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
setIsEditorExpanded(false);
}
},
[],
);
if (!note && !ghost) { if (!note && !ghost) {
return ( return (
<div className="flex h-full flex-col justify-between p-4 sm:p-5"> <div className="flex h-full flex-col justify-between p-4 sm:p-5">
@@ -154,7 +179,17 @@ export const ResearchInspector: React.FC<ResearchInspectorProps> = ({
/> />
</label> </label>
<label className="flex flex-col gap-2"> <label className="flex flex-col gap-2">
<div className="flex items-center justify-between">
<span className="text-[10px] uppercase tracking-[0.18em] text-term-text-tertiary">Body</span> <span className="text-[10px] uppercase tracking-[0.18em] text-term-text-tertiary">Body</span>
<button
type="button"
onClick={() => setIsEditorExpanded(true)}
className="inline-flex items-center gap-1 rounded-lg border border-term-border bg-term-surface px-1.5 py-1 text-term-text-tertiary transition-colors hover:border-info/60 hover:text-term-text"
aria-label="Expand editor"
>
<Maximize2 className="h-3 w-3" />
</button>
</div>
<textarea <textarea
value={draftBody} value={draftBody}
onChange={(event) => setDraftBody(event.target.value)} onChange={(event) => setDraftBody(event.target.value)}
@@ -273,6 +308,60 @@ export const ResearchInspector: React.FC<ResearchInspectorProps> = ({
<p className="text-sm text-term-text-tertiary">Select a note to inspect its evidence chain.</p> <p className="text-sm text-term-text-tertiary">Select a note to inspect its evidence chain.</p>
)} )}
</div> </div>
{isEditorExpanded && note && createPortal(
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4"
onClick={() => setIsEditorExpanded(false)}
role="dialog"
aria-modal="true"
aria-label="Expanded note editor"
>
<div
className="flex max-h-[90vh] w-full max-w-4xl flex-col overflow-hidden rounded-[24px] border border-term-border bg-term-elevated shadow-2xl"
onClick={(e) => e.stopPropagation()}
onKeyDown={handleExpandKeyDown}
>
<div className="flex items-center justify-between border-b border-term-border bg-term-surface px-6 py-4">
<div>
<h2 className="text-lg font-mono font-semibold text-term-text">Edit Note</h2>
<p className="mt-1 text-xs text-term-text-muted">{draftTitle || 'Untitled'}</p>
</div>
<button
type="button"
onClick={() => setIsEditorExpanded(false)}
className="inline-flex items-center gap-1.5 rounded-xl border border-term-border bg-term-bg px-2.5 py-2 text-term-text-tertiary transition-colors hover:border-info/60 hover:text-term-text"
aria-label="Collapse editor"
>
<Minimize2 className="h-4 w-4" />
</button>
</div>
<div className="flex-1 overflow-y-auto p-6">
<textarea
ref={expandedEditorRef}
value={draftBody}
onChange={(e) => setDraftBody(e.target.value)}
className="min-h-[50vh] w-full resize-y rounded-2xl border border-term-border bg-term-bg px-4 py-3 text-sm leading-7 text-term-text outline-none transition-colors focus:border-info"
placeholder="Write your note..."
/>
</div>
<div className="flex items-center justify-between border-t border-term-border bg-term-surface px-6 py-4">
<p className="text-[10px] font-mono text-term-text-tertiary">
Press <kbd className="rounded border border-term-border bg-term-bg px-1 py-0.5">Esc</kbd> or{' '}
<kbd className="rounded border border-term-border bg-term-bg px-1 py-0.5"> Enter</kbd> to collapse
</p>
<button
type="button"
onClick={() => setIsEditorExpanded(false)}
className="rounded border border-info bg-info px-4 py-2 text-xs font-mono font-semibold text-term-bg transition-colors hover:brightness-110"
>
Done
</button>
</div>
</div>
</div>,
document.body,
)}
</div> </div>
); );
}; };