@emblemvault/hustle-react 1.3.0 → 1.4.0

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.js CHANGED
@@ -2006,7 +2006,7 @@ var screenshotExecutor = async (args2) => {
2006
2006
  if (!window.html2canvas) {
2007
2007
  await new Promise((resolve, reject) => {
2008
2008
  const script = document.createElement("script");
2009
- script.src = "https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js";
2009
+ script.src = "https://cdn.jsdelivr.net/npm/html2canvas-pro@1.5.0/dist/html2canvas-pro.min.js";
2010
2010
  script.onload = () => resolve();
2011
2011
  script.onerror = () => reject(new Error("Failed to load html2canvas"));
2012
2012
  document.head.appendChild(script);
@@ -3715,7 +3715,11 @@ var styles = {
3715
3715
  border: "none",
3716
3716
  borderRadius: 0,
3717
3717
  color: tokens.colors.textTertiary,
3718
- flexShrink: 0
3718
+ flexShrink: 0,
3719
+ display: "flex",
3720
+ alignItems: "center",
3721
+ justifyContent: "center",
3722
+ cursor: "pointer"
3719
3723
  },
3720
3724
  inputWrapper: {
3721
3725
  flex: 1
@@ -3937,6 +3941,7 @@ function HustleChat({
3937
3941
  isLoading,
3938
3942
  error: error2,
3939
3943
  models,
3944
+ client,
3940
3945
  chatStream,
3941
3946
  uploadFile,
3942
3947
  selectedModel,
@@ -3963,6 +3968,7 @@ function HustleChat({
3963
3968
  const [showSettingsPanel, setShowSettingsPanel] = useState(false);
3964
3969
  const messagesEndRef = useRef(null);
3965
3970
  const fileInputRef = useRef(null);
3971
+ const messagesRef = useRef(messages);
3966
3972
  useEffect(() => {
3967
3973
  if (initialSystemPrompt && !systemPrompt) {
3968
3974
  setSystemPrompt(initialSystemPrompt);
@@ -3971,6 +3977,9 @@ function HustleChat({
3971
3977
  useEffect(() => {
3972
3978
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
3973
3979
  }, [messages]);
3980
+ useEffect(() => {
3981
+ messagesRef.current = messages;
3982
+ }, [messages]);
3974
3983
  const handleFileSelect = useCallback(
3975
3984
  async (e) => {
3976
3985
  const files = e.target.files;
@@ -3992,6 +4001,96 @@ function HustleChat({
3992
4001
  const removeAttachment = useCallback((index) => {
3993
4002
  setAttachments((prev) => prev.filter((_, i) => i !== index));
3994
4003
  }, []);
4004
+ const sendContinue = useCallback(async () => {
4005
+ if (isStreaming || !isReady) return;
4006
+ console.log("[AUTO_CONTINUE] Sending continue message...");
4007
+ const userMessage = {
4008
+ id: generateId(),
4009
+ role: "user",
4010
+ content: "continue"
4011
+ };
4012
+ setMessages((prev) => [...prev, userMessage]);
4013
+ onMessage?.(userMessage);
4014
+ const assistantMessage = {
4015
+ id: generateId(),
4016
+ role: "assistant",
4017
+ content: "",
4018
+ isStreaming: true,
4019
+ toolCalls: []
4020
+ };
4021
+ setMessages((prev) => [...prev, assistantMessage]);
4022
+ setIsStreaming(true);
4023
+ setCurrentToolCalls([]);
4024
+ try {
4025
+ const chatMessages = messagesRef.current.filter((m) => !m.isStreaming).map((m) => ({ role: m.role, content: m.content }));
4026
+ chatMessages.push({ role: "user", content: "continue" });
4027
+ const stream = chatStream({
4028
+ messages: chatMessages,
4029
+ processChunks: true
4030
+ });
4031
+ let fullContent = "";
4032
+ const toolCallsAccumulated = [];
4033
+ for await (const chunk of stream) {
4034
+ if (chunk.type === "text") {
4035
+ fullContent += chunk.value;
4036
+ setMessages(
4037
+ (prev) => prev.map(
4038
+ (m) => m.id === assistantMessage.id ? { ...m, content: fullContent } : m
4039
+ )
4040
+ );
4041
+ } else if (chunk.type === "tool_call") {
4042
+ const toolCall = chunk.value;
4043
+ toolCallsAccumulated.push(toolCall);
4044
+ setCurrentToolCalls([...toolCallsAccumulated]);
4045
+ setMessages(
4046
+ (prev) => prev.map(
4047
+ (m) => m.id === assistantMessage.id ? { ...m, toolCalls: [...toolCallsAccumulated] } : m
4048
+ )
4049
+ );
4050
+ onToolCall?.(toolCall);
4051
+ } else if (chunk.type === "error") {
4052
+ console.error("Stream error:", chunk.value);
4053
+ }
4054
+ }
4055
+ const processedResponse = await stream.response;
4056
+ const finalContent = processedResponse?.content || fullContent || "(No response)";
4057
+ setMessages(
4058
+ (prev) => prev.map(
4059
+ (m) => m.id === assistantMessage.id ? { ...m, isStreaming: false, content: finalContent } : m
4060
+ )
4061
+ );
4062
+ onResponse?.(finalContent);
4063
+ } catch (err) {
4064
+ console.error("Continue error:", err);
4065
+ setMessages(
4066
+ (prev) => prev.map(
4067
+ (m) => m.id === assistantMessage.id ? { ...m, isStreaming: false, content: `Error: ${err instanceof Error ? err.message : "Unknown error"}` } : m
4068
+ )
4069
+ );
4070
+ } finally {
4071
+ setIsStreaming(false);
4072
+ setCurrentToolCalls([]);
4073
+ }
4074
+ }, [isStreaming, isReady, chatStream, onMessage, onToolCall, onResponse]);
4075
+ useEffect(() => {
4076
+ if (!client) return;
4077
+ const unsubMaxTools = client.on("max_tools_reached", (event) => {
4078
+ console.log(`[AUTO_CONTINUE] Max tools reached (${event.toolsExecuted}/${event.maxSteps}), auto-continuing...`);
4079
+ setTimeout(() => {
4080
+ sendContinue();
4081
+ }, 100);
4082
+ });
4083
+ const unsubTimeout = client.on("timeout", (event) => {
4084
+ console.log(`[AUTO_CONTINUE] Timeout: ${event.message}, auto-continuing...`);
4085
+ setTimeout(() => {
4086
+ sendContinue();
4087
+ }, 100);
4088
+ });
4089
+ return () => {
4090
+ unsubMaxTools();
4091
+ unsubTimeout();
4092
+ };
4093
+ }, [client, sendContinue]);
3995
4094
  const sendMessage = useCallback(async () => {
3996
4095
  const content = inputValue.trim();
3997
4096
  if (!content || isStreaming || !isReady) return;