Update messaggio in area input

This commit is contained in:
Samuele E. Locatelli
2025-09-04 10:54:31 +00:00
parent 83503d61dd
commit 343d5cb6e4
+108 -24
View File
@@ -1,47 +1,79 @@
// src/ChatInput.jsx
import React, { useState, useEffect, useRef } from "react";
const MAX_EXECUTION_TIME_MS = 2 * 60 * 1000; // 2 minuti
const MAX_EXECUTION_TIME_MS_DEFAULT = 2 * 60 * 1000; // 2 minuti
const EXTRA_TIME_MS = 60 * 1000;
const CHAR_LIMIT_FOR_TEXTAREA = 80;
const LINE_LIMIT_FOR_TEXTAREA = 1;
export default function ChatInput({ onSend, onStop, loading }) {
const [inputValue, setInputValue] = useState("");
const [timeLeft, setTimeLeft] = useState(null);
const [elapsedTime, setElapsedTime] = useState(0);
const [maxExecutionTime, setMaxExecutionTime] = useState(MAX_EXECUTION_TIME_MS_DEFAULT);
const stopTimerRef = useRef(null);
const countdownIntervalRef = useRef(null);
const elapsedIntervalRef = useRef(null);
const inputRef = useRef(null);
// Gestione auto-stop e countdown
const isTextarea =
inputValue.length > CHAR_LIMIT_FOR_TEXTAREA ||
inputValue.split("\n").length > LINE_LIMIT_FOR_TEXTAREA;
// Focus automatico
useEffect(() => {
if (!loading && inputRef.current) {
inputRef.current.focus();
const val = inputRef.current.value;
inputRef.current.setSelectionRange(val.length, val.length);
}
}, [isTextarea, loading]);
// Autoresize
useEffect(() => {
if (isTextarea && inputRef.current) {
inputRef.current.style.height = "auto";
inputRef.current.style.height = inputRef.current.scrollHeight + "px";
}
}, [inputValue, isTextarea]);
// Gestione timer e countdown
useEffect(() => {
if (loading) {
setTimeLeft(Math.floor(MAX_EXECUTION_TIME_MS / 1000));
setTimeLeft(Math.floor(maxExecutionTime / 1000));
setElapsedTime(0);
stopTimerRef.current = setTimeout(() => {
console.warn("⏱ Tempo massimo raggiunto, stop automatico");
onStop();
}, MAX_EXECUTION_TIME_MS);
}, maxExecutionTime);
countdownIntervalRef.current = setInterval(() => {
setTimeLeft(prev => {
if (prev === null) return null;
if (prev <= 1) {
clearInterval(countdownIntervalRef.current);
return 0;
}
return prev - 1;
});
setTimeLeft(prev => (prev > 0 ? prev - 1 : 0));
}, 1000);
elapsedIntervalRef.current = setInterval(() => {
setElapsedTime(prev => prev + 1);
}, 1000);
} else {
clearTimeout(stopTimerRef.current);
clearInterval(countdownIntervalRef.current);
clearInterval(elapsedIntervalRef.current);
stopTimerRef.current = null;
countdownIntervalRef.current = null;
elapsedIntervalRef.current = null;
setTimeLeft(null);
setElapsedTime(0);
setMaxExecutionTime(MAX_EXECUTION_TIME_MS_DEFAULT);
}
return () => {
clearTimeout(stopTimerRef.current);
clearInterval(countdownIntervalRef.current);
clearInterval(elapsedIntervalRef.current);
};
}, [loading, onStop]);
}, [loading, maxExecutionTime, onStop]);
const handleSend = () => {
if (inputValue.trim()) {
@@ -50,25 +82,77 @@ export default function ChatInput({ onSend, onStop, loading }) {
}
};
const handleAddMinute = () => {
setMaxExecutionTime(prev => prev + EXTRA_TIME_MS);
setTimeLeft(prev => (prev !== null ? prev + 60 : prev));
clearTimeout(stopTimerRef.current);
stopTimerRef.current = setTimeout(() => {
console.warn("⏱ Tempo massimo raggiunto, stop automatico");
onStop();
}, maxExecutionTime + EXTRA_TIME_MS);
};
const handleKeyDown = (e) => {
if (e.key === "Enter") {
if (e.shiftKey) {
e.preventDefault();
const { selectionStart, selectionEnd } = e.target;
const newValue =
inputValue.substring(0, selectionStart) +
"\n" +
inputValue.substring(selectionEnd);
setInputValue(newValue);
requestAnimationFrame(() => {
inputRef.current.selectionStart = inputRef.current.selectionEnd = selectionStart + 1;
});
} else {
e.preventDefault();
handleSend();
}
}
};
return (
<div className="chat-input-container p-2 border-top d-flex align-items-center">
<input
type="text"
className="form-control me-2"
placeholder="Scrivi un messaggio..."
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
disabled={loading}
onKeyDown={(e) => e.key === "Enter" && handleSend()}
/>
{loading ? (
<div className="flex-grow-1 me-2 p-2 bg-light rounded small text-muted">
💬 Il modello sta processando (tempo trascorso: {elapsedTime}s)
</div>
) : isTextarea ? (
<textarea
ref={inputRef}
className="form-control me-2"
placeholder="Scrivi un messaggio..."
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={handleKeyDown}
rows={3}
style={{ resize: "none", overflow: "hidden" }}
/>
) : (
<input
ref={inputRef}
type="text"
className="form-control me-2"
placeholder="Scrivi un messaggio..."
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={handleKeyDown}
/>
)}
{loading ? (
<div className="d-flex align-items-center">
<button className="btn btn-warning btn-sm me-2" onClick={onStop}>
<button className="btn btn-danger btn-sm me-2" onClick={onStop}>
Stop
</button>
<button className="btn btn-secondary btn-sm me-2" onClick={handleAddMinute}>
+1 min
</button>
{timeLeft !== null && (
<span className="text-muted small">
<span
className={`small fw-bold ${timeLeft <= 10 ? "text-danger" : "text-muted"}`}
>
{timeLeft}s
</span>
)}