mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-12 09:12:40 +02:00
49 lines
1.9 KiB
TypeScript
49 lines
1.9 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
|
||
|
|
import { createContext, useContext } from "react";
|
||
|
|
import { isInterruptResult } from "./types";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Where this tool-call card is currently rendering.
|
||
|
|
*
|
||
|
|
* - ``"body"`` (default) — assistant-ui's ``MessagePrimitive.Parts`` renders
|
||
|
|
* the card inside the message bubble.
|
||
|
|
* - ``"timeline"`` — ``ThinkingStepsDisplay`` renders the SAME component
|
||
|
|
* inline under the matching step row so the HITL approval lives in the
|
||
|
|
* chain-of-thought instead of as a standalone card in the message body.
|
||
|
|
*
|
||
|
|
* The two render targets share one component implementation; the context
|
||
|
|
* lets the body render skip itself when the timeline copy will show the
|
||
|
|
* card, avoiding a double-render.
|
||
|
|
*/
|
||
|
|
export type HitlRenderTarget = "body" | "timeline";
|
||
|
|
|
||
|
|
const HitlRenderTargetContext = createContext<HitlRenderTarget>("body");
|
||
|
|
|
||
|
|
export const HitlRenderTargetProvider = HitlRenderTargetContext.Provider;
|
||
|
|
|
||
|
|
export function useHitlRenderTarget(): HitlRenderTarget {
|
||
|
|
return useContext(HitlRenderTargetContext);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Hide the body render of a tool-call whose result is a HITL interrupt.
|
||
|
|
* The same component is mounted again inside ``ThinkingStepsDisplay``
|
||
|
|
* with ``HitlRenderTargetProvider value="timeline"`` — that copy renders
|
||
|
|
* normally, so the card "moves" from the message body to the timeline.
|
||
|
|
*
|
||
|
|
* Pure pass-through for non-HITL results AND for the timeline render.
|
||
|
|
*/
|
||
|
|
export function withHitlInTimeline(
|
||
|
|
Component: ToolCallMessagePartComponent
|
||
|
|
): ToolCallMessagePartComponent {
|
||
|
|
const Wrapped: ToolCallMessagePartComponent = (props) => {
|
||
|
|
const target = useHitlRenderTarget();
|
||
|
|
if (target === "body" && isInterruptResult(props.result)) return null;
|
||
|
|
return <Component {...props} />;
|
||
|
|
};
|
||
|
|
Wrapped.displayName = `withHitlInTimeline(${Component.displayName ?? Component.name ?? "ToolUI"})`;
|
||
|
|
return Wrapped;
|
||
|
|
}
|