feat(js): add message-specific fallback UI components (#4430)

This commit is contained in:
David Duong
2025-04-28 12:17:07 +02:00
committed by GitHub
+35 -2
View File
@@ -126,7 +126,7 @@ interface LoadExternalComponentProps
meta?: unknown;
/** Fallback to be rendered when the component is loading */
fallback?: React.ReactNode;
fallback?: React.ReactNode | Record<string, React.ReactNode>;
/**
* Map of components that can be rendered directly without fetching the UI code
@@ -135,6 +135,33 @@ interface LoadExternalComponentProps
components?: Record<string, React.FunctionComponent | React.ComponentClass>;
}
const isIterable = (value: unknown): value is Iterable<unknown> =>
value != null && typeof value === "object" && Symbol.iterator in value;
const isPromise = (value: unknown): value is Promise<unknown> =>
value != null &&
typeof value === "object" &&
"then" in value &&
typeof value.then === "function";
const isReactNode = (value: unknown): value is React.ReactNode => {
if (React.isValidElement(value)) return true;
if (value == null) return true;
if (
typeof value === "string" ||
typeof value === "number" ||
typeof value === "bigint" ||
typeof value === "boolean"
) {
return true;
}
if (isIterable(value)) return true;
if (isPromise(value)) return true;
return false;
};
export function LoadExternalComponent({
stream,
namespace,
@@ -157,6 +184,12 @@ export function LoadExternalComponent({
const clientComponent = components?.[message.name];
const hasClientComponent = clientComponent != null;
const fallbackComponent = isReactNode(fallback)
? fallback
: typeof fallback === "object" && fallback != null
? fallback?.[message.name]
: null;
const uiNamespace = namespace ?? stream.assistantId;
const uiClient = stream.client["~ui"];
React.useEffect(() => {
@@ -192,7 +225,7 @@ export function LoadExternalComponent({
React.createElement(state.comp, message.props),
state.target,
)
: fallback}
: fallbackComponent}
</UseStreamContext.Provider>
</>
);