diff --git a/MosaicIQ/src/components/Research/ResearchInspectorPane.tsx b/MosaicIQ/src/components/Research/ResearchInspectorPane.tsx index 1811c54..2e9aefc 100644 --- a/MosaicIQ/src/components/Research/ResearchInspectorPane.tsx +++ b/MosaicIQ/src/components/Research/ResearchInspectorPane.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from 'react'; -import { Archive, Pin, RefreshCw, Sparkles } from 'lucide-react'; +import { createPortal } from 'react-dom'; +import { Archive, Maximize2, Minimize2, Pin, RefreshCw, Sparkles } from 'lucide-react'; import { researchBridge } from '../../lib/researchBridge'; import type { GhostNote, @@ -84,6 +85,7 @@ export const ResearchInspectorPane: React.FC = ({ const [draftBodyHtml, setDraftBodyHtml] = useState(''); const [draftEmbeds, setDraftEmbeds] = useState([]); const [draftType, setDraftType] = useState('claim'); + const [isEditorFullscreen, setIsEditorFullscreen] = useState(false); useEffect(() => { if (!note) { @@ -97,6 +99,15 @@ export const ResearchInspectorPane: React.FC = ({ setDraftType(note.noteType); }, [note]); + useEffect(() => { + if (!isEditorFullscreen) return; + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') setIsEditorFullscreen(false); + }; + document.addEventListener('keydown', handleKeyDown); + return () => document.removeEventListener('keydown', handleKeyDown); + }, [isEditorFullscreen]); + if (!note && !ghost) { return (
@@ -218,24 +229,36 @@ export const ResearchInspectorPane: React.FC = ({ />
) : null} + + {isEditorFullscreen && note + ? createPortal( +
setIsEditorFullscreen(false)} + role="dialog" + aria-modal="true" + aria-label="Expanded note editor" + > +
e.stopPropagation()} + > +
+
+

Edit Note

+

{draftTitle || 'Untitled'}

+
+ +
+
+ { + const result = await researchBridge.previewNoteLink({ url, kind: 'article' }); + return result.embed; + }} + onChange={(html, text, embeds) => { + setDraftBodyHtml(html); + setDraftBody(text); + setDraftEmbeds(embeds); + }} + /> +
+
+

+ Press Esc to collapse +

+ +
+
+
, + document.body, + ) + : null} ); }; diff --git a/MosaicIQ/src/components/Research/primitives/ResearchEditor.tsx b/MosaicIQ/src/components/Research/primitives/ResearchEditor.tsx index 607077f..def4c5b 100644 --- a/MosaicIQ/src/components/Research/primitives/ResearchEditor.tsx +++ b/MosaicIQ/src/components/Research/primitives/ResearchEditor.tsx @@ -114,14 +114,16 @@ const EmbeddedLinkCard = Node.create({ excerpt ? ['div', { class: 'mt-1 text-xs leading-5 text-term-text-muted' }, excerpt] : ['div', { class: 'hidden' }, ''], - [ - 'a', - { - href: HTMLAttributes.url, - class: 'mt-2 block truncate text-xs text-info underline decoration-info/40 underline-offset-2', - }, - HTMLAttributes.url, - ], + [ + 'a', + { + href: HTMLAttributes.url, + target: '_blank', + rel: 'noopener noreferrer', + class: 'mt-2 block truncate text-xs text-info underline decoration-info/40 underline-offset-2', + }, + HTMLAttributes.url, + ], ]; if (imageUrl) { @@ -187,7 +189,7 @@ export const ResearchEditor: React.FC = ({ Link.configure({ openOnClick: false, autolink: true, - HTMLAttributes: { class: 'text-info underline decoration-info/40 underline-offset-2' }, + HTMLAttributes: { class: 'text-info underline decoration-info/40 underline-offset-2', target: '_blank', rel: 'noopener noreferrer' }, }), EmbeddedLinkCard, ], diff --git a/MosaicIQ/src/components/Research/views/EvidenceTracePanel.tsx b/MosaicIQ/src/components/Research/views/EvidenceTracePanel.tsx index 85eda46..9e54716 100644 --- a/MosaicIQ/src/components/Research/views/EvidenceTracePanel.tsx +++ b/MosaicIQ/src/components/Research/views/EvidenceTracePanel.tsx @@ -51,6 +51,8 @@ export const EvidenceTracePanel: React.FC<{ auditTrail: NoteAuditTrail | null }> {source.url ? (
{source.url}