@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.cjs CHANGED
@@ -2032,7 +2032,7 @@ var screenshotExecutor = async (args2) => {
2032
2032
  if (!window.html2canvas) {
2033
2033
  await new Promise((resolve, reject) => {
2034
2034
  const script = document.createElement("script");
2035
- script.src = "https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js";
2035
+ script.src = "https://cdn.jsdelivr.net/npm/html2canvas-pro@1.5.0/dist/html2canvas-pro.min.js";
2036
2036
  script.onload = () => resolve();
2037
2037
  script.onerror = () => reject(new Error("Failed to load html2canvas"));
2038
2038
  document.head.appendChild(script);
@@ -3741,7 +3741,11 @@ var styles = {
3741
3741
  border: "none",
3742
3742
  borderRadius: 0,
3743
3743
  color: tokens.colors.textTertiary,
3744
- flexShrink: 0
3744
+ flexShrink: 0,
3745
+ display: "flex",
3746
+ alignItems: "center",
3747
+ justifyContent: "center",
3748
+ cursor: "pointer"
3745
3749
  },
3746
3750
  inputWrapper: {
3747
3751
  flex: 1
@@ -3963,6 +3967,7 @@ function HustleChat({
3963
3967
  isLoading,
3964
3968
  error: error2,
3965
3969
  models,
3970
+ client,
3966
3971
  chatStream,
3967
3972
  uploadFile,
3968
3973
  selectedModel,
@@ -3989,6 +3994,7 @@ function HustleChat({
3989
3994
  const [showSettingsPanel, setShowSettingsPanel] = react.useState(false);
3990
3995
  const messagesEndRef = react.useRef(null);
3991
3996
  const fileInputRef = react.useRef(null);
3997
+ const messagesRef = react.useRef(messages);
3992
3998
  react.useEffect(() => {
3993
3999
  if (initialSystemPrompt && !systemPrompt) {
3994
4000
  setSystemPrompt(initialSystemPrompt);
@@ -3997,6 +4003,9 @@ function HustleChat({
3997
4003
  react.useEffect(() => {
3998
4004
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
3999
4005
  }, [messages]);
4006
+ react.useEffect(() => {
4007
+ messagesRef.current = messages;
4008
+ }, [messages]);
4000
4009
  const handleFileSelect = react.useCallback(
4001
4010
  async (e) => {
4002
4011
  const files = e.target.files;
@@ -4018,6 +4027,96 @@ function HustleChat({
4018
4027
  const removeAttachment = react.useCallback((index) => {
4019
4028
  setAttachments((prev) => prev.filter((_, i) => i !== index));
4020
4029
  }, []);
4030
+ const sendContinue = react.useCallback(async () => {
4031
+ if (isStreaming || !isReady) return;
4032
+ console.log("[AUTO_CONTINUE] Sending continue message...");
4033
+ const userMessage = {
4034
+ id: generateId(),
4035
+ role: "user",
4036
+ content: "continue"
4037
+ };
4038
+ setMessages((prev) => [...prev, userMessage]);
4039
+ onMessage?.(userMessage);
4040
+ const assistantMessage = {
4041
+ id: generateId(),
4042
+ role: "assistant",
4043
+ content: "",
4044
+ isStreaming: true,
4045
+ toolCalls: []
4046
+ };
4047
+ setMessages((prev) => [...prev, assistantMessage]);
4048
+ setIsStreaming(true);
4049
+ setCurrentToolCalls([]);
4050
+ try {
4051
+ const chatMessages = messagesRef.current.filter((m) => !m.isStreaming).map((m) => ({ role: m.role, content: m.content }));
4052
+ chatMessages.push({ role: "user", content: "continue" });
4053
+ const stream = chatStream({
4054
+ messages: chatMessages,
4055
+ processChunks: true
4056
+ });
4057
+ let fullContent = "";
4058
+ const toolCallsAccumulated = [];
4059
+ for await (const chunk of stream) {
4060
+ if (chunk.type === "text") {
4061
+ fullContent += chunk.value;
4062
+ setMessages(
4063
+ (prev) => prev.map(
4064
+ (m) => m.id === assistantMessage.id ? { ...m, content: fullContent } : m
4065
+ )
4066
+ );
4067
+ } else if (chunk.type === "tool_call") {
4068
+ const toolCall = chunk.value;
4069
+ toolCallsAccumulated.push(toolCall);
4070
+ setCurrentToolCalls([...toolCallsAccumulated]);
4071
+ setMessages(
4072
+ (prev) => prev.map(
4073
+ (m) => m.id === assistantMessage.id ? { ...m, toolCalls: [...toolCallsAccumulated] } : m
4074
+ )
4075
+ );
4076
+ onToolCall?.(toolCall);
4077
+ } else if (chunk.type === "error") {
4078
+ console.error("Stream error:", chunk.value);
4079
+ }
4080
+ }
4081
+ const processedResponse = await stream.response;
4082
+ const finalContent = processedResponse?.content || fullContent || "(No response)";
4083
+ setMessages(
4084
+ (prev) => prev.map(
4085
+ (m) => m.id === assistantMessage.id ? { ...m, isStreaming: false, content: finalContent } : m
4086
+ )
4087
+ );
4088
+ onResponse?.(finalContent);
4089
+ } catch (err) {
4090
+ console.error("Continue error:", err);
4091
+ setMessages(
4092
+ (prev) => prev.map(
4093
+ (m) => m.id === assistantMessage.id ? { ...m, isStreaming: false, content: `Error: ${err instanceof Error ? err.message : "Unknown error"}` } : m
4094
+ )
4095
+ );
4096
+ } finally {
4097
+ setIsStreaming(false);
4098
+ setCurrentToolCalls([]);
4099
+ }
4100
+ }, [isStreaming, isReady, chatStream, onMessage, onToolCall, onResponse]);
4101
+ react.useEffect(() => {
4102
+ if (!client) return;
4103
+ const unsubMaxTools = client.on("max_tools_reached", (event) => {
4104
+ console.log(`[AUTO_CONTINUE] Max tools reached (${event.toolsExecuted}/${event.maxSteps}), auto-continuing...`);
4105
+ setTimeout(() => {
4106
+ sendContinue();
4107
+ }, 100);
4108
+ });
4109
+ const unsubTimeout = client.on("timeout", (event) => {
4110
+ console.log(`[AUTO_CONTINUE] Timeout: ${event.message}, auto-continuing...`);
4111
+ setTimeout(() => {
4112
+ sendContinue();
4113
+ }, 100);
4114
+ });
4115
+ return () => {
4116
+ unsubMaxTools();
4117
+ unsubTimeout();
4118
+ };
4119
+ }, [client, sendContinue]);
4021
4120
  const sendMessage = react.useCallback(async () => {
4022
4121
  const content = inputValue.trim();
4023
4122
  if (!content || isStreaming || !isReady) return;