feat: Stop archives a session, Sleep keeps it; SSH keys per type (#82); update Auto fix
- Stop (a tab's or pane's X, Close tab) now archives the session it held (DELETE /api/run/:id?archive=1); the new Sleep (moon left of the X, and in the tab, pane and session right-click menus) stops the pane but keeps the session listed to resume later. Shared endRuns in client/src/lib/stopSession.ts. - Git identity (#82): an account keeps an ED25519 AND an RSA key (AWS CodeCommit takes only RSA), every ssh git offers both, and a member's ~/.gitconfig gets core.sshCommand so a git typed in a terminal uses them. - Update dialog: Auto fix starts a Claude Code session on the install folder with the failed update's error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,19 +5,20 @@
|
||||
* does: open it the way its click does (a live pane attaches, an ended session
|
||||
* opens its conversation — never a resume), open a live one in a split,
|
||||
* rename / pin / unpin / archive / unarchive when the list offers them, copy its id, open
|
||||
* its analysis page, and close its live tab (`DELETE /api/run/:id`, what the
|
||||
* tab's X does — after the same confirm for an AI tab, which may be mid-turn).
|
||||
* its analysis page, and end its live tab as the tab's buttons do (`endRuns`,
|
||||
* after the same confirm for an AI tab, which may be mid-turn): **Sleep** stops
|
||||
* it and keeps the session listed to resume later, **Close tab** stops it and
|
||||
* archives the session.
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { api, type RunHandle } from "../../lib/api";
|
||||
import type { RunHandle } from "../../lib/api";
|
||||
import type { Session } from "../../lib/types";
|
||||
import { ContextMenu, MenuItem, MenuSeparator, type MenuPoint } from "../ContextMenu";
|
||||
import { confirmDialog } from "../../lib/confirm";
|
||||
import { copyText } from "../../lib/clipboard";
|
||||
import { toast } from "../../lib/toast";
|
||||
import { canSleep, endRuns } from "../../lib/stopSession";
|
||||
|
||||
export function SessionMenu({
|
||||
laneId,
|
||||
@@ -50,24 +51,12 @@ export function SessionMenu({
|
||||
onClose();
|
||||
fn();
|
||||
};
|
||||
const closeTab = async () => {
|
||||
// Stop archives the session; Sleep keeps it listed to resume later
|
||||
// (`endRuns` asks first for an AI pane, which may be mid-turn).
|
||||
const endTab = (sleep: boolean) => {
|
||||
if (!run) return;
|
||||
onClose();
|
||||
if (
|
||||
run.tool !== "shell" &&
|
||||
!(await confirmDialog({
|
||||
title: t("tabs.stopTitle", { name: session?.name || t("tree.newSession") }),
|
||||
message: t("tabs.stopBody"),
|
||||
confirmLabel: t("tabs.stopConfirm"),
|
||||
destructive: true,
|
||||
}))
|
||||
)
|
||||
return;
|
||||
try {
|
||||
await api.run.kill(run.id);
|
||||
} catch (err) {
|
||||
toast.error(err);
|
||||
}
|
||||
void endRuns([run], { sleep, name: session?.name || t("tree.newSession"), t });
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -105,8 +94,13 @@ export function SessionMenu({
|
||||
{(run || onArchive || onUnarchive) && <MenuSeparator />}
|
||||
{onUnarchive && <MenuItem onSelect={pick(onUnarchive)}>{t("tree.unarchive")}</MenuItem>}
|
||||
{onArchive && <MenuItem onSelect={pick(onArchive)}>{t("sessionMenu.archive")}</MenuItem>}
|
||||
{canSleep(run) && (
|
||||
<MenuItem testId="session-menu-sleep" onSelect={() => endTab(true)}>
|
||||
{t("tabs.sleep")}
|
||||
</MenuItem>
|
||||
)}
|
||||
{run && (
|
||||
<MenuItem danger onSelect={() => void closeTab()}>
|
||||
<MenuItem danger onSelect={() => endTab(false)}>
|
||||
{t("tabs.close")}
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user