feat: port source-first ingestion to os repo
- make source the canonical field across os routes, tools, ui, and mcp - align fresh-install schema, search, and embedding flows with source-first ingestion Generated with Claude Code
This commit is contained in:
+132
-139
@@ -9,10 +9,10 @@ import { parseNodeMarkers } from '@/tools/infrastructure/nodeFormatter';
|
||||
import { Node, NodeConnection, Chunk } from '@/types/database';
|
||||
import DimensionTags from './dimensions/DimensionTags';
|
||||
import { getNodeIcon } from '@/utils/nodeIcons';
|
||||
import { openExternalUrl, shouldOpenExternally } from '@/utils/openExternalUrl';
|
||||
import { useDimensionIcons } from '@/context/DimensionIconsContext';
|
||||
import ConfirmDialog from '../common/ConfirmDialog';
|
||||
import { SourceReader } from './source';
|
||||
import { openExternalUrl, shouldOpenExternally } from '@/utils/openExternalUrl';
|
||||
|
||||
|
||||
interface NodeSearchResult {
|
||||
@@ -36,8 +36,8 @@ interface FocusPanelProps {
|
||||
}
|
||||
|
||||
export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeClick, onTabClose, refreshTrigger, onOpenInOtherSlot, hideTabBar, onTextSelect, highlightedPassage }: FocusPanelProps) {
|
||||
const { dimensionIcons } = useDimensionIcons();
|
||||
const [nodesData, setNodesData] = useState<Record<number, Node>>({});
|
||||
const { dimensionIcons } = useDimensionIcons();
|
||||
|
||||
// Context menu state
|
||||
const [contextMenu, setContextMenu] = useState<{ x: number; y: number; tabId: number } | null>(null);
|
||||
@@ -97,10 +97,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
const [chunkExpanded, setChunkExpanded] = useState<{ [key: number]: boolean }>({});
|
||||
|
||||
// Edges expand/collapse state
|
||||
// edgesExpanded removed — all connections shown in Edges tab
|
||||
|
||||
// Connections section collapsed state (default closed)
|
||||
const [edgeSearchOpen, setEdgeSearchOpen] = useState(false);
|
||||
const [edgesExpanded, setEdgesExpanded] = useState<{ [key: number]: boolean }>({});
|
||||
|
||||
// Title expanded state for click-to-expand full title
|
||||
const [titleExpanded, setTitleExpanded] = useState<{ [key: number]: boolean }>({});
|
||||
@@ -108,7 +105,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
// Description regeneration state
|
||||
const [regeneratingDescription, setRegeneratingDescription] = useState<number | null>(null);
|
||||
|
||||
// Content tab state: 'notes', 'desc', or 'source'
|
||||
// Content tab state: 'desc', 'notes', 'edges', or 'source'
|
||||
const [activeContentTab, setActiveContentTab] = useState<'notes' | 'desc' | 'edges' | 'source'>('desc');
|
||||
|
||||
// Desc (description) edit mode state
|
||||
@@ -135,6 +132,9 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
const [loadingChunks, setLoadingChunks] = useState<Set<number>>(new Set());
|
||||
const [chunksExpanded, setChunksExpanded] = useState<Record<number, boolean>>({});
|
||||
|
||||
// Edge creation search toggle
|
||||
const [edgeSearchOpen, setEdgeSearchOpen] = useState(false);
|
||||
|
||||
// Helper: preview edge type based on heuristics (mirrors backend logic)
|
||||
const previewEdgeType = (explanation: string): { type: string; label: string } | null => {
|
||||
const norm = (explanation || '').trim().toLowerCase();
|
||||
@@ -230,13 +230,16 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
setEditingField(null);
|
||||
setEditingValue('');
|
||||
setEditingNodeId(null);
|
||||
// Also clear notes/desc/source edit modes
|
||||
// Also clear notes/desc/source/edges edit modes
|
||||
setNotesEditMode(false);
|
||||
setNotesEditValue('');
|
||||
setDescEditMode(false);
|
||||
setDescEditValue('');
|
||||
setSourceEditMode(false);
|
||||
setSourceEditValue('');
|
||||
setEdgeSearchOpen(false);
|
||||
setNodeSearchQuery('');
|
||||
setNodeSearchSuggestions([]);
|
||||
}, [activeTab]);
|
||||
|
||||
const fetchNodeData = async (id: number) => {
|
||||
@@ -356,7 +359,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
const updateData: Record<string, string> = {};
|
||||
|
||||
if (editingField === 'notes') {
|
||||
updateData.content = editingValue;
|
||||
updateData.notes = editingValue;
|
||||
} else {
|
||||
updateData[editingField] = editingValue;
|
||||
}
|
||||
@@ -378,7 +381,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
setNodesData(prev => ({ ...prev, [nodeId]: result.node }));
|
||||
}
|
||||
|
||||
// Safety net: ensure edges exist for any tokens present in saved content
|
||||
// Safety net: ensure edges exist for any tokens present in saved notes
|
||||
if (editingField === 'notes' && typeof editingValue === 'string') {
|
||||
try {
|
||||
const tokens = parseNodeMarkers(editingValue);
|
||||
@@ -421,7 +424,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
const response = await fetch(`/api/nodes/${nodeId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ notes: contentValue }),
|
||||
body: JSON.stringify({ content: contentValue }),
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to save');
|
||||
const result = await response.json();
|
||||
@@ -497,16 +500,16 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
setDescEditMode(true);
|
||||
};
|
||||
|
||||
// Sync description to source (chunk) and re-embed
|
||||
// Sync description to source and re-embed
|
||||
const syncDescToSource = async () => {
|
||||
if (!activeTab) return;
|
||||
setDescSaving(true);
|
||||
try {
|
||||
// Save description to chunk field
|
||||
// Save description to source field
|
||||
const response = await fetch(`/api/nodes/${activeTab}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ chunk: descEditValue }),
|
||||
body: JSON.stringify({ source: descEditValue }),
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to sync');
|
||||
const result = await response.json();
|
||||
@@ -533,7 +536,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
}
|
||||
};
|
||||
|
||||
// Save notes (content) with explicit Save button
|
||||
// Save notes-tab content into canonical source
|
||||
const saveNotes = async () => {
|
||||
if (!activeTab) return;
|
||||
setNotesSaving(true);
|
||||
@@ -541,7 +544,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
const response = await fetch(`/api/nodes/${activeTab}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ notes: notesEditValue }),
|
||||
body: JSON.stringify({ source: notesEditValue }),
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to save');
|
||||
const result = await response.json();
|
||||
@@ -571,8 +574,8 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
setNotesEditMode(false);
|
||||
setNotesEditValue('');
|
||||
} catch (e) {
|
||||
console.error('Error saving notes:', e);
|
||||
alert('Failed to save notes. Please try again.');
|
||||
console.error('Error saving source content:', e);
|
||||
alert('Failed to save content. Please try again.');
|
||||
} finally {
|
||||
setNotesSaving(false);
|
||||
}
|
||||
@@ -587,11 +590,11 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
// Start editing notes
|
||||
const startNotesEdit = () => {
|
||||
if (!activeTab || !nodesData[activeTab]) return;
|
||||
setNotesEditValue(nodesData[activeTab].notes || '');
|
||||
setNotesEditValue(nodesData[activeTab].source || nodesData[activeTab].notes || '');
|
||||
setNotesEditMode(true);
|
||||
};
|
||||
|
||||
// Save source (chunk) with explicit Save button
|
||||
// Save source with explicit Save button
|
||||
const saveSource = async () => {
|
||||
if (!activeTab) return;
|
||||
setSourceSaving(true);
|
||||
@@ -599,7 +602,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
const response = await fetch(`/api/nodes/${activeTab}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ chunk: sourceEditValue }),
|
||||
body: JSON.stringify({ source: sourceEditValue }),
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to save');
|
||||
const result = await response.json();
|
||||
@@ -625,7 +628,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
// Start editing source
|
||||
const startSourceEdit = () => {
|
||||
if (!activeTab || !nodesData[activeTab]) return;
|
||||
setSourceEditValue(nodesData[activeTab].chunk || '');
|
||||
setSourceEditValue(nodesData[activeTab].source || nodesData[activeTab].chunk || '');
|
||||
setSourceEditMode(true);
|
||||
};
|
||||
|
||||
@@ -641,11 +644,11 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
setSyncing(true);
|
||||
setShowSyncConfirm(false);
|
||||
try {
|
||||
// First, save notes content to chunk field
|
||||
// First, save notes content to source field
|
||||
const response = await fetch(`/api/nodes/${activeTab}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ chunk: notesEditValue }),
|
||||
body: JSON.stringify({ source: notesEditValue }),
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to sync');
|
||||
const result = await response.json();
|
||||
@@ -997,14 +1000,14 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
const embedContent = async (nodeId: number) => {
|
||||
const node = nodesData[nodeId];
|
||||
const hasNotes = node?.notes?.trim();
|
||||
const hasChunk = node?.chunk?.trim();
|
||||
// If chunk is empty but content exists, auto-populate chunk from content
|
||||
if (!hasChunk && hasNotes) {
|
||||
const hasSource = (node?.source || node?.chunk)?.trim();
|
||||
// If source is empty but notes exist, auto-populate source from notes
|
||||
if (!hasSource && hasNotes) {
|
||||
try {
|
||||
const response = await fetch(`/api/nodes/${nodeId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ chunk: hasNotes })
|
||||
body: JSON.stringify({ source: hasNotes })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
@@ -1014,13 +1017,13 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to auto-populate chunk for embedding:', error);
|
||||
console.error('Failed to auto-populate source for embedding:', error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If neither content nor chunk exist, require content
|
||||
if (!hasNotes && !hasChunk) {
|
||||
startEdit('content', '');
|
||||
// If neither notes nor source exist, require notes
|
||||
if (!hasNotes && !hasSource) {
|
||||
startEdit('notes', '');
|
||||
return;
|
||||
}
|
||||
setEmbeddingNode(nodeId);
|
||||
@@ -1119,7 +1122,6 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
setAddingEdge(null);
|
||||
setEdgeExplanation('');
|
||||
setPendingEdgeTarget(null);
|
||||
setEdgeSearchOpen(false);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error creating edge:', error);
|
||||
@@ -1157,7 +1159,6 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
setPendingEdgeTarget(null);
|
||||
setNodeSearchQuery('');
|
||||
setNodeSearchSuggestions([]);
|
||||
setEdgeSearchOpen(false);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error creating edge:', error);
|
||||
@@ -1277,63 +1278,55 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
borderRadius: '8px',
|
||||
maxHeight: '200px',
|
||||
overflowY: 'auto',
|
||||
zIndex: 10,
|
||||
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.4)'
|
||||
zIndex: 100,
|
||||
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.5)',
|
||||
}}>
|
||||
{nodeSearchSuggestions.map((suggestion, index) => (
|
||||
<div
|
||||
key={suggestion.id}
|
||||
onClick={() => handleSelectNodeSuggestion(suggestion)}
|
||||
onClick={() => {
|
||||
handleSelectNodeSuggestion(suggestion);
|
||||
setNodeSearchQuery('');
|
||||
setNodeSearchSuggestions([]);
|
||||
setEdgeSearchOpen(false);
|
||||
}}
|
||||
style={{
|
||||
padding: '10px 14px',
|
||||
padding: '8px 12px',
|
||||
cursor: 'pointer',
|
||||
borderBottom: index < nodeSearchSuggestions.length - 1 ? '1px solid var(--rah-bg-active)' : 'none',
|
||||
borderBottom: index < nodeSearchSuggestions.length - 1 ? '1px solid var(--rah-border)' : 'none',
|
||||
background: index === selectedSearchIndex ? 'var(--rah-bg-active)' : 'transparent',
|
||||
transition: 'background 100ms ease',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '10px'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (index !== selectedSearchIndex) {
|
||||
e.currentTarget.style.background = 'var(--rah-bg-active)';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (index !== selectedSearchIndex) {
|
||||
e.currentTarget.style.background = 'transparent';
|
||||
}
|
||||
gap: '8px',
|
||||
}}
|
||||
onMouseEnter={(e) => { e.currentTarget.style.background = 'var(--rah-bg-active)'; }}
|
||||
onMouseLeave={(e) => { if (index !== selectedSearchIndex) e.currentTarget.style.background = 'transparent'; }}
|
||||
>
|
||||
<span style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: '9px',
|
||||
fontWeight: 600,
|
||||
color: 'var(--rah-bg-base)',
|
||||
background: '#22c55e',
|
||||
padding: '3px 6px',
|
||||
borderRadius: '6px',
|
||||
minWidth: '24px',
|
||||
textAlign: 'center',
|
||||
color: 'var(--rah-text-inverse)',
|
||||
background: 'var(--rah-accent-green)',
|
||||
padding: '2px 5px',
|
||||
borderRadius: '4px',
|
||||
flexShrink: 0,
|
||||
fontFamily: "'SF Mono', 'Fira Code', monospace"
|
||||
fontFamily: "'SF Mono', 'Fira Code', monospace",
|
||||
}}>
|
||||
{suggestion.id}
|
||||
</span>
|
||||
<span style={{
|
||||
fontSize: '13px',
|
||||
fontSize: '12px',
|
||||
color: 'var(--rah-text-base)',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
flex: 1
|
||||
flex: 1,
|
||||
}}>
|
||||
{suggestion.title}
|
||||
</span>
|
||||
{index === selectedSearchIndex && (
|
||||
<span style={{ color: 'var(--rah-text-muted)', fontSize: '13px' }}>↵</span>
|
||||
<span style={{ color: 'var(--rah-text-muted)', fontSize: '12px' }}>↵</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
@@ -1342,10 +1335,10 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Connections List */}
|
||||
{/* Connections list */}
|
||||
<div style={{ flex: 1, overflowY: 'auto' }}>
|
||||
{loadingEdges.has(activeTab) ? (
|
||||
<div style={{ color: 'var(--rah-text-muted)', fontSize: '12px', fontStyle: 'italic', padding: '16px 0', textAlign: 'center' }}>Loading connections…</div>
|
||||
<div style={{ color: 'var(--rah-text-muted)', fontSize: '12px', fontStyle: 'italic', padding: '8px 0' }}>Loading connections…</div>
|
||||
) : (() => {
|
||||
const list = edgesData[activeTab] || [];
|
||||
if (list.length === 0) {
|
||||
@@ -1358,18 +1351,18 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
key={connection.id}
|
||||
style={{
|
||||
padding: '6px 0',
|
||||
borderBottom: '1px solid var(--rah-bg-panel)',
|
||||
borderBottom: '1px solid var(--rah-border)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '2px',
|
||||
minWidth: 0,
|
||||
}}
|
||||
>
|
||||
{/* Row 1: arrow + ID + icon + title + delete */}
|
||||
{/* Row 1: arrow + ID + title + delete */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', minWidth: 0 }}>
|
||||
<span style={{
|
||||
fontSize: '11px',
|
||||
color: connection.edge.from_node_id === activeTab ? '#22c55e' : '#f59e0b',
|
||||
color: connection.edge.from_node_id === activeTab ? 'var(--rah-accent-green)' : '#f59e0b',
|
||||
fontWeight: 600,
|
||||
width: '14px',
|
||||
textAlign: 'center',
|
||||
@@ -1380,8 +1373,8 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
<span style={{
|
||||
fontSize: '9px',
|
||||
fontWeight: 700,
|
||||
color: 'var(--rah-bg-base)',
|
||||
background: '#22c55e',
|
||||
color: 'var(--rah-text-inverse)',
|
||||
background: 'var(--rah-accent-green)',
|
||||
padding: '1px 5px',
|
||||
borderRadius: '4px',
|
||||
flexShrink: 0,
|
||||
@@ -1405,7 +1398,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
minWidth: 0,
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onMouseEnter={(e) => { e.currentTarget.style.color = '#22c55e'; }}
|
||||
onMouseEnter={(e) => { e.currentTarget.style.color = 'var(--rah-accent-green)'; }}
|
||||
onMouseLeave={(e) => { e.currentTarget.style.color = 'var(--rah-text-base)'; }}
|
||||
>
|
||||
{connection.connected_node.title}
|
||||
@@ -1414,7 +1407,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
onClick={() => deleteEdge(connection.edge.id)}
|
||||
disabled={deletingEdge === connection.edge.id}
|
||||
style={{
|
||||
color: 'var(--rah-border-stronger)',
|
||||
color: 'var(--rah-text-muted)',
|
||||
fontSize: '14px',
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
@@ -1424,7 +1417,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
lineHeight: 1,
|
||||
}}
|
||||
onMouseEnter={(e) => { if (deletingEdge !== connection.edge.id) e.currentTarget.style.color = '#dc2626'; }}
|
||||
onMouseLeave={(e) => { if (deletingEdge !== connection.edge.id) e.currentTarget.style.color = 'var(--rah-border-stronger)'; }}
|
||||
onMouseLeave={(e) => { if (deletingEdge !== connection.edge.id) e.currentTarget.style.color = 'var(--rah-text-muted)'; }}
|
||||
>
|
||||
{deletingEdge === connection.edge.id ? '...' : '×'}
|
||||
</button>
|
||||
@@ -1464,8 +1457,8 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
disabled={edgeSavingId === connection.edge.id}
|
||||
style={{
|
||||
fontSize: '10px',
|
||||
color: '#000',
|
||||
background: '#22c55e',
|
||||
color: 'var(--rah-text-inverse)',
|
||||
background: 'var(--rah-accent-green)',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
padding: '3px 8px',
|
||||
@@ -1508,9 +1501,9 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
{typeof connection.edge.context?.type === 'string' && (
|
||||
<span style={{
|
||||
fontSize: '9px',
|
||||
color: 'var(--rah-text-muted)',
|
||||
color: 'var(--rah-text-soft)',
|
||||
background: 'var(--rah-bg-active)',
|
||||
border: '1px solid var(--rah-bg-active)',
|
||||
border: '1px solid var(--rah-border)',
|
||||
padding: '1px 5px',
|
||||
borderRadius: '999px',
|
||||
flexShrink: 0,
|
||||
@@ -1522,7 +1515,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
<span
|
||||
style={{
|
||||
fontSize: '11px',
|
||||
color: connection.edge.context?.explanation ? '#777' : '#444',
|
||||
color: connection.edge.context?.explanation ? 'var(--rah-text-muted)' : 'var(--rah-text-soft)',
|
||||
fontStyle: connection.edge.context?.explanation ? 'normal' : 'italic',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
@@ -1594,7 +1587,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
borderBottom: '1px solid var(--rah-border)',
|
||||
background: 'var(--rah-bg-surface)',
|
||||
background: 'var(--rah-bg-base)',
|
||||
flexShrink: 0,
|
||||
overflowX: 'auto',
|
||||
overflowY: 'hidden'
|
||||
@@ -1631,7 +1624,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
borderRight: '1px solid var(--rah-border)',
|
||||
background: isActive ? 'var(--rah-bg-subtle)' : 'var(--rah-bg-surface)',
|
||||
background: isActive ? 'var(--rah-bg-subtle)' : 'var(--rah-bg-base)',
|
||||
borderBottom: isActive ? '2px solid var(--rah-text-muted)' : 'none',
|
||||
paddingBottom: isActive ? '0' : '2px',
|
||||
minWidth: '120px',
|
||||
@@ -1646,7 +1639,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
padding: '8px 12px',
|
||||
fontSize: '12px',
|
||||
fontFamily: 'inherit',
|
||||
color: isActive ? 'var(--rah-text-active)' : 'var(--rah-text-muted)',
|
||||
color: isActive ? 'var(--rah-text-active)' : 'var(--rah-text-soft)',
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
@@ -1708,7 +1701,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
Loading...
|
||||
</div>
|
||||
) : !currentNode ? (
|
||||
<div style={{ color: 'var(var(--rah-text-muted))', fontSize: '13px' }}>Node not found.</div>
|
||||
<div style={{ color: 'var(--rah-text-muted)', fontSize: '13px' }}>Node not found.</div>
|
||||
) : nodesData[activeTab] ? (
|
||||
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||
{/* URL Row - Above Title */}
|
||||
@@ -1791,8 +1784,8 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
return;
|
||||
}
|
||||
|
||||
const link = nodesData[activeTab].link;
|
||||
if (!link || !shouldOpenExternally(link)) {
|
||||
const link = nodesData[activeTab].link || '';
|
||||
if (!shouldOpenExternally(link)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1819,7 +1812,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
<span
|
||||
onClick={() => startEdit('link', '')}
|
||||
style={{
|
||||
color: 'var(--rah-text-muted)',
|
||||
color: '#555',
|
||||
fontSize: '11px',
|
||||
cursor: 'pointer',
|
||||
fontStyle: 'italic'
|
||||
@@ -1850,8 +1843,8 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
}}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
background: '#22c55e',
|
||||
color: 'var(--rah-bg-base)',
|
||||
background: 'var(--rah-accent-green)',
|
||||
color: 'var(--rah-text-inverse)',
|
||||
fontSize: '10px',
|
||||
fontWeight: 600,
|
||||
padding: '2px 6px',
|
||||
@@ -1864,7 +1857,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
{activeTab}
|
||||
</span>
|
||||
|
||||
{/* Node type icon */}
|
||||
{/* Node icon */}
|
||||
{nodesData[activeTab] && (
|
||||
<span style={{ flexShrink: 0, display: 'flex', alignItems: 'center' }}>
|
||||
{getNodeIcon(nodesData[activeTab], dimensionIcons, 18)}
|
||||
@@ -1938,7 +1931,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
title={titleExpanded[activeTab] ? undefined : (nodesData[activeTab].title || 'Untitled')}
|
||||
>
|
||||
{nodesData[activeTab].title || 'Untitled'}
|
||||
{savingField === 'title' && <span style={{ color: 'var(--rah-text-muted)', fontSize: '10px', marginLeft: '6px' }}>saving...</span>}
|
||||
{savingField === 'title' && <span style={{ color: '#555', fontSize: '10px', marginLeft: '6px' }}>saving...</span>}
|
||||
</h1>
|
||||
)}
|
||||
|
||||
@@ -1948,14 +1941,14 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
gap: '6px',
|
||||
padding: '4px 8px',
|
||||
fontSize: '10px',
|
||||
fontWeight: 500,
|
||||
color: activeContentTab === 'edges' ? '#22c55e' : 'var(--rah-text-muted)',
|
||||
color: activeContentTab === 'edges' ? 'var(--rah-accent-green)' : 'var(--rah-text-muted)',
|
||||
background: activeContentTab === 'edges' ? '#0f2417' : 'transparent',
|
||||
border: '1px solid',
|
||||
borderColor: activeContentTab === 'edges' ? '#22c55e' : 'var(--rah-border-strong)',
|
||||
borderColor: activeContentTab === 'edges' ? 'var(--rah-accent-green)' : 'var(--rah-border-strong)',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s',
|
||||
@@ -1963,7 +1956,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (activeContentTab !== 'edges') {
|
||||
e.currentTarget.style.color = '#22c55e';
|
||||
e.currentTarget.style.color = 'var(--rah-accent-green)';
|
||||
e.currentTarget.style.borderColor = '#166534';
|
||||
}
|
||||
}}
|
||||
@@ -1983,18 +1976,18 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
style={{
|
||||
minWidth: '18px',
|
||||
height: '18px',
|
||||
padding: '0 6px',
|
||||
borderRadius: '999px',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: '0 6px',
|
||||
background: activeContentTab === 'edges' ? 'rgba(34, 197, 94, 0.18)' : 'var(--rah-bg-active)',
|
||||
color: activeContentTab === 'edges' ? '#a7f3b8' : 'var(--rah-text-muted)',
|
||||
background: activeContentTab === 'edges' ? 'var(--rah-accent-green-soft)' : 'var(--rah-bg-active)',
|
||||
color: activeContentTab === 'edges' ? 'var(--rah-accent-green)' : 'var(--rah-text-soft)',
|
||||
fontSize: '10px',
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
{(edgesData[activeTab] || []).length}
|
||||
{edgesData[activeTab]?.length ?? 0}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
@@ -2084,7 +2077,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
borderBottom: '1px solid var(--rah-border)'
|
||||
}}>
|
||||
<button
|
||||
onClick={() => { setActiveContentTab('desc'); setNotesEditMode(false); setSourceEditMode(false); }}
|
||||
onClick={() => { setActiveContentTab('desc'); setNotesEditMode(false); setSourceEditMode(false); setEdgeSearchOpen(false); }}
|
||||
style={{
|
||||
padding: '8px 16px',
|
||||
fontSize: '11px',
|
||||
@@ -2092,7 +2085,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
color: activeContentTab === 'desc' ? 'var(--rah-text-base)' : 'var(--rah-text-muted)',
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
borderBottom: activeContentTab === 'desc' ? '2px solid #22c55e' : '2px solid transparent',
|
||||
borderBottom: activeContentTab === 'desc' ? '2px solid var(--rah-accent-green)' : '2px solid transparent',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.15s',
|
||||
marginBottom: '-1px'
|
||||
@@ -2101,7 +2094,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
Desc
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setActiveContentTab('notes'); setDescEditMode(false); setSourceEditMode(false); }}
|
||||
onClick={() => { setActiveContentTab('notes'); setDescEditMode(false); setSourceEditMode(false); setEdgeSearchOpen(false); }}
|
||||
style={{
|
||||
padding: '8px 16px',
|
||||
fontSize: '11px',
|
||||
@@ -2109,7 +2102,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
color: activeContentTab === 'notes' ? 'var(--rah-text-base)' : 'var(--rah-text-muted)',
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
borderBottom: activeContentTab === 'notes' ? '2px solid #22c55e' : '2px solid transparent',
|
||||
borderBottom: activeContentTab === 'notes' ? '2px solid var(--rah-accent-green)' : '2px solid transparent',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.15s',
|
||||
marginBottom: '-1px'
|
||||
@@ -2126,13 +2119,13 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
color: activeContentTab === 'edges' ? 'var(--rah-text-base)' : 'var(--rah-text-muted)',
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
borderBottom: activeContentTab === 'edges' ? '2px solid #22c55e' : '2px solid transparent',
|
||||
borderBottom: activeContentTab === 'edges' ? '2px solid var(--rah-accent-green)' : '2px solid transparent',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.15s',
|
||||
marginBottom: '-1px'
|
||||
}}
|
||||
>
|
||||
Edges{activeTab && edgesData[activeTab]?.length ? ` (${edgesData[activeTab].length})` : ''}
|
||||
Edges
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setActiveContentTab('source'); setDescEditMode(false); setNotesEditMode(false); setEdgeSearchOpen(false); }}
|
||||
@@ -2143,7 +2136,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
color: activeContentTab === 'source' ? 'var(--rah-text-base)' : 'var(--rah-text-muted)',
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
borderBottom: activeContentTab === 'source' ? '2px solid #22c55e' : '2px solid transparent',
|
||||
borderBottom: activeContentTab === 'source' ? '2px solid var(--rah-accent-green)' : '2px solid transparent',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.15s',
|
||||
marginBottom: '-1px'
|
||||
@@ -2164,7 +2157,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
gap: '4px',
|
||||
padding: '4px 8px',
|
||||
fontSize: '10px',
|
||||
color: 'var(--rah-text-muted)',
|
||||
color: 'var(--rah-text-soft)',
|
||||
background: 'transparent',
|
||||
border: '1px solid var(--rah-border-strong)',
|
||||
borderRadius: '4px',
|
||||
@@ -2183,7 +2176,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
gap: '4px',
|
||||
padding: '4px 8px',
|
||||
fontSize: '10px',
|
||||
color: 'var(--rah-text-muted)',
|
||||
color: 'var(--rah-text-soft)',
|
||||
background: 'transparent',
|
||||
border: '1px solid var(--rah-border-strong)',
|
||||
borderRadius: '4px',
|
||||
@@ -2208,7 +2201,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
gap: '4px',
|
||||
padding: '4px 8px',
|
||||
fontSize: '10px',
|
||||
color: '#22c55e',
|
||||
color: 'var(--rah-accent-green)',
|
||||
background: 'transparent',
|
||||
border: '1px solid #166534',
|
||||
borderRadius: '4px',
|
||||
@@ -2227,7 +2220,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
gap: '4px',
|
||||
padding: '4px 8px',
|
||||
fontSize: '10px',
|
||||
color: 'var(--rah-text-muted)',
|
||||
color: 'var(--rah-text-soft)',
|
||||
background: 'transparent',
|
||||
border: '1px solid var(--rah-border-strong)',
|
||||
borderRadius: '4px',
|
||||
@@ -2268,8 +2261,8 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
gap: '4px',
|
||||
padding: '4px 8px',
|
||||
fontSize: '10px',
|
||||
color: edgeSearchOpen ? '#000' : '#22c55e',
|
||||
background: edgeSearchOpen ? '#22c55e' : 'transparent',
|
||||
color: edgeSearchOpen ? 'var(--rah-text-inverse)' : 'var(--rah-accent-green)',
|
||||
background: edgeSearchOpen ? 'var(--rah-accent-green)' : 'transparent',
|
||||
border: '1px solid #166534',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
@@ -2383,7 +2376,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
gap: '4px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '11px',
|
||||
color: 'var(--rah-text-muted)',
|
||||
color: 'var(--rah-text-soft)',
|
||||
background: 'transparent',
|
||||
border: '1px solid var(--rah-border-strong)',
|
||||
borderRadius: '4px',
|
||||
@@ -2402,8 +2395,8 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
gap: '4px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '11px',
|
||||
color: '#000',
|
||||
background: '#22c55e',
|
||||
color: 'var(--rah-text-inverse)',
|
||||
background: 'var(--rah-accent-green)',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
@@ -2433,7 +2426,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
<div
|
||||
onClick={startDescEdit}
|
||||
style={{
|
||||
color: 'var(--rah-text-muted)',
|
||||
color: '#555',
|
||||
fontSize: '12px',
|
||||
fontStyle: 'italic',
|
||||
cursor: 'pointer',
|
||||
@@ -2536,7 +2529,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
top: '50px',
|
||||
left: '12px',
|
||||
background: 'var(--rah-bg-active)',
|
||||
border: '1px solid var(--rah-border-stronger)',
|
||||
border: '1px solid var(--rah-border-strong)',
|
||||
borderRadius: '6px',
|
||||
zIndex: 1000,
|
||||
maxHeight: '200px',
|
||||
@@ -2566,11 +2559,11 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
fontSize: '13px',
|
||||
color: 'var(--rah-text-base)',
|
||||
cursor: 'pointer',
|
||||
background: idx === mentionIndex ? 'var(--rah-bg-hover)' : 'transparent',
|
||||
background: idx === mentionIndex ? 'var(--rah-bg-elevated)' : 'transparent',
|
||||
borderBottom: idx < mentionResults.length - 1 ? '1px solid var(--rah-border-strong)' : 'none'
|
||||
}}
|
||||
>
|
||||
<span style={{ color: '#22c55e', marginRight: '8px', fontWeight: 600 }}>{n.id}</span>
|
||||
<span style={{ color: 'var(--rah-accent-green)', marginRight: '8px', fontWeight: 600 }}>{n.id}</span>
|
||||
<span>{n.title.length > 50 ? n.title.slice(0, 50) + '…' : n.title}</span>
|
||||
</div>
|
||||
))
|
||||
@@ -2621,7 +2614,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
gap: '4px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '11px',
|
||||
color: 'var(--rah-text-muted)',
|
||||
color: 'var(--rah-text-soft)',
|
||||
background: 'transparent',
|
||||
border: '1px solid var(--rah-border-strong)',
|
||||
borderRadius: '4px',
|
||||
@@ -2640,8 +2633,8 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
gap: '4px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '11px',
|
||||
color: '#000',
|
||||
background: '#22c55e',
|
||||
color: 'var(--rah-text-inverse)',
|
||||
background: 'var(--rah-accent-green)',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
@@ -2671,7 +2664,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
style={{
|
||||
padding: '4px 10px',
|
||||
fontSize: '11px',
|
||||
color: 'var(--rah-text-muted)',
|
||||
color: 'var(--rah-text-soft)',
|
||||
background: 'transparent',
|
||||
border: '1px solid var(--rah-border-strong)',
|
||||
borderRadius: '4px',
|
||||
@@ -2685,7 +2678,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
style={{
|
||||
padding: '4px 10px',
|
||||
fontSize: '11px',
|
||||
color: '#000',
|
||||
color: 'var(--rah-text-inverse)',
|
||||
background: '#f59e0b',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
@@ -2711,7 +2704,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
}}
|
||||
>
|
||||
<MarkdownWithNodeTokens
|
||||
content={nodesData[activeTab].notes || ''}
|
||||
content={nodesData[activeTab].notes}
|
||||
onNodeClick={onNodeClick || onTabSelect}
|
||||
/>
|
||||
</div>
|
||||
@@ -2719,7 +2712,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
<div
|
||||
onClick={startNotesEdit}
|
||||
style={{
|
||||
color: 'var(--rah-text-muted)',
|
||||
color: '#555',
|
||||
fontSize: '12px',
|
||||
fontStyle: 'italic',
|
||||
cursor: 'pointer',
|
||||
@@ -2814,7 +2807,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
gap: '4px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '11px',
|
||||
color: 'var(--rah-text-muted)',
|
||||
color: 'var(--rah-text-soft)',
|
||||
background: 'transparent',
|
||||
border: '1px solid var(--rah-border-strong)',
|
||||
borderRadius: '4px',
|
||||
@@ -2833,8 +2826,8 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
gap: '4px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '11px',
|
||||
color: '#000',
|
||||
background: '#22c55e',
|
||||
color: 'var(--rah-text-inverse)',
|
||||
background: 'var(--rah-accent-green)',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
@@ -2861,7 +2854,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
display: 'flex',
|
||||
background: 'var(--rah-bg-surface)',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid var(--rah-bg-active)',
|
||||
border: '1px solid var(--rah-border)',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<button
|
||||
@@ -2871,7 +2864,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
fontSize: '10px',
|
||||
fontWeight: 500,
|
||||
color: sourceReaderMode === 'raw' ? 'var(--rah-text-active)' : 'var(--rah-text-muted)',
|
||||
background: sourceReaderMode === 'raw' ? 'var(--rah-border-strong)' : 'transparent',
|
||||
background: sourceReaderMode === 'raw' ? 'var(--rah-bg-active)' : 'transparent',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.15s ease',
|
||||
@@ -2886,7 +2879,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
fontSize: '10px',
|
||||
fontWeight: 500,
|
||||
color: sourceReaderMode === 'reader' ? 'var(--rah-text-active)' : 'var(--rah-text-muted)',
|
||||
background: sourceReaderMode === 'reader' ? 'var(--rah-border-strong)' : 'transparent',
|
||||
background: sourceReaderMode === 'reader' ? 'var(--rah-bg-active)' : 'transparent',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.15s ease',
|
||||
@@ -2907,17 +2900,17 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
fontSize: '10px',
|
||||
color: 'var(--rah-text-muted)',
|
||||
background: 'transparent',
|
||||
border: '1px solid var(--rah-bg-active)',
|
||||
border: '1px solid var(--rah-border)',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = 'var(--rah-text-secondary)';
|
||||
e.currentTarget.style.borderColor = 'var(--rah-border-stronger)';
|
||||
e.currentTarget.style.color = 'var(--rah-text-soft)';
|
||||
e.currentTarget.style.borderColor = 'var(--rah-border-strong)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = 'var(--rah-text-muted)';
|
||||
e.currentTarget.style.borderColor = 'var(--rah-bg-active)';
|
||||
e.currentTarget.style.borderColor = 'var(--rah-border)';
|
||||
}}
|
||||
>
|
||||
<Pencil size={10} />
|
||||
@@ -2927,10 +2920,10 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
|
||||
{/* Content display */}
|
||||
<div style={{ flex: 1, overflow: 'auto' }}>
|
||||
{nodesData[activeTab]?.chunk ? (
|
||||
{(nodesData[activeTab]?.source || nodesData[activeTab]?.chunk) ? (
|
||||
sourceReaderMode === 'reader' ? (
|
||||
<SourceReader
|
||||
content={nodesData[activeTab].chunk}
|
||||
content={nodesData[activeTab].source || nodesData[activeTab].chunk || ''}
|
||||
onTextSelect={onTextSelect ? (text) => onTextSelect(activeTab, nodesData[activeTab]?.title || 'Untitled', text) : undefined}
|
||||
highlightedText={highlightedPassage?.nodeId === activeTab ? highlightedPassage.selectedText : null}
|
||||
/>
|
||||
@@ -2949,7 +2942,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
minHeight: '100%',
|
||||
}}
|
||||
>
|
||||
{nodesData[activeTab].chunk}
|
||||
{nodesData[activeTab].source || nodesData[activeTab].chunk}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
@@ -3036,7 +3029,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
textAlign: 'left',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = 'var(--rah-border-strong)';
|
||||
e.currentTarget.style.background = 'var(--rah-bg-elevated)';
|
||||
e.currentTarget.style.color = 'var(--rah-text-active)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
@@ -3068,7 +3061,7 @@ export default function FocusPanel({ openTabs, activeTab, onTabSelect, onNodeCli
|
||||
textAlign: 'left',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = 'var(--rah-border-strong)';
|
||||
e.currentTarget.style.background = 'var(--rah-bg-elevated)';
|
||||
e.currentTarget.style.color = 'var(--rah-text-active)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
|
||||
Reference in New Issue
Block a user