@ash-cloud/ash-ui 0.2.3 → 0.2.5
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 +119 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -3
- package/dist/index.d.ts +47 -3
- package/dist/index.js +120 -8
- package/dist/index.js.map +1 -1
- package/dist/styles-full.css +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -423,11 +423,17 @@ function Conversation({
|
|
|
423
423
|
children,
|
|
424
424
|
className,
|
|
425
425
|
autoScroll: initialAutoScroll = true,
|
|
426
|
-
scrollThreshold = 100
|
|
426
|
+
scrollThreshold = 100,
|
|
427
|
+
onLoadMore,
|
|
428
|
+
hasMore = false,
|
|
429
|
+
isLoadingMore = false,
|
|
430
|
+
loadMoreThreshold = 100
|
|
427
431
|
}) {
|
|
428
432
|
const containerRef = react.useRef(null);
|
|
429
433
|
const [isScrolledUp, setIsScrolledUp] = react.useState(false);
|
|
430
434
|
const [autoScroll, setAutoScroll] = react.useState(initialAutoScroll);
|
|
435
|
+
const prevScrollHeightRef = react.useRef(0);
|
|
436
|
+
const wasLoadingMoreRef = react.useRef(false);
|
|
431
437
|
const scrollToBottom = react.useCallback((behavior = "smooth") => {
|
|
432
438
|
if (containerRef.current) {
|
|
433
439
|
containerRef.current.scrollTo({
|
|
@@ -445,7 +451,26 @@ function Conversation({
|
|
|
445
451
|
if (isAtBottom && !autoScroll) {
|
|
446
452
|
setAutoScroll(true);
|
|
447
453
|
}
|
|
448
|
-
|
|
454
|
+
if (scrollTop < loadMoreThreshold && hasMore && !isLoadingMore && onLoadMore) {
|
|
455
|
+
onLoadMore();
|
|
456
|
+
}
|
|
457
|
+
}, [scrollThreshold, autoScroll, loadMoreThreshold, hasMore, isLoadingMore, onLoadMore]);
|
|
458
|
+
react.useEffect(() => {
|
|
459
|
+
if (isLoadingMore && !wasLoadingMoreRef.current && containerRef.current) {
|
|
460
|
+
prevScrollHeightRef.current = containerRef.current.scrollHeight;
|
|
461
|
+
}
|
|
462
|
+
wasLoadingMoreRef.current = isLoadingMore;
|
|
463
|
+
}, [isLoadingMore]);
|
|
464
|
+
react.useLayoutEffect(() => {
|
|
465
|
+
if (!isLoadingMore && prevScrollHeightRef.current > 0 && containerRef.current) {
|
|
466
|
+
const newScrollHeight = containerRef.current.scrollHeight;
|
|
467
|
+
const scrollDelta = newScrollHeight - prevScrollHeightRef.current;
|
|
468
|
+
if (scrollDelta > 0) {
|
|
469
|
+
containerRef.current.scrollTop += scrollDelta;
|
|
470
|
+
}
|
|
471
|
+
prevScrollHeightRef.current = 0;
|
|
472
|
+
}
|
|
473
|
+
}, [isLoadingMore]);
|
|
449
474
|
react.useEffect(() => {
|
|
450
475
|
if (autoScroll && !isScrolledUp) {
|
|
451
476
|
scrollToBottom("instant");
|
|
@@ -456,7 +481,9 @@ function Conversation({
|
|
|
456
481
|
scrollToBottom,
|
|
457
482
|
isScrolledUp,
|
|
458
483
|
autoScroll,
|
|
459
|
-
setAutoScroll
|
|
484
|
+
setAutoScroll,
|
|
485
|
+
hasMore,
|
|
486
|
+
isLoadingMore
|
|
460
487
|
};
|
|
461
488
|
return /* @__PURE__ */ jsxRuntime.jsx(ConversationContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
462
489
|
"div",
|
|
@@ -554,6 +581,50 @@ function ConversationScrollButton({
|
|
|
554
581
|
}
|
|
555
582
|
);
|
|
556
583
|
}
|
|
584
|
+
function ConversationLoadMore({
|
|
585
|
+
className
|
|
586
|
+
}) {
|
|
587
|
+
const { hasMore, isLoadingMore } = useConversation();
|
|
588
|
+
if (!hasMore && !isLoadingMore) return null;
|
|
589
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
590
|
+
"div",
|
|
591
|
+
{
|
|
592
|
+
className: cn(
|
|
593
|
+
"ash-conversation-load-more flex items-center justify-center py-3",
|
|
594
|
+
className
|
|
595
|
+
),
|
|
596
|
+
children: isLoadingMore && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
597
|
+
"svg",
|
|
598
|
+
{
|
|
599
|
+
className: "w-5 h-5 animate-spin text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
|
|
600
|
+
fill: "none",
|
|
601
|
+
viewBox: "0 0 24 24",
|
|
602
|
+
children: [
|
|
603
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
604
|
+
"circle",
|
|
605
|
+
{
|
|
606
|
+
className: "opacity-25",
|
|
607
|
+
cx: "12",
|
|
608
|
+
cy: "12",
|
|
609
|
+
r: "10",
|
|
610
|
+
stroke: "currentColor",
|
|
611
|
+
strokeWidth: "4"
|
|
612
|
+
}
|
|
613
|
+
),
|
|
614
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
615
|
+
"path",
|
|
616
|
+
{
|
|
617
|
+
className: "opacity-75",
|
|
618
|
+
fill: "currentColor",
|
|
619
|
+
d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
620
|
+
}
|
|
621
|
+
)
|
|
622
|
+
]
|
|
623
|
+
}
|
|
624
|
+
)
|
|
625
|
+
}
|
|
626
|
+
);
|
|
627
|
+
}
|
|
557
628
|
var ReactMarkdown = react.lazy(() => import('react-markdown'));
|
|
558
629
|
function LazyMarkdown({ children, fallback, components, className }) {
|
|
559
630
|
const [mounted, setMounted] = react.useState(false);
|
|
@@ -3595,6 +3666,8 @@ function useAgentChat(options) {
|
|
|
3595
3666
|
onSessionEnd,
|
|
3596
3667
|
onError,
|
|
3597
3668
|
onSandboxLog,
|
|
3669
|
+
canUseTool,
|
|
3670
|
+
resolveToolPermission,
|
|
3598
3671
|
onReconnect,
|
|
3599
3672
|
maxReconnectAttempts = 3,
|
|
3600
3673
|
reconnectBaseDelay = 1e3,
|
|
@@ -3628,6 +3701,23 @@ function useAgentChat(options) {
|
|
|
3628
3701
|
const emitStreamingEntries = react.useCallback((newEntries) => {
|
|
3629
3702
|
setStreamingEntries([...newEntries]);
|
|
3630
3703
|
}, []);
|
|
3704
|
+
const handleToolPermission = react.useCallback(async (event) => {
|
|
3705
|
+
if (event.type !== "tool_permission") return;
|
|
3706
|
+
if (!canUseTool) return;
|
|
3707
|
+
if (!event.requestId || !event.sessionId || !event.toolName) return;
|
|
3708
|
+
const request = {
|
|
3709
|
+
requestId: event.requestId,
|
|
3710
|
+
sessionId: event.sessionId,
|
|
3711
|
+
toolName: event.toolName,
|
|
3712
|
+
input: event.input
|
|
3713
|
+
};
|
|
3714
|
+
const allow = await canUseTool(request);
|
|
3715
|
+
if (!resolveToolPermission) {
|
|
3716
|
+
console.warn("[useAgentChat] resolveToolPermission not provided for tool permission response");
|
|
3717
|
+
return;
|
|
3718
|
+
}
|
|
3719
|
+
await resolveToolPermission(request, allow);
|
|
3720
|
+
}, [canUseTool, resolveToolPermission]);
|
|
3631
3721
|
const createTextEntry = react.useCallback((id, content) => ({
|
|
3632
3722
|
id,
|
|
3633
3723
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3786,6 +3876,7 @@ function useAgentChat(options) {
|
|
|
3786
3876
|
for await (const event of stream) {
|
|
3787
3877
|
if (controller.signal.aborted) break;
|
|
3788
3878
|
eventCountRef.current++;
|
|
3879
|
+
await handleToolPermission(event);
|
|
3789
3880
|
localStreamingEntries = processEvent(event, localStreamingEntries);
|
|
3790
3881
|
emitStreamingEntries(localStreamingEntries);
|
|
3791
3882
|
if (event.type === "complete" || event.type === "session_end" || event.type === "error") {
|
|
@@ -3819,7 +3910,7 @@ function useAgentChat(options) {
|
|
|
3819
3910
|
}
|
|
3820
3911
|
return false;
|
|
3821
3912
|
}
|
|
3822
|
-
}, [subscribeToSession, maxReconnectAttempts, reconnectBaseDelay, onReconnect, processEvent, emitStreamingEntries]);
|
|
3913
|
+
}, [subscribeToSession, maxReconnectAttempts, reconnectBaseDelay, onReconnect, processEvent, emitStreamingEntries, handleToolPermission]);
|
|
3823
3914
|
const send = react.useCallback(async (prompt) => {
|
|
3824
3915
|
if (isStreaming) return;
|
|
3825
3916
|
let finalPrompt = prompt;
|
|
@@ -3898,6 +3989,7 @@ function useAgentChat(options) {
|
|
|
3898
3989
|
console.error("[useAgentChat] onEvent error:", err);
|
|
3899
3990
|
}
|
|
3900
3991
|
}
|
|
3992
|
+
await handleToolPermission(event);
|
|
3901
3993
|
let processedEvent = event;
|
|
3902
3994
|
if (middleware?.length) {
|
|
3903
3995
|
processedEvent = await applyEventMiddleware(middleware, event);
|
|
@@ -3947,7 +4039,7 @@ function useAgentChat(options) {
|
|
|
3947
4039
|
abortControllerRef.current = null;
|
|
3948
4040
|
resetStreamingState();
|
|
3949
4041
|
}
|
|
3950
|
-
}, [isStreaming, sessionId, historyEntries, streamingEntries, createStream, subscribeToSession, processEvent, emitStreamingEntries, resetStreamingState, onError, attemptReconnect, onBeforeSend, onEvent, middleware]);
|
|
4042
|
+
}, [isStreaming, sessionId, historyEntries, streamingEntries, createStream, subscribeToSession, processEvent, emitStreamingEntries, resetStreamingState, onError, attemptReconnect, onBeforeSend, onEvent, middleware, handleToolPermission]);
|
|
3951
4043
|
const stop = react.useCallback(() => {
|
|
3952
4044
|
reconnectAttemptsRef.current = maxReconnectAttempts + 1;
|
|
3953
4045
|
setIsReconnecting(false);
|
|
@@ -3998,6 +4090,8 @@ function useChat(options) {
|
|
|
3998
4090
|
initialSessionId,
|
|
3999
4091
|
initialMessages = [],
|
|
4000
4092
|
onToolCall,
|
|
4093
|
+
canUseTool,
|
|
4094
|
+
resolveToolPermission,
|
|
4001
4095
|
onFinish,
|
|
4002
4096
|
onError,
|
|
4003
4097
|
onSessionStart,
|
|
@@ -4032,6 +4126,23 @@ function useChat(options) {
|
|
|
4032
4126
|
return prev;
|
|
4033
4127
|
});
|
|
4034
4128
|
}, []);
|
|
4129
|
+
const handleToolPermission = react.useCallback(async (event) => {
|
|
4130
|
+
if (event.type !== "tool_permission") return;
|
|
4131
|
+
if (!canUseTool) return;
|
|
4132
|
+
if (!event.requestId || !event.sessionId || !event.toolName) return;
|
|
4133
|
+
const request = {
|
|
4134
|
+
requestId: event.requestId,
|
|
4135
|
+
sessionId: event.sessionId,
|
|
4136
|
+
toolName: event.toolName,
|
|
4137
|
+
input: event.input
|
|
4138
|
+
};
|
|
4139
|
+
const allow = await canUseTool(request);
|
|
4140
|
+
if (!resolveToolPermission) {
|
|
4141
|
+
console.warn("[useChat] resolveToolPermission not provided for tool permission response");
|
|
4142
|
+
return;
|
|
4143
|
+
}
|
|
4144
|
+
await resolveToolPermission(request, allow);
|
|
4145
|
+
}, [canUseTool, resolveToolPermission]);
|
|
4035
4146
|
const processEvent = react.useCallback((event) => {
|
|
4036
4147
|
switch (event.type) {
|
|
4037
4148
|
case "session_start":
|
|
@@ -4151,6 +4262,7 @@ function useChat(options) {
|
|
|
4151
4262
|
const stream = createStream(finalPrompt, sessionIdRef.current || void 0, streamOptions);
|
|
4152
4263
|
for await (const event of stream) {
|
|
4153
4264
|
if (controller.signal.aborted) break;
|
|
4265
|
+
await handleToolPermission(event);
|
|
4154
4266
|
let processedEvent = event;
|
|
4155
4267
|
if (middleware?.length) {
|
|
4156
4268
|
processedEvent = await applyEventMiddleware(middleware, event);
|
|
@@ -4180,7 +4292,7 @@ function useChat(options) {
|
|
|
4180
4292
|
abortControllerRef.current = null;
|
|
4181
4293
|
currentAssistantMessageRef.current = null;
|
|
4182
4294
|
}
|
|
4183
|
-
}, [isLoading, createStream, processEvent, middleware, onError]);
|
|
4295
|
+
}, [isLoading, createStream, processEvent, middleware, onError, handleToolPermission]);
|
|
4184
4296
|
const stop = react.useCallback(() => {
|
|
4185
4297
|
reconnectAttemptsRef.current = maxReconnectAttempts + 1;
|
|
4186
4298
|
setIsReconnecting(false);
|
|
@@ -4335,6 +4447,7 @@ exports.CodeIcon = CodeIcon;
|
|
|
4335
4447
|
exports.Conversation = Conversation;
|
|
4336
4448
|
exports.ConversationContent = ConversationContent;
|
|
4337
4449
|
exports.ConversationEmptyState = ConversationEmptyState;
|
|
4450
|
+
exports.ConversationLoadMore = ConversationLoadMore;
|
|
4338
4451
|
exports.ConversationScrollButton = ConversationScrollButton;
|
|
4339
4452
|
exports.CopyIcon = CopyIcon;
|
|
4340
4453
|
exports.DEFAULT_STYLE_CONFIG = DEFAULT_STYLE_CONFIG;
|