@assistant-ui/react
Version:
Open-source TypeScript/React library for building production-grade AI chat experiences
149 lines (148 loc) • 5 kB
JavaScript
"use client";
import { tool } from "@assistant-ui/core";
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
import { createContext, forwardRef, useContext, useEffect, useId, useRef } from "@assistant-ui/tap/react-shim";
import { useAui } from "@assistant-ui/store";
import { jsx } from "react/jsx-runtime";
import { useComposedRefs } from "radix-ui/internal";
// src/model-context/makeAssistantVisible.tsx
const DEFAULT_ACTION_SETTLE_DELAY_MS = 2e3;
const MAX_ACTION_SETTLE_DELAY_MS = 2147483647;
const waitForSettle = async (settleDelayMs) => {
const delay = Number.isFinite(settleDelayMs) && settleDelayMs >= 0 && settleDelayMs <= MAX_ACTION_SETTLE_DELAY_MS ? settleDelayMs : DEFAULT_ACTION_SETTLE_DELAY_MS;
if (delay === 0) return;
await new Promise((resolve) => setTimeout(resolve, delay));
};
const readActionSettleDelay = (element) => {
const value = element.dataset.actionSettleDelay;
return value === void 0 || value.trim() === "" ? DEFAULT_ACTION_SETTLE_DELAY_MS : Number(value);
};
const click = tool({
parameters: {
type: "object",
properties: { clickId: { type: "string" } },
required: ["clickId"]
},
execute: async ({ clickId }) => {
const escapedClickId = CSS.escape(clickId);
const el = document.querySelector(`[data-click-id='${escapedClickId}']`);
if (el instanceof HTMLElement) {
el.click();
await waitForSettle(readActionSettleDelay(el));
return {};
} else return "Element not found";
}
});
const setNativeValue = (element, value) => {
const prototype = element instanceof HTMLInputElement ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype;
const setter = Object.getOwnPropertyDescriptor(prototype, "value")?.set;
if (setter) setter.call(element, value);
else element.value = value;
};
const edit = tool({
parameters: {
type: "object",
properties: {
editId: { type: "string" },
value: { type: "string" }
},
required: ["editId", "value"]
},
execute: async ({ editId, value }) => {
const escapedEditId = CSS.escape(editId);
const el = document.querySelector(`[data-edit-id='${escapedEditId}']`);
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
setNativeValue(el, value);
el.dispatchEvent(new Event("input", { bubbles: true }));
el.dispatchEvent(new Event("change", { bubbles: true }));
await waitForSettle(readActionSettleDelay(el));
return {};
} else return "Element not found";
}
});
const ReadableContext = createContext(false);
const makeAssistantVisible = (Component, config) => {
const settleDelayMs = config?.settleDelayMs ?? DEFAULT_ACTION_SETTLE_DELAY_MS;
if (!Number.isFinite(settleDelayMs) || settleDelayMs < 0 || settleDelayMs > MAX_ACTION_SETTLE_DELAY_MS) throw new RangeError(`settleDelayMs must be between 0 and ${MAX_ACTION_SETTLE_DELAY_MS}`);
const ReadableComponent = forwardRef((props, outerRef) => {
const $ = c(16);
const isNestedReadable = useContext(ReadableContext);
const clickId = useId();
const componentRef = useRef(null);
const aui = useAui();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = config ?? {};
$[0] = t0;
} else t0 = $[0];
const { clickable, editable } = t0;
let t1;
if ($[1] !== aui.modelContext || $[2] !== isNestedReadable) {
t1 = () => aui.modelContext.register({ getModelContext: () => ({
tools: {
...clickable ? { click } : {},
...editable ? { edit } : {}
},
system: !isNestedReadable ? componentRef.current?.outerHTML : void 0
}) });
$[1] = aui.modelContext;
$[2] = isNestedReadable;
$[3] = t1;
} else t1 = $[3];
let t2;
if ($[4] !== aui || $[5] !== isNestedReadable) {
t2 = [
isNestedReadable,
aui,
clickable,
editable
];
$[4] = aui;
$[5] = isNestedReadable;
$[6] = t2;
} else t2 = $[6];
useEffect(t1, t2);
const ref = useComposedRefs(componentRef, outerRef);
const t3 = props;
let t4;
let t5;
if ($[7] !== clickId) {
t4 = config?.clickable ? { "data-click-id": clickId } : {};
t5 = config?.editable ? { "data-edit-id": clickId } : {};
$[7] = clickId;
$[8] = t4;
$[9] = t5;
} else {
t4 = $[8];
t5 = $[9];
}
let t6;
if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
t6 = config?.settleDelayMs !== void 0 && (config.clickable || config.editable) ? { "data-action-settle-delay": settleDelayMs } : {};
$[10] = t6;
} else t6 = $[10];
let t7;
if ($[11] !== ref || $[12] !== t3 || $[13] !== t4 || $[14] !== t5) {
t7 = /* @__PURE__ */ jsx(ReadableContext.Provider, {
value: true,
children: /* @__PURE__ */ jsx(Component, {
...t3,
...t4,
...t5,
...t6,
ref
})
});
$[11] = ref;
$[12] = t3;
$[13] = t4;
$[14] = t5;
$[15] = t7;
} else t7 = $[15];
return t7;
});
ReadableComponent.displayName = Component.displayName;
return ReadableComponent;
};
export { makeAssistantVisible as default, makeAssistantVisible };
//# sourceMappingURL=makeAssistantVisible.js.map