fix: isolate teacher references from unrelated selections
This commit is contained in:
1 parent
c55e9c7182
commit
36202e0642
6 files changed
+235
-11
No files matched your search
@@ -191,8 +191,13 @@ export function CodingChatPanel({
|
||||
const [teacherComposing, setTeacherComposing] = useState(false);
|
||||
const [consultationWidth, setConsultationWidth] = useState(508);
|
||||
const account = useAuthStore((state) => state.user?.userId ?? state.user?.username ?? 'signed-out');
|
||||
const [teacherQuote, setTeacherQuote] = useState<TeacherReference | undefined>();
|
||||
const [teacherQuoteSource, setTeacherQuoteSource] = useState<string | null>(null);
|
||||
const operationChatRef = useRef<HTMLDivElement>(null);
|
||||
const [teacherQuote, setTeacherQuote] = useState<{
|
||||
reference: TeacherReference; account: string; projectId: string; sourceId: string; agentId: string;
|
||||
}>();
|
||||
const consumeTeacherQuote = useCallback((reference: TeacherReference) => {
|
||||
setTeacherQuote(current => current?.reference === reference ? undefined : current);
|
||||
}, []);
|
||||
const appliedNavigationDraftRef = useRef<string | null>(null);
|
||||
const selectedConversationContextRef = useRef<string | null>(null);
|
||||
const attachmentsRef = useRef(attachmentsByDraftKey);
|
||||
@@ -251,9 +256,18 @@ export function CodingChatPanel({
|
||||
const [teacherIntroduction, setTeacherIntroduction] = useState<HTMLDivElement | null>(null);
|
||||
const openTeacher = (agentId?: string) => {
|
||||
if (agentId) teacherCompanion.selectAgent(agentId);
|
||||
const selectedText = window.getSelection()?.toString().trim();
|
||||
setTeacherQuote(selectedText ? { kind: 'code', text: selectedText.slice(0, 12000) } : undefined);
|
||||
setTeacherQuoteSource(`${activeProject?.id}:${targetConversationId}`);
|
||||
const selection = window.getSelection();
|
||||
const operationChat = operationChatRef.current;
|
||||
// Avatar navigation must not turn selected text in another teacher's chat into a quote.
|
||||
const fromOperationChat = operationChat && !operationChat.hidden && selection?.rangeCount
|
||||
&& Array.from({ length: selection.rangeCount }, (_, index) => selection.getRangeAt(index))
|
||||
.every(range => operationChat.contains(range.commonAncestorContainer));
|
||||
const selectedText = fromOperationChat ? selection.toString().trim() : '';
|
||||
setTeacherQuote(selectedText && activeProject && targetConversationId ? {
|
||||
reference: { kind: 'code', text: selectedText.slice(0, 12000) }, account,
|
||||
projectId: activeProject.id, sourceId: targetConversationId,
|
||||
agentId: agentId ?? teacherCompanion.selectedAgent?.teacher_id ?? 'legacy',
|
||||
} : undefined);
|
||||
setConsultationOpen(true);
|
||||
setAgentBrowserOpen(true);
|
||||
};
|
||||
@@ -313,7 +327,7 @@ export function CodingChatPanel({
|
||||
useEffect(() => {
|
||||
setAgentBrowserOpen(false);
|
||||
}, [activeProject?.id]);
|
||||
useEffect(() => { setTeacherQuote(undefined); }, [activeProject?.id, targetConversationId]);
|
||||
useEffect(() => { setTeacherQuote(undefined); }, [account, activeProject?.id, targetConversationId]);
|
||||
|
||||
useEffect(() => () => disconnectEvents(), [disconnectEvents]);
|
||||
|
||||
@@ -812,7 +826,7 @@ export function CodingChatPanel({
|
||||
}} />
|
||||
</div>
|
||||
<CodingChatBody welcome={showWelcome} hidden={agentBrowserOpen} bubble={teacherBubble}>
|
||||
<div id="classroom-chat-view" role="tabpanel" aria-labelledby="classroom-chat-tab" hidden={agentBrowserOpen} className="classroom-chat-view min-h-0 flex-1 flex-col">
|
||||
<div ref={operationChatRef} id="classroom-chat-view" role="tabpanel" aria-labelledby="classroom-chat-tab" hidden={agentBrowserOpen} className="classroom-chat-view min-h-0 flex-1 flex-col">
|
||||
|
||||
{(workspaceError || conversationMetadataError || connectionError) && (
|
||||
<div className="mx-4 mt-3 flex min-h-10 items-center gap-2 rounded-xl bg-destructive/5 px-3 py-2 text-xs text-destructive sm:mx-5">
|
||||
@@ -984,7 +998,10 @@ export function CodingChatPanel({
|
||||
externalTopic={teacherCompanion.topic}
|
||||
onTopicChange={teacherCompanion.noteTopic}
|
||||
onComposingChange={setTeacherComposing}
|
||||
quote={consultationOpen && teacherQuoteSource === `${activeProject.id}:${targetConversationId}` ? teacherQuote : undefined} />
|
||||
quote={consultationOpen && teacherQuote?.account === account && teacherQuote.projectId === activeProject.id
|
||||
&& teacherQuote.sourceId === targetConversationId
|
||||
&& teacherQuote.agentId === (teacherCompanion.selectedAgent?.teacher_id ?? 'legacy') ? teacherQuote.reference : undefined}
|
||||
onQuoteConsumed={consumeTeacherQuote} />
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
@@ -43,6 +43,7 @@ function ProjectTeacherChatPanel({
|
||||
sourceId,
|
||||
onBringBack,
|
||||
quote,
|
||||
onQuoteConsumed,
|
||||
draftRevision,
|
||||
sampleContext,
|
||||
externalTopic,
|
||||
@@ -56,6 +57,7 @@ function ProjectTeacherChatPanel({
|
||||
sourceId: string;
|
||||
onBringBack?(text: string): void;
|
||||
quote?: TeacherReference;
|
||||
onQuoteConsumed?(reference: TeacherReference): void;
|
||||
draftRevision?: number;
|
||||
sampleContext?: string;
|
||||
projectName?: string;
|
||||
@@ -84,7 +86,7 @@ function ProjectTeacherChatPanel({
|
||||
return selectedAgent ? JSON.parse(localStorage.getItem(`makelore-consultation-draft:${account}:${projectId}:agent:${selectedAgent.teacher_id}`) ?? '{}') : {};
|
||||
} catch { return {}; }
|
||||
});
|
||||
const [referenceScope, setReferenceScope] = useState({ projectId: saved.projectId ?? projectId, sourceId: saved.sourceId ?? sourceId });
|
||||
const [referenceScope, setReferenceScope] = useState(quote ? { projectId, sourceId } : { projectId: saved.projectId ?? projectId, sourceId: saved.sourceId ?? sourceId });
|
||||
const referenceScopeRef = useRef(referenceScope);
|
||||
referenceScopeRef.current = referenceScope;
|
||||
const [legacyDraft] = useState<{ text?: string; references?: TeacherReference[] }>(() => {
|
||||
@@ -211,8 +213,12 @@ function ProjectTeacherChatPanel({
|
||||
}
|
||||
}, [busy]);
|
||||
useEffect(() => {
|
||||
if (quote) { setReferences([quote]); setReferenceScope({ projectId, sourceId }); }
|
||||
}, [quote, projectId, sourceId]);
|
||||
if (quote) {
|
||||
setReferences([quote]);
|
||||
setReferenceScope({ projectId, sourceId });
|
||||
onQuoteConsumed?.(quote);
|
||||
}
|
||||
}, [quote, projectId, sourceId, onQuoteConsumed]);
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
setBusy(true);
|
||||
|
||||
Reference in new issue
Block a user