a screen can hear when a chat turn finished
ChatPanelWrapper already refreshed the file tree and the preview when a turn ran tools. it kept that to itself, so a screen whose own panels show something a turn can change had no way to know — which is every plugin with a chat beside a list. so onTurnComplete is a prop now, composed with the platform's own handling rather than replacing it. called for EVERY turn, outside the hadToolCalls guard, because "did anything I care about change" is a question about the caller's screen and not about this one — it gets the flag and decides. read through a ref: a caller passing an inline arrow would otherwise rebuild the callback every render, and it goes into EmbeddableChat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
import { useWorkspace } from '../../components/Workspace';
|
import { useWorkspace } from '../../components/Workspace';
|
||||||
import { useActiveChatSession, useFilesRefresh, usePreviewRefresh } from '../../channels';
|
import { useActiveChatSession, useFilesRefresh, usePreviewRefresh } from '../../channels';
|
||||||
@@ -164,9 +164,26 @@ type ChatPanelWrapperProps = {
|
|||||||
* platform mounts, not a caller that could pass this.
|
* platform mounts, not a caller that could pass this.
|
||||||
*/
|
*/
|
||||||
chatContext?: { context: string; contextId?: string };
|
chatContext?: { context: string; contextId?: string };
|
||||||
|
/**
|
||||||
|
* Called when a turn ends, with whether it ran any tools.
|
||||||
|
*
|
||||||
|
* The platform's own refreshes — the file tree, the preview — always happen; this is composed on top for
|
||||||
|
* a screen whose panels show something a turn can change. A plugin's lists are the case: an agent that
|
||||||
|
* deletes a proxy host leaves every panel beside it describing a world that no longer exists, and the
|
||||||
|
* only signal a panel gets is this one.
|
||||||
|
*
|
||||||
|
* `hadToolCalls` is the whole discrimination worth making. A turn that only talked changed nothing, and
|
||||||
|
* re-reading after every sentence would put a request behind every reply.
|
||||||
|
*/
|
||||||
|
onTurnComplete?: (hadToolCalls: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ChatPanelWrapper = ({ panelId, promptPrefix, chatContext: declared }: ChatPanelWrapperProps) => {
|
export const ChatPanelWrapper = ({
|
||||||
|
panelId,
|
||||||
|
promptPrefix,
|
||||||
|
chatContext: declared,
|
||||||
|
onTurnComplete: alsoOnTurnComplete,
|
||||||
|
}: ChatPanelWrapperProps) => {
|
||||||
const { workspace, cwd, root } = useWorkspace();
|
const { workspace, cwd, root } = useWorkspace();
|
||||||
const scoped = cwd !== '~';
|
const scoped = cwd !== '~';
|
||||||
|
|
||||||
@@ -198,12 +215,20 @@ export const ChatPanelWrapper = ({ panelId, promptPrefix, chatContext: declared
|
|||||||
const [, setPreviewRefresh] = usePreviewRefresh();
|
const [, setPreviewRefresh] = usePreviewRefresh();
|
||||||
const [, bumpFilesRefresh] = useFilesRefresh();
|
const [, bumpFilesRefresh] = useFilesRefresh();
|
||||||
|
|
||||||
|
// The caller's handler is read through a ref rather than named as a dependency: a plugin passing an
|
||||||
|
// inline arrow would otherwise rebuild this callback every render, and it goes into EmbeddableChat.
|
||||||
|
const alsoRef = useRef(alsoOnTurnComplete);
|
||||||
|
alsoRef.current = alsoOnTurnComplete;
|
||||||
|
|
||||||
const onTurnComplete = useCallback(
|
const onTurnComplete = useCallback(
|
||||||
(hadToolCalls: boolean) => {
|
(hadToolCalls: boolean) => {
|
||||||
if (hadToolCalls) {
|
if (hadToolCalls) {
|
||||||
setPreviewRefresh(Date.now());
|
setPreviewRefresh(Date.now());
|
||||||
bumpFilesRefresh();
|
bumpFilesRefresh();
|
||||||
}
|
}
|
||||||
|
// Outside the `hadToolCalls` guard deliberately — the caller is told about every turn and decides
|
||||||
|
// for itself, because "did anything change" is a question about that screen, not about this one.
|
||||||
|
alsoRef.current?.(hadToolCalls);
|
||||||
},
|
},
|
||||||
[setPreviewRefresh, bumpFilesRefresh],
|
[setPreviewRefresh, bumpFilesRefresh],
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user