@ash-cloud/ash-ui 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1742,6 +1742,9 @@ function isToolCallEntry(entry) {
1742
1742
  function isErrorEntry(entry) {
1743
1743
  return entry.type === "error";
1744
1744
  }
1745
+ function isWidgetEntry(entry) {
1746
+ return entry.type === "widget";
1747
+ }
1745
1748
  var DEFAULT_DISPLAY_CONFIG = {
1746
1749
  mode: "inline",
1747
1750
  breakEveryNToolCalls: 0,
@@ -1797,10 +1800,25 @@ function MessageList({
1797
1800
  streamingContent,
1798
1801
  displayConfig: displayConfigProp,
1799
1802
  onOptionSelect,
1803
+ renderWidget,
1804
+ onWidgetAction,
1800
1805
  className
1801
1806
  }) {
1802
1807
  const contextConfig = useDisplayConfig();
1803
1808
  const config = displayConfigProp || contextConfig;
1809
+ const createWidgetActionHandler = react.useCallback(
1810
+ (entryId, widgetType) => {
1811
+ if (!onWidgetAction) return void 0;
1812
+ return (action) => {
1813
+ onWidgetAction({
1814
+ ...action,
1815
+ entryId,
1816
+ widgetType
1817
+ });
1818
+ };
1819
+ },
1820
+ [onWidgetAction]
1821
+ );
1804
1822
  const groupedEntries = react.useMemo(() => {
1805
1823
  if (config.mode === "inline") {
1806
1824
  return entries.map((entry) => ({
@@ -1814,7 +1832,20 @@ function MessageList({
1814
1832
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex-1 overflow-y-auto p-4 space-y-4 ash-scrollbar", className), children: [
1815
1833
  groupedEntries.map((groupedEntry) => {
1816
1834
  if (groupedEntry.type === "single") {
1817
- return /* @__PURE__ */ jsxRuntime.jsx(MessageEntry, { entry: groupedEntry.entry, onOptionSelect }, groupedEntry.entry.id);
1835
+ const entry = groupedEntry.entry;
1836
+ if (entry.entryType.type === "widget" && renderWidget) {
1837
+ const widgetEntry = entry.entryType;
1838
+ const widgetContent = renderWidget({
1839
+ entry,
1840
+ widgetType: widgetEntry.widgetType,
1841
+ widgetData: widgetEntry.widgetData,
1842
+ onAction: createWidgetActionHandler(entry.id, widgetEntry.widgetType)
1843
+ });
1844
+ if (widgetContent !== null) {
1845
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-animate-fade-in", children: widgetContent }, entry.id);
1846
+ }
1847
+ }
1848
+ return /* @__PURE__ */ jsxRuntime.jsx(MessageEntry, { entry, onOptionSelect }, entry.id);
1818
1849
  }
1819
1850
  const toolCalls = extractToolCallsFromGroup(groupedEntry.entries);
1820
1851
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
@@ -2942,6 +2973,212 @@ function useFileUpload({
2942
2973
  openFilePicker
2943
2974
  };
2944
2975
  }
2976
+ function useAgentChat(options) {
2977
+ const {
2978
+ createStream,
2979
+ initialSessionId,
2980
+ initialEntries = [],
2981
+ onSessionStart,
2982
+ onSessionEnd,
2983
+ onError,
2984
+ onSandboxLog
2985
+ } = options;
2986
+ const [historyEntries, setHistoryEntries] = react.useState(initialEntries);
2987
+ const [streamingEntries, setStreamingEntries] = react.useState([]);
2988
+ const [isStreaming, setIsStreaming] = react.useState(false);
2989
+ const [error, setError] = react.useState(null);
2990
+ const [sessionId, setSessionId] = react.useState(initialSessionId || null);
2991
+ const abortControllerRef = react.useRef(null);
2992
+ const currentTextRef = react.useRef("");
2993
+ const currentTextIdRef = react.useRef(null);
2994
+ const pendingToolCallsRef = react.useRef(/* @__PURE__ */ new Map());
2995
+ const hadToolCallSinceTextRef = react.useRef(false);
2996
+ const entries = [...historyEntries, ...streamingEntries];
2997
+ const emitStreamingEntries = react.useCallback((newEntries) => {
2998
+ setStreamingEntries([...newEntries]);
2999
+ }, []);
3000
+ const createTextEntry = react.useCallback((id, content) => ({
3001
+ id,
3002
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3003
+ entryType: { type: "assistant_message" },
3004
+ content
3005
+ }), []);
3006
+ const resetStreamingState = react.useCallback(() => {
3007
+ currentTextRef.current = "";
3008
+ currentTextIdRef.current = null;
3009
+ pendingToolCallsRef.current.clear();
3010
+ hadToolCallSinceTextRef.current = false;
3011
+ setStreamingEntries([]);
3012
+ }, []);
3013
+ const processEvent = react.useCallback((event, streamEntries) => {
3014
+ const newEntries = [...streamEntries];
3015
+ switch (event.type) {
3016
+ case "session_start":
3017
+ if (event.sessionId) {
3018
+ setSessionId(event.sessionId);
3019
+ onSessionStart?.(event.sessionId);
3020
+ }
3021
+ break;
3022
+ case "text_delta":
3023
+ if (event.delta) {
3024
+ if (hadToolCallSinceTextRef.current && currentTextIdRef.current) {
3025
+ currentTextRef.current = "";
3026
+ currentTextIdRef.current = null;
3027
+ hadToolCallSinceTextRef.current = false;
3028
+ }
3029
+ currentTextRef.current += event.delta;
3030
+ if (!currentTextIdRef.current) {
3031
+ currentTextIdRef.current = `text-${Date.now()}-${Math.random().toString(36).slice(2)}`;
3032
+ newEntries.push(createTextEntry(currentTextIdRef.current, currentTextRef.current));
3033
+ } else {
3034
+ const entryIndex = newEntries.findIndex((e) => e.id === currentTextIdRef.current);
3035
+ if (entryIndex !== -1) {
3036
+ newEntries[entryIndex] = createTextEntry(currentTextIdRef.current, currentTextRef.current);
3037
+ }
3038
+ }
3039
+ }
3040
+ break;
3041
+ case "tool_use":
3042
+ if (event.toolId && event.toolName) {
3043
+ currentTextRef.current = "";
3044
+ currentTextIdRef.current = null;
3045
+ hadToolCallSinceTextRef.current = true;
3046
+ const toolCall = createToolCall({
3047
+ id: event.toolId,
3048
+ name: event.toolName,
3049
+ input: event.input
3050
+ });
3051
+ const entry = {
3052
+ id: event.toolId,
3053
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3054
+ entryType: { type: "tool_call", toolCall },
3055
+ content: toolCall.summary
3056
+ };
3057
+ const entryIndex = newEntries.length;
3058
+ newEntries.push(entry);
3059
+ pendingToolCallsRef.current.set(event.toolId, { entryIndex, toolCall });
3060
+ }
3061
+ break;
3062
+ case "tool_result":
3063
+ if (event.toolId) {
3064
+ const pending = pendingToolCallsRef.current.get(event.toolId);
3065
+ if (pending) {
3066
+ const updatedToolCall = updateToolCallWithResult(
3067
+ pending.toolCall,
3068
+ event.toolResult,
3069
+ event.isError
3070
+ );
3071
+ const existingEntry = newEntries[pending.entryIndex];
3072
+ if (existingEntry && existingEntry.entryType.type === "tool_call") {
3073
+ newEntries[pending.entryIndex] = {
3074
+ ...existingEntry,
3075
+ entryType: { type: "tool_call", toolCall: updatedToolCall }
3076
+ };
3077
+ }
3078
+ pendingToolCallsRef.current.delete(event.toolId);
3079
+ }
3080
+ }
3081
+ break;
3082
+ case "sandbox_log":
3083
+ if (event.entry) {
3084
+ onSandboxLog?.(event.entry);
3085
+ }
3086
+ break;
3087
+ case "error":
3088
+ if (event.error) {
3089
+ setError(event.error);
3090
+ onError?.(event.error);
3091
+ newEntries.push({
3092
+ id: `error-${Date.now()}`,
3093
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3094
+ entryType: { type: "error", message: event.error, code: event.code },
3095
+ content: event.error
3096
+ });
3097
+ }
3098
+ break;
3099
+ case "session_end":
3100
+ onSessionEnd?.(event.sessionId || sessionId || "", event.status || "completed");
3101
+ break;
3102
+ case "complete":
3103
+ if (newEntries.length > 0) {
3104
+ setHistoryEntries((prev) => [...prev, ...newEntries]);
3105
+ }
3106
+ resetStreamingState();
3107
+ return [];
3108
+ }
3109
+ return newEntries;
3110
+ }, [sessionId, onSessionStart, onSessionEnd, onError, onSandboxLog, createTextEntry, resetStreamingState]);
3111
+ const send = react.useCallback(async (prompt) => {
3112
+ if (isStreaming) return;
3113
+ setIsStreaming(true);
3114
+ setError(null);
3115
+ const userEntry = {
3116
+ id: `user-${Date.now()}`,
3117
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3118
+ entryType: { type: "user_message" },
3119
+ content: prompt
3120
+ };
3121
+ setHistoryEntries((prev) => [...prev, userEntry]);
3122
+ resetStreamingState();
3123
+ const controller = new AbortController();
3124
+ abortControllerRef.current = controller;
3125
+ let localStreamingEntries = [];
3126
+ try {
3127
+ const stream = createStream(prompt, sessionId || void 0);
3128
+ for await (const event of stream) {
3129
+ if (controller.signal.aborted) break;
3130
+ localStreamingEntries = processEvent(event, localStreamingEntries);
3131
+ emitStreamingEntries(localStreamingEntries);
3132
+ }
3133
+ if (localStreamingEntries.length > 0) {
3134
+ setHistoryEntries((prev) => [...prev, ...localStreamingEntries]);
3135
+ setStreamingEntries([]);
3136
+ }
3137
+ } catch (err) {
3138
+ if (err.name !== "AbortError") {
3139
+ const errorMessage = err instanceof Error ? err.message : "Unknown error";
3140
+ setError(errorMessage);
3141
+ onError?.(errorMessage);
3142
+ }
3143
+ } finally {
3144
+ setIsStreaming(false);
3145
+ abortControllerRef.current = null;
3146
+ resetStreamingState();
3147
+ }
3148
+ }, [isStreaming, sessionId, createStream, processEvent, emitStreamingEntries, resetStreamingState, onError]);
3149
+ const stop = react.useCallback(() => {
3150
+ if (abortControllerRef.current) {
3151
+ abortControllerRef.current.abort();
3152
+ }
3153
+ }, []);
3154
+ const clear = react.useCallback(() => {
3155
+ setHistoryEntries([]);
3156
+ resetStreamingState();
3157
+ setSessionId(initialSessionId || null);
3158
+ setError(null);
3159
+ }, [initialSessionId, resetStreamingState]);
3160
+ const setEntries = react.useCallback((newEntries) => {
3161
+ resetStreamingState();
3162
+ setHistoryEntries(newEntries);
3163
+ }, [resetStreamingState]);
3164
+ react.useEffect(() => {
3165
+ return () => {
3166
+ if (abortControllerRef.current) {
3167
+ abortControllerRef.current.abort();
3168
+ }
3169
+ };
3170
+ }, []);
3171
+ return {
3172
+ entries,
3173
+ isStreaming,
3174
+ error,
3175
+ sessionId,
3176
+ send,
3177
+ stop,
3178
+ clear,
3179
+ setEntries
3180
+ };
3181
+ }
2945
3182
 
2946
3183
  exports.ActionIcon = ActionIcon;
2947
3184
  exports.AlertCircleIcon = AlertCircleIcon;
@@ -3036,6 +3273,7 @@ exports.isTodoWriteAction = isTodoWriteAction;
3036
3273
  exports.isToolCallEntry = isToolCallEntry;
3037
3274
  exports.isWebFetchAction = isWebFetchAction;
3038
3275
  exports.isWebSearchAction = isWebSearchAction;
3276
+ exports.isWidgetEntry = isWidgetEntry;
3039
3277
  exports.keyframes = keyframes;
3040
3278
  exports.keyframesCss = keyframesCss;
3041
3279
  exports.mapToolToActionType = mapToolToActionType;
@@ -3050,6 +3288,7 @@ exports.transitions = transitions;
3050
3288
  exports.truncate = truncate;
3051
3289
  exports.typography = typography;
3052
3290
  exports.updateToolCallWithResult = updateToolCallWithResult;
3291
+ exports.useAgentChat = useAgentChat;
3053
3292
  exports.useDisplayConfig = useDisplayConfig;
3054
3293
  exports.useDisplayMode = useDisplayMode;
3055
3294
  exports.useFileUpload = useFileUpload;