Repository URL to install this package:
|
Version:
0.4.41 ▾
|
import React, { useCallback, useEffect, useState } from "react";
import { Box, Text, useInput, useStdout } from "ink";
import { api } from "../rpc.js";
import { Panel } from "../components/Panel.js";
import { HelpOverlay } from "../components/HelpOverlay.js";
const ROLE_COLORS = {
user: "cyan",
assistant: "green",
tool: "yellow",
system: "magenta",
};
const HELP_HINTS = [
{ key: "j/k", label: "navigate messages" },
{ key: "g", label: "jump to top" },
{ key: "G", label: "jump to bottom" },
{ key: "r", label: "refresh" },
{ key: "?", label: "help" },
{ key: "esc", label: "back to ticket" },
];
const BAR_HINTS = [
{ key: "j/k", label: "navigate" },
{ key: "g/G", label: "top/bottom" },
{ key: "r", label: "refresh" },
{ key: "?", label: "help" },
{ key: "esc", label: "back" },
];
export function LogViewer({ ticketId, ticketTitle, projectId, onBack, onQuit, onHints, onStatus }) {
const [messages, setMessages] = useState([]);
const [selected, setSelected] = useState(0);
const [scrollOffset, setScrollOffset] = useState(0);
const [status, setStatus] = useState(null);
const [loading, setLoading] = useState(true);
const [showHelp, setShowHelp] = useState(false);
const { stdout } = useStdout();
const visibleRows = (stdout?.rows ?? 24) - 6;
const refresh = useCallback(async () => {
try {
const result = await api.ticketHistory(ticketId, projectId, 200);
setMessages(result);
setLoading(false);
}
catch (err) {
setStatus(`Error: ${err instanceof Error ? err.message : String(err)}`);
setLoading(false);
}
}, [ticketId, projectId]);
useEffect(() => {
refresh();
const interval = setInterval(refresh, 10000);
return () => clearInterval(interval);
}, [refresh]);
useEffect(() => {
if (!status)
return;
const timer = setTimeout(() => setStatus(null), 4000);
return () => clearTimeout(timer);
}, [status]);
// Report status/hints to parent for app-level StatusBar
useEffect(() => { onStatus?.(status ?? null); }, [status]);
useEffect(() => { onHints?.(BAR_HINTS); }, []);
useInput((input, key) => {
if (showHelp)
return;
if (input === "?") {
setShowHelp(true);
return;
}
if (key.escape) {
onBack();
return;
}
if (input === "q") {
onQuit();
return;
}
if (input === "r") {
setLoading(true);
refresh();
setStatus("Refreshed");
return;
}
if ((key.upArrow || input === "k") && messages.length > 0) {
setSelected((v) => {
const next = Math.max(0, v - 1);
if (next < scrollOffset)
setScrollOffset(next);
return next;
});
return;
}
if ((key.downArrow || input === "j") && messages.length > 0) {
setSelected((v) => {
const next = Math.min(messages.length - 1, v + 1);
if (next >= scrollOffset + visibleRows)
setScrollOffset(next - visibleRows + 1);
return next;
});
return;
}
if (input === "G") {
const last = messages.length - 1;
setSelected(last);
setScrollOffset(Math.max(0, last - visibleRows + 1));
return;
}
if (input === "g") {
setSelected(0);
setScrollOffset(0);
return;
}
});
if (showHelp) {
return (React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
React.createElement(HelpOverlay, { title: "Log Viewer", hints: HELP_HINTS, onClose: () => setShowHelp(false) })));
}
const visibleMessages = messages.slice(scrollOffset, scrollOffset + visibleRows);
return (React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
React.createElement(Panel, { title: `Log \u2014 ${ticketTitle} (${messages.length})`, focused: true, flexGrow: 1 },
React.createElement(Box, { flexDirection: "column", paddingX: 1 }, loading && messages.length === 0 ? (React.createElement(Text, { color: "gray" }, "Loading history...")) : messages.length === 0 ? (React.createElement(Text, { color: "gray" }, "No history found.")) : (visibleMessages.map((msg, i) => {
const actualIndex = scrollOffset + i;
const isSelected = actualIndex === selected;
const roleColor = ROLE_COLORS[msg.role] ?? "white";
const preview = isSelected
? msg.content
: msg.content.length > 120 ? msg.content.slice(0, 120) + "\u2026" : msg.content;
const lines = preview.split("\n");
const displayContent = isSelected ? lines.join("\n") : lines[0] + (lines.length > 1 ? " \u2026" : "");
return (React.createElement(Text, { key: msg.index, color: isSelected ? "cyan" : undefined, wrap: "truncate" },
isSelected ? "\u25B8 " : " ",
React.createElement(Text, { color: roleColor, bold: true },
"[",
msg.role,
"]"),
" ",
displayContent));
}))))));
}
//# sourceMappingURL=LogViewer.js.map