feat: harden runtime readiness and session workflow
This commit is contained in:
@@ -15,13 +15,24 @@ interface Props {
|
||||
sessionId: string | null;
|
||||
/** Called with the new session id when the box starts a fresh session. */
|
||||
onSessionCreated?: (id: string) => void;
|
||||
/** Paint the provisional session view before the create request completes. */
|
||||
onSessionCreating?: (question: string) => void;
|
||||
/** Revert the provisional view when session creation fails. */
|
||||
onSessionCreateFailed?: () => void;
|
||||
/** Interrupt the running session, persisting its state. */
|
||||
onStop?: () => void;
|
||||
/** Lets the parent focus the composer (e.g. on "New session"). */
|
||||
inputRef?: React.Ref<HTMLTextAreaElement>;
|
||||
}
|
||||
|
||||
export function SteerInput({ sessionId, onSessionCreated, onStop, inputRef }: Props) {
|
||||
export function SteerInput({
|
||||
sessionId,
|
||||
onSessionCreated,
|
||||
onSessionCreating,
|
||||
onSessionCreateFailed,
|
||||
onStop,
|
||||
inputRef,
|
||||
}: Props) {
|
||||
const [text, setText] = useState("");
|
||||
const [busy, setBusy] = useState(false);
|
||||
const setLastUserEntry = useSessionStore((s) => s.setLastUserEntry);
|
||||
@@ -48,16 +59,29 @@ export function SteerInput({ sessionId, onSessionCreated, onStop, inputRef }: Pr
|
||||
const trimmed = text.trim();
|
||||
if (!trimmed || busy) return;
|
||||
setBusy(true);
|
||||
try {
|
||||
if (sessionId) {
|
||||
if (sessionId) {
|
||||
try {
|
||||
await postSteer(sessionId, trimmed);
|
||||
} else {
|
||||
const { id } = await createSession({ question: trimmed });
|
||||
setPhase("F1"); // optimistic: paint F1 yellow during the cold start, before the first gate
|
||||
onSessionCreated?.(id);
|
||||
setLastUserEntry({ kind: "input", text: trimmed });
|
||||
setText("");
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
setLastUserEntry({ kind: "input", text: trimmed });
|
||||
return;
|
||||
}
|
||||
|
||||
// Session creation can include cold-start work. Hand the UI state to the
|
||||
// provisional session view before awaiting it so feedback is immediate.
|
||||
setPhase("F1");
|
||||
setLastUserEntry({ kind: "input", text: trimmed });
|
||||
onSessionCreating?.(trimmed);
|
||||
try {
|
||||
const { id } = await createSession({ question: trimmed });
|
||||
onSessionCreated?.(id);
|
||||
setText("");
|
||||
} catch {
|
||||
// Keep the question in the composer so retrying does not require retyping.
|
||||
onSessionCreateFailed?.();
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -92,7 +116,8 @@ export function SteerInput({ sessionId, onSessionCreated, onStop, inputRef }: Pr
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={sessionId ? "Free steering (!)…" : "Ask a question to start a new session…"}
|
||||
aria-label={sessionId ? "Steering" : "New question"}
|
||||
className="max-h-40 flex-1 resize-none bg-transparent px-1 py-1 text-sm leading-relaxed outline-none placeholder:text-muted-foreground"
|
||||
data-awaiting-input={sessionId ? undefined : "true"}
|
||||
className={["max-h-40 flex-1 resize-none rounded-lg px-1 py-1 text-sm leading-relaxed outline-none placeholder:text-muted-foreground", sessionId ? "bg-transparent" : "thot-awaiting-input"].join(" ")}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user