Watch
0
0
Fork
You've already forked hyperhive
0

agent ui: add a clickable new-session menu entry

Same gap #4611 fixed for logout: the Preact rewrite's StatusChips menu
never picked up new-session as a click path, only /new-session typed
twice into the terminal. postNewSession already existed in
termActions.ts. Wire it into the status menu with the same arm-then-
confirm click pattern logout/cancel-turn already use.

Closes #4612
This commit is contained in:
iris 2026-09-28 12:14:17 +02:00 • committed by mara
commit 9a444c27fa
2 changed files with 43 additions and 2 deletions

View file

@ -27,7 +27,11 @@ import { fmtAge, fmtTokens } from "./lib/format.js";
import { resolveDashboardBase } from "./lib/dashboardBase.js";
import { submitPauseResume } from "./lib/pauseAction.js";
import { postModel, postEffort } from "./lib/modelEffort.js";
import { postCancelTurn, postLogout } from "./lib/termActions.js";
import {
postCancelTurn,
postLogout,
postNewSession,
} from "./lib/termActions.js";
import type { TokenUsage } from "./types.js";
type OpenPanel = "inbox" | "todos" | null;
@ -225,6 +229,7 @@ export function Root() {
onTogglePause={() => {}}
thinking={false}
onCancelTurn={() => Promise.resolve()}
onNewSession={() => Promise.resolve()}
onLogout={() => Promise.resolve()}
/>
</Header>
@ -332,6 +337,15 @@ export function Root() {
refresh();
})
}
onNewSession={() =>
postNewSession().then((r) => {
if (!r.ok)
liveStreamRef.current?.pushNote(
`✗ /new-session failed${r.detail ? ": " + r.detail : ""}`,
);
refresh();
})
}
onLogout={() =>
postLogout().then((r) => {
if (!r.ok)

View file

@ -37,6 +37,12 @@ export interface StatusChipsProps {
lastTurnLabel?: string;
thinking: boolean;
onCancelTurn: () => Promise<void>;
/** Drops all prior `--continue` context and starts fresh. Always
* offered, same as logout — the old app.js overflow menu had it as
* an unconditional click target too, this is just the click path
* catching up to what `/new-session` already does. See
* `termActions.ts`'s `postNewSession`. */
onNewSession: () => Promise<void>;
/** Rotates OAuth creds + parks the agent in needs-login. Always
* offered (unlike cancel-turn, not gated on `thinking`) — the old
* page's overflow menu had it unconditionally too. See
@ -119,6 +125,7 @@ function StatusMenu({
onTogglePause,
thinking,
onCancelTurn,
onNewSession,
onLogout,
}: Pick<
StatusChipsProps,
@ -129,16 +136,19 @@ function StatusMenu({
| "onTogglePause"
| "thinking"
| "onCancelTurn"
| "onNewSession"
| "onLogout"
>) {
const [open, setOpen] = useState(false);
const [confirmCancel, setConfirmCancel] = useState(false);
const [confirmNewSession, setConfirmNewSession] = useState(false);
const [confirmLogout, setConfirmLogout] = useState(false);
const anchorRef = useRef<HTMLDivElement>(null);
function close() {
setOpen(false);
setConfirmCancel(false);
setConfirmNewSession(false);
setConfirmLogout(false);
}
@ -153,7 +163,12 @@ function StatusMenu({
});
}
// Always offered, regardless of turn state — same as the old overflow
// menu's unconditional 🔓 logout entry.
// menu's unconditional ↻ new-session and 🔓 logout entries.
options.push({
value: "new-session",
label: confirmNewSession ? "↻ click again to confirm" : "↻ new session",
danger: true,
});
options.push({
value: "logout",
label: confirmLogout ? "🔓 click again to confirm" : "🔓 logout",
@ -189,6 +204,16 @@ function StatusMenu({
close();
return;
}
if (value === "new-session") {
// Same arm-then-fire shape as cancel-turn/logout.
if (!confirmNewSession) {
setConfirmNewSession(true);
return;
}
onNewSession();
close();
return;
}
// value === 'logout': same arm-then-fire shape as cancel-turn.
if (!confirmLogout) {
setConfirmLogout(true);
@ -222,6 +247,7 @@ export function StatusChips({
lastTurnLabel,
thinking,
onCancelTurn,
onNewSession,
onLogout,
}: StatusChipsProps) {
return (
@ -234,6 +260,7 @@ export function StatusChips({
onTogglePause={onTogglePause}
thinking={thinking}
onCancelTurn={onCancelTurn}
onNewSession={onNewSession}
onLogout={onLogout}
/>
<Picker