feat: show update notification in TUI and footer (#1204)

This commit is contained in:
cthomas
2026-02-27 15:03:47 -08:00
committed by GitHub
parent bb5d7d0a39
commit 841e2332f3
5 changed files with 100 additions and 8 deletions

View File

@@ -967,6 +967,7 @@ export default function App({
showCompactions = false,
agentProvenance = null,
releaseNotes = null,
updateNotification = null,
sessionContextReminderEnabled = true,
}: {
agentId: string;
@@ -988,6 +989,7 @@ export default function App({
showCompactions?: boolean;
agentProvenance?: AgentProvenance | null;
releaseNotes?: string | null; // Markdown release notes to display above header
updateNotification?: string | null; // Latest version when a significant auto-update was applied
sessionContextReminderEnabled?: boolean;
}) {
// Warm the model-access cache in the background so /model is fast on first open.
@@ -1707,6 +1709,31 @@ export default function App({
// Static items (things that are done rendering and can be frozen)
const [staticItems, setStaticItems] = useState<StaticItem[]>([]);
// Show in-transcript notification when auto-update applied a significant new version
const [footerUpdateText, setFooterUpdateText] = useState<string | null>(null);
useEffect(() => {
if (!updateNotification) return;
setStaticItems((prev) => {
if (prev.some((item) => item.id === "update-notification")) return prev;
return [
...prev,
{
kind: "status" as const,
id: "update-notification",
lines: [
`A new version of Letta Code is available (**${updateNotification}**). Restart to update!`,
],
},
];
});
// Also show briefly in the footer placeholder area
setFooterUpdateText(
`New version available (${updateNotification}). Restart to update!`,
);
const timer = setTimeout(() => setFooterUpdateText(null), 8000);
return () => clearTimeout(timer);
}, [updateNotification]);
// Track committed ids to avoid duplicates
const emittedIdsRef = useRef<Set<string>>(new Set());
@@ -12743,6 +12770,7 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
statusLineRight={statusLine.rightText || undefined}
statusLinePadding={statusLine.padding || 0}
statusLinePrompt={statusLine.prompt}
footerNotification={footerUpdateText}
/>
</Box>

View File

@@ -234,6 +234,7 @@ const InputFooter = memo(function InputFooter({
statusLineText,
statusLineRight,
statusLinePadding,
footerNotification,
}: {
ctrlCPressed: boolean;
escapePressed: boolean;
@@ -252,6 +253,7 @@ const InputFooter = memo(function InputFooter({
statusLineText?: string;
statusLineRight?: string;
statusLinePadding?: number;
footerNotification?: string | null;
}) {
const hideFooterContent = hideFooter;
const maxAgentChars = Math.max(10, Math.floor(rightColumnWidth * 0.45));
@@ -328,6 +330,10 @@ const InputFooter = memo(function InputFooter({
(shift+tab to {showExitHint ? "exit" : "cycle"})
</Text>
</Text>
) : footerNotification ? (
<Text color={colors.status.processingShimmer}>
{footerNotification}
</Text>
) : (
<Text dimColor>Press / for commands</Text>
)}
@@ -621,6 +627,7 @@ export function Input({
statusLinePadding = 0,
statusLinePrompt,
onCycleReasoningEffort,
footerNotification,
}: {
visible?: boolean;
streaming: boolean;
@@ -662,6 +669,7 @@ export function Input({
statusLinePadding?: number;
statusLinePrompt?: string;
onCycleReasoningEffort?: () => void;
footerNotification?: string | null;
}) {
const [value, setValue] = useState("");
const [escapePressed, setEscapePressed] = useState(false);
@@ -1484,6 +1492,7 @@ export function Input({
statusLineText={statusLineText}
statusLineRight={statusLineRight}
statusLinePadding={statusLinePadding}
footerNotification={footerNotification}
/>
)}
</Box>
@@ -1530,6 +1539,7 @@ export function Input({
statusLineText,
statusLineRight,
statusLinePadding,
footerNotification,
promptChar,
promptVisualWidth,
suppressDividers,