feat: add Ctrl+O activity panel toggle, fix terminal 997;1n garbage on exit

Wire up the activity_toggle keybind (Ctrl+O) to show/hide the activity
panel via new activityVisible store state. Fix terminal garbage text on
exit by draining stdin after renderer teardown to consume pending
DECRQM mode 997 responses before they echo in the shell.
This commit is contained in:
2026-02-14 12:47:53 -05:00
parent 51b5fe1aa1
commit b51e3d49a6
8 changed files with 98 additions and 8 deletions

View File

@@ -286,8 +286,19 @@ function AppContent(props: AppProps) {
return;
}
// Toggle activity panel
if (matchesAction(evt, "activity_toggle")) {
app.toggleActivity();
evt.preventDefault();
return;
}
// Command menu trigger from "/" when input is empty
if (matchesAction(evt, "command_menu") && app.mode() === "idle" && !app.inputBuffer()) {
if (
matchesAction(evt, "command_menu") &&
app.mode() === "idle" &&
!app.inputBuffer()
) {
app.openCommandMenu();
evt.preventDefault();
return;

View File

@@ -44,6 +44,7 @@ interface AppStore {
availableModels: ProviderModel[];
sessionStats: SessionStats;
todosVisible: boolean;
activityVisible: boolean;
debugLogVisible: boolean;
interruptPending: boolean;
exitPending: boolean;
@@ -89,6 +90,7 @@ interface AppContextValue {
availableModels: Accessor<ProviderModel[]>;
sessionStats: Accessor<SessionStats>;
todosVisible: Accessor<boolean>;
activityVisible: Accessor<boolean>;
debugLogVisible: Accessor<boolean>;
interruptPending: Accessor<boolean>;
exitPending: Accessor<boolean>;
@@ -172,6 +174,7 @@ interface AppContextValue {
// UI state actions
toggleTodos: () => void;
toggleActivity: () => void;
toggleDebugLog: () => void;
setInterruptPending: (pending: boolean) => void;
setExitPending: (pending: boolean) => void;
@@ -276,6 +279,7 @@ export const { provider: AppStoreProvider, use: useAppStore } =
availableModels: [],
sessionStats: createInitialSessionStats(),
todosVisible: true,
activityVisible: true,
debugLogVisible: false,
interruptPending: false,
exitPending: false,
@@ -331,6 +335,7 @@ export const { provider: AppStoreProvider, use: useAppStore } =
const availableModels = (): ProviderModel[] => store.availableModels;
const sessionStats = (): SessionStats => store.sessionStats;
const todosVisible = (): boolean => store.todosVisible;
const activityVisible = (): boolean => store.activityVisible;
const debugLogVisible = (): boolean => store.debugLogVisible;
const interruptPending = (): boolean => store.interruptPending;
const exitPending = (): boolean => store.exitPending;
@@ -700,6 +705,10 @@ export const { provider: AppStoreProvider, use: useAppStore } =
setStore("todosVisible", !store.todosVisible);
};
const toggleActivity = (): void => {
setStore("activityVisible", !store.activityVisible);
};
const toggleDebugLog = (): void => {
setStore("debugLogVisible", !store.debugLogVisible);
};
@@ -889,6 +898,7 @@ export const { provider: AppStoreProvider, use: useAppStore } =
availableModels,
sessionStats,
todosVisible,
activityVisible,
debugLogVisible,
interruptPending,
exitPending,
@@ -990,6 +1000,7 @@ export const { provider: AppStoreProvider, use: useAppStore } =
// UI state actions
toggleTodos,
toggleActivity,
toggleDebugLog,
setInterruptPending,
setExitPending,
@@ -1085,6 +1096,7 @@ export const appStore = {
sessionStats: storeRef.sessionStats(),
cascadeEnabled: storeRef.cascadeEnabled(),
todosVisible: storeRef.todosVisible(),
activityVisible: storeRef.activityVisible(),
debugLogVisible: storeRef.debugLogVisible(),
interruptPending: storeRef.interruptPending(),
exitPending: storeRef.exitPending(),
@@ -1217,6 +1229,11 @@ export const appStore = {
storeRef.toggleTodos();
},
toggleActivity: (): void => {
if (!storeRef) return;
storeRef.toggleActivity();
},
toggleDebugLog: (): void => {
if (!storeRef) return;
storeRef.toggleDebugLog();

View File

@@ -282,7 +282,9 @@ export function Session(props: SessionProps) {
<LogPanel />
</box>
<ActivityPanel />
<Show when={app.activityVisible()}>
<ActivityPanel />
</Show>
<Show when={app.todosVisible() && props.plan}>
<TodoPanel plan={props.plan ?? null} visible={app.todosVisible()} />
@@ -303,9 +305,7 @@ export function Session(props: SessionProps) {
/>
</Show>
<Show
when={app.mode() === "plan_approval" && app.planApprovalPrompt()}
>
<Show when={app.mode() === "plan_approval" && app.planApprovalPrompt()}>
<PlanApprovalModal
prompt={app.planApprovalPrompt()!}
onRespond={props.onPlanApprovalResponse}
@@ -314,7 +314,11 @@ export function Session(props: SessionProps) {
</Show>
<StatusBar />
<Show when={app.mode() !== "permission_prompt" && app.mode() !== "plan_approval"}>
<Show
when={
app.mode() !== "permission_prompt" && app.mode() !== "plan_approval"
}
>
<InputArea onSubmit={props.onSubmit} />
</Show>