дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-08 19:02:17 +02:00
Debug session tabs had no close button, and ending a session only marked it terminated, so every session stayed in the strip and Cmd+W fell through to hiding the window. Each tab can now be closed: a running session asks before it is terminated (the Run tab's question, shared through the workbench), then the session, its children and its console are dropped and the tab on the left is selected. Co-authored-by: payailk <[email protected]>
144 рядки
4.5 KiB
TypeScript
144 рядки
4.5 KiB
TypeScript
/**
|
|
* Run feature dialogs: "Process is running" (rerun confirmation), the External Tools
|
|
* `$Prompt$` input and the SSH password / passphrase of a run target.
|
|
*/
|
|
import { useState } from "react";
|
|
import { create } from "zustand";
|
|
import { call, commands, isTauri } from "@/platform/ipc";
|
|
import { loadSettings } from "@/platform/project";
|
|
import { Checkbox, Dialog, Input } from "@/ui";
|
|
import { confirmChecked } from "@/workbench/messages";
|
|
import styles from "./Run.module.css";
|
|
import { tx } from "@/platform/i18n";
|
|
|
|
interface Pending {
|
|
title: string;
|
|
resolve: (value: string | null) => void;
|
|
}
|
|
|
|
interface PendingSecret {
|
|
message: string;
|
|
save: boolean;
|
|
resolve: (value: { secret: string; save: boolean } | null) => void;
|
|
}
|
|
|
|
const useRunDialog = create<{ pending: Pending | null; secret: PendingSecret | null }>(() => ({
|
|
pending: null,
|
|
secret: null,
|
|
}));
|
|
|
|
/**
|
|
* IDEA's "Process 'x' Is Running" dialog, shown when Run starts a configuration
|
|
* that doesn't allow parallel runs. Resolves true for Stop and Rerun.
|
|
*/
|
|
export async function confirmRerun(name: string, instances = 1): Promise<boolean> {
|
|
const answer = await confirmChecked({
|
|
title: tx`Process '${name}' Is Running`,
|
|
message:
|
|
instances > 1
|
|
? tx`'${name}' is not allowed to run in parallel.\nWould you like to stop ${instances} running instances?`
|
|
: tx`'${name}' is not allowed to run in parallel.\nWould you like to stop the running one?`,
|
|
ok: "Stop and Rerun",
|
|
checkbox: "Do not ask again",
|
|
});
|
|
if (answer?.checked && isTauri()) {
|
|
try {
|
|
await call(commands.settingsSet("global", "run.confirmRerun", "false"));
|
|
await loadSettings();
|
|
} catch {
|
|
/* keep asking */
|
|
}
|
|
}
|
|
return answer !== null;
|
|
}
|
|
|
|
export { confirmTerminate } from "@/workbench/confirmTerminate";
|
|
|
|
/** Asks for the value of `$Prompt$`; null when cancelled. */
|
|
export function promptValue(title: string): Promise<string | null> {
|
|
return new Promise((resolve) => useRunDialog.setState({ pending: { title, resolve } }));
|
|
}
|
|
|
|
/** Asks for the SSH password or key passphrase of a run target (`message`: the backend's error); null when cancelled. */
|
|
export function promptSecret(message: string, save: boolean): Promise<{ secret: string; save: boolean } | null> {
|
|
return new Promise((resolve) => useRunDialog.setState({ secret: { message, save, resolve } }));
|
|
}
|
|
|
|
function close() {
|
|
useRunDialog.setState({ pending: null });
|
|
}
|
|
|
|
function SecretDialog({ message, save: initialSave, resolve }: PendingSecret) {
|
|
const [secret, setSecret] = useState("");
|
|
const [save, setSave] = useState(initialSave);
|
|
const done = (v: { secret: string; save: boolean } | null) => {
|
|
useRunDialog.setState({ secret: null });
|
|
resolve(v);
|
|
};
|
|
return (
|
|
<Dialog
|
|
open
|
|
title="SSH Authentication"
|
|
onClose={() => done(null)}
|
|
width={420}
|
|
footerStart={<Checkbox checked={save} onChange={setSave} label="Save" />}
|
|
buttons={[
|
|
{ label: "Connect", role: "default", disabled: !secret, onClick: () => done({ secret, save }) },
|
|
{ label: "Cancel", role: "cancel" },
|
|
]}
|
|
>
|
|
<div className={styles.dialogBody}>
|
|
<span>{message}</span>
|
|
<Input
|
|
autoFocus
|
|
type="password"
|
|
aria-label="Password or passphrase"
|
|
value={secret}
|
|
onChange={(e) => setSecret(e.target.value)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter" && secret) {
|
|
e.preventDefault();
|
|
done({ secret, save });
|
|
}
|
|
}}
|
|
/>
|
|
</div>
|
|
</Dialog>
|
|
);
|
|
}
|
|
|
|
function PromptDialog({ title, resolve }: { title: string; resolve: (v: string | null) => void }) {
|
|
const [value, setValue] = useState("");
|
|
const done = (v: string | null) => {
|
|
close();
|
|
resolve(v);
|
|
};
|
|
return (
|
|
<Dialog
|
|
open
|
|
title={title}
|
|
onClose={() => done(null)}
|
|
width={420}
|
|
buttons={[
|
|
{ label: "OK", role: "default", onClick: () => done(value) },
|
|
{ label: "Cancel", role: "cancel" },
|
|
]}
|
|
>
|
|
<div className={styles.dialogBody}>
|
|
<Input autoFocus aria-label="Value" value={value} onChange={(e) => setValue(e.target.value)} />
|
|
</div>
|
|
</Dialog>
|
|
);
|
|
}
|
|
|
|
export function RunDialogsOverlay() {
|
|
const pending = useRunDialog((s) => s.pending);
|
|
const secret = useRunDialog((s) => s.secret);
|
|
return (
|
|
<>
|
|
{pending && <PromptDialog title={pending.title} resolve={pending.resolve} />}
|
|
{secret && <SecretDialog {...secret} />}
|
|
</>
|
|
);
|
|
}
|