UNPKG

askeroo

Version:

A modern CLI prompt library with flow control, history navigation, and conditional prompts

124 lines 5.79 kB
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime"; import { useEffect, useState } from "react"; import { Box, Text } from "ink"; import { streamStore } from "./stream-store.js"; // Main component for the stream plugin export const StreamDisplay = ({ node, options, events, }) => { const [spinnerFrame, setSpinnerFrame] = useState(0); // Use the stream ID from options (should always be provided) const streamId = options.streamId || `stream_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; // Subscribe to the stream store - auto-updates on any store change const store = streamStore.use(); // Extract data for this stream const streamState = store.streams.get(streamId) || { status: "active", lines: [], }; // Animated spinner frames (same as tasks) const spinnerFrames = ["⠂", "-", "–", "—", "–", "-"]; // Animate spinner only when active/streaming useEffect(() => { if (streamState.status !== "active") { return; } const interval = setInterval(() => { setSpinnerFrame((prev) => (prev + 1) % spinnerFrames.length); }, 150); return () => clearInterval(interval); }, [streamState.status]); // Auto-submit immediately (to avoid blocking runtime) // This allows the runtime to proceed while the stream continues updating in the background // Use deferCompletion to prevent the node from being marked as completed immediately useEffect(() => { if (node.state === "active" && events.onSubmit) { // Submit immediately to allow runtime to continue, but defer completion events.onSubmit({ type: "auto", deferCompletion: true }); } }, [node.state, events.onSubmit]); // Mark node as completed when stream finishes useEffect(() => { if ((streamState.status === "completed" || streamState.status === "error") && events.onComplete) { // If there's a submitDelay, wait for it before marking as complete if (options.submitDelay && options.submitDelay > 0) { const timer = setTimeout(() => { events.onComplete(); // PluginWrapper handles the promptId }, options.submitDelay); return () => clearTimeout(timer); } else { // Mark as complete immediately events.onComplete(); // PluginWrapper handles the promptId } } }, [streamState.status, events.onComplete, options.submitDelay]); // Track whether we should hide after delay const [shouldHideAfterDelay, setShouldHideAfterDelay] = useState(false); // Handle delayed hiding when stream completes with submitDelay useEffect(() => { // Only applies when hideOnCompletion is true and there's a submitDelay if (!options.hideOnCompletion || !options.submitDelay || options.submitDelay === 0) { return; } // When stream finishes, wait for submitDelay then trigger hiding if (streamState.status === "completed" || streamState.status === "error") { const timer = setTimeout(() => { setShouldHideAfterDelay(true); }, options.submitDelay); return () => clearTimeout(timer); } }, [streamState.status, options.submitDelay, options.hideOnCompletion]); // Hide if hideOnCompletion is true and conditions are met if (options.hideOnCompletion && node.state === "completed") { const streamFinished = streamState.status === "completed" || streamState.status === "error"; // If stream finished and either no delay or delay has elapsed if (streamFinished) { // No delay: hide immediately if (!options.submitDelay || options.submitDelay === 0) { return null; } // With delay: hide after delay timer completes if (shouldHideAfterDelay) { return null; } } } // Get the lines to display (respect maxLines if set) const linesToDisplay = options.maxLines ? streamState.lines.slice(-options.maxLines) : streamState.lines; // Determine status symbol (without color) const getStatusSymbol = (status) => { if (status === "completed") return "■"; if (status === "error") return "✗"; return spinnerFrames[spinnerFrame]; // active - animated }; // Determine status color const getStatusColor = (status) => { if (status === "completed") return "green"; if (status === "error") return "red"; return "blue"; // active }; // Get label if set const label = streamState.label || options.label; return (_jsxs(Box, { flexDirection: "column", children: [label && (_jsxs(Text, { color: getStatusColor(streamState.status), children: [getStatusSymbol(streamState.status), " ", label] })), linesToDisplay.map((line, index) => { const actualLineNumber = options.maxLines ? streamState.lines.length - linesToDisplay.length + index + 1 : index + 1; return (_jsxs(Box, { children: [options.showLineNumbers && (_jsxs(Text, { dimColor: true, children: [actualLineNumber.toString().padStart(3), " "] })), options.prefixSymbol && (_jsxs(Text, { dimColor: true, children: [options.prefixSymbol, " "] })), _jsx(Text, { children: line })] }, `${streamId}-${actualLineNumber}`)); })] })); }; //# sourceMappingURL=Stream.js.map