feat: sync open-source context and capsule removal

- remove legacy contexts surfaces from the app, MCP bridge, standalone server, and docs
- keep getContext and retrieveQueryContext while aligning the simplified graph-only contract
- harden dev startup migration behavior and disable the accidental chat surface in open source

Generated with Codex
This commit is contained in:
“BeeRad”
2026-04-17 12:34:51 +10:00
parent 97eeb0789f
commit 07754f5030
87 changed files with 2688 additions and 4861 deletions
-102
View File
@@ -12,7 +12,6 @@ import { openExternalUrl, shouldOpenExternally } from '@/utils/openExternalUrl';
import { normalizeNodeLink } from '@/utils/nodeLink';
import NodeSearchModal from './edges/NodeSearchModal';
import { getNodeProcessedState, parseNodeMetadata } from '@/services/nodes/metadata';
import type { ContextSummary } from '@/types/database';
interface FocusPanelProps {
openTabs: number[];
@@ -68,9 +67,6 @@ export default function FocusPanel({
const [sourceEditMode, setSourceEditMode] = useState(false);
const [sourceEditValue, setSourceEditValue] = useState('');
const [sourceSaving, setSourceSaving] = useState(false);
const [availableContexts, setAvailableContexts] = useState<ContextSummary[]>([]);
const [contextSaving, setContextSaving] = useState(false);
const [contextMenuOpen, setContextMenuOpen] = useState(false);
const [hoveredSection, setHoveredSection] = useState<HoverSection>(null);
const titleInputRef = useRef<HTMLInputElement>(null);
@@ -79,7 +75,6 @@ export default function FocusPanel({
const sourceTextareaRef = useRef<HTMLTextAreaElement>(null);
const skipTitleBlurRef = useRef(false);
const skipLinkBlurRef = useRef(false);
const contextMenuRef = useRef<HTMLDivElement>(null);
const currentNode = activeTab !== null ? nodesData[activeTab] : undefined;
const normalizedCurrentLink = normalizeNodeLink(currentNode?.link || null);
@@ -105,34 +100,6 @@ export default function FocusPanel({
if (sourceEditMode) sourceTextareaRef.current?.focus();
}, [sourceEditMode]);
useEffect(() => {
if (!contextMenuOpen) return;
const handlePointerDown = (event: MouseEvent) => {
if (!contextMenuRef.current?.contains(event.target as globalThis.Node)) {
setContextMenuOpen(false);
}
};
document.addEventListener('mousedown', handlePointerDown);
return () => document.removeEventListener('mousedown', handlePointerDown);
}, [contextMenuOpen]);
useEffect(() => {
void (async () => {
try {
const response = await fetch('/api/contexts');
const data = await response.json();
if (response.ok && data.success) {
setAvailableContexts(data.data || []);
}
} catch (error) {
console.error('Failed to fetch contexts:', error);
}
})();
}, []);
useEffect(() => {
openTabs.forEach((tabId) => {
if (!nodesData[tabId] && !loadingNodes.has(tabId)) {
@@ -165,7 +132,6 @@ export default function FocusPanel({
setDescEditValue('');
setSourceEditMode(false);
setSourceEditValue('');
setContextMenuOpen(false);
setEdgeSearchOpen(false);
setEdgeEditingId(null);
setEdgeEditingValue('');
@@ -389,20 +355,6 @@ export default function FocusPanel({
}
};
const saveContext = async (value: string) => {
if (!activeTab) return;
setContextSaving(true);
try {
await updateNode(activeTab, { context_id: value ? Number(value) : null });
setContextMenuOpen(false);
} catch (error) {
console.error('Error saving context:', error);
window.alert('Failed to save context. Please try again.');
} finally {
setContextSaving(false);
}
};
const embedContent = async (nodeId: number) => {
setEmbeddingNode(nodeId);
try {
@@ -1192,60 +1144,6 @@ export default function FocusPanel({
</div>
</div>
<div className="prop-row">
<div className="prop-label">context</div>
<div className="prop-value context-prop-value">
<div className="context-select-shell" ref={contextMenuRef}>
<div className="context-inline-row">
<span className={currentNode.context ? 'context-select-name' : 'context-select-empty'}>
{currentNode.context?.name || 'No context'}
</span>
<button
type="button"
className="context-inline-button"
disabled={contextSaving}
onClick={(event) => {
event.stopPropagation();
setContextMenuOpen((prev) => !prev);
}}
title={currentNode.context ? 'Change context' : 'Add context'}
>
<Plus size={12} />
</button>
</div>
{contextMenuOpen ? (
<div className="context-select-menu">
<button
type="button"
className={`context-option ${currentNode.context_id == null ? 'active' : ''}`}
onClick={(event) => {
event.stopPropagation();
void saveContext('');
}}
>
No context
</button>
{availableContexts.map((context) => (
<button
key={context.id}
type="button"
className={`context-option ${currentNode.context_id === context.id ? 'active' : ''}`}
onClick={(event) => {
event.stopPropagation();
void saveContext(String(context.id));
}}
>
<span>{context.name}</span>
<span className="context-option-count">{context.count}</span>
</button>
))}
</div>
) : null}
</div>
</div>
</div>
{/* Connections */}
<div className="prop-row prop-row-top">
<div className="prop-label">edge</div>