authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-10-03 21:19:52-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-03 21:19:52-03:00
log2d81e4c27559bbd78564bc8cc3b38a0011814361
tree60dfe0921031196d9aecefabc54b57ea22684bc6
parentd3071275d576bb289a6cb177c6cc28fff8e95015
signature Signed by PGP key B5690EEEBB952194

fix(web): clear stale chat action shortcuts (#15394)


1 files changed, 61 insertions(+), 11 deletions(-)

apps/web/src/components/ChatView.tsx+61-11
...@@ -294,7 +294,10 @@ import {...@@ -294,7 +294,10 @@ import {
294import { cn, randomUUID } from "~/lib/utils";294import { cn, randomUUID } from "~/lib/utils";
295import { COLLAPSED_SIDEBAR_TITLEBAR_INSET_CLASS } from "~/workspaceTitlebar";295import { COLLAPSED_SIDEBAR_TITLEBAR_INSET_CLASS } from "~/workspaceTitlebar";
296import { stackedThreadToast, toastManager } from "./ui/toast";296import { stackedThreadToast, toastManager } from "./ui/toast";
297import { decodeProjectScriptKeybindingRule } from "~/lib/projectScriptKeybindings";297import {
298 decodeProjectScriptKeybindingRule,
299 keybindingValueForCommand,
300} from "~/lib/projectScriptKeybindings";
298import { type NewProjectScriptInput } from "./ProjectScriptsControl";301import { type NewProjectScriptInput } from "./ProjectScriptsControl";
299import {302import {
300 buildProjectScript,303 buildProjectScript,
...@@ -1534,6 +1537,9 @@ export default function ChatView(props: ChatViewProps) {...@@ -1534,6 +1537,9 @@ export default function ChatView(props: ChatViewProps) {
1534 const upsertKeybinding = useAtomCommand(serverEnvironment.upsertKeybinding, {1537 const upsertKeybinding = useAtomCommand(serverEnvironment.upsertKeybinding, {
1535 reportFailure: false,1538 reportFailure: false,
1536 });1539 });
1540 const removeKeybinding = useAtomCommand(serverEnvironment.removeKeybinding, {
1541 reportFailure: false,
1542 });
1537 const openTerminal = useAtomCommand(terminalEnvironment.open, "terminal open");1543 const openTerminal = useAtomCommand(terminalEnvironment.open, "terminal open");
1538 const writeTerminal = useAtomCommand(terminalEnvironment.write, "terminal write");1544 const writeTerminal = useAtomCommand(terminalEnvironment.write, "terminal write");
1539 const closeTerminalMutation = useAtomCommand(terminalEnvironment.close, "terminal close");1545 const closeTerminalMutation = useAtomCommand(terminalEnvironment.close, "terminal close");
...@@ -4940,20 +4946,64 @@ export default function ChatView(props: ChatViewProps) {...@@ -4940,20 +4946,64 @@ export default function ChatView(props: ChatViewProps) {
4940 command: input.keybindingCommand,4946 command: input.keybindingCommand,
4941 });4947 });
49424948
4943 if (isElectron && keybindingRule) {4949 if (!isElectron) return updateResult;
4944 return mapAtomCommandResult(4950
4945 await upsertKeybinding({4951 const scriptId = input.keybindingCommand
4946 environmentId,4952 ? projectScriptIdFromCommand(input.keybindingCommand)
4947 input: keybindingRule,4953 : null;
4948 }),4954 if (!keybindingRule && !input.previousScripts.some((script) => script.id === scriptId)) {
4949 () => undefined,4955 return updateResult;
4950 );
4951 }4956 }
4952 return updateResult;4957 const retainedElsewhere =
4958 !input.nextScripts.some((script) => script.id === scriptId) &&
4959 (settings.defaultProjectScripts.some((script) => script.id === scriptId) ||
4960 Object.entries(settings.projectSettingsOverrides).some(
4961 ([projectId, entry]) =>
4962 projectId !== input.projectId &&
4963 entry.defaultProjectScripts?.some((script) => script.id === scriptId),
4964 ) ||
4965 allProjects.some(
4966 (other) =>
4967 other.environmentId === environmentId &&
4968 other.id !== input.projectId &&
4969 resolveProjectScripts(settings, other).some((script) => script.id === scriptId),
4970 ));
4971 if (!keybindingRule && retainedElsewhere) return updateResult;
4972
4973 const previousRules = (
4974 environmentById.get(environmentId)?.serverConfig?.keybindings ?? []
4975 ).flatMap((binding) => {
4976 if (binding.command !== input.keybindingCommand || binding.whenAst) return [];
4977 const previous = decodeProjectScriptKeybindingRule({
4978 keybinding: keybindingValueForCommand([binding], input.keybindingCommand),
4979 command: input.keybindingCommand,
4980 });
4981 return previous ? [previous] : [];
4982 });
4983 const previous = previousRules.at(-1);
4984 for (const rule of keybindingRule ? previousRules.slice(0, -1) : previousRules) {
4985 const result = await removeKeybinding({ environmentId, input: rule });
4986 if (result._tag === "Failure") return mapAtomCommandResult(result, () => undefined);
4987 }
4988 return keybindingRule
4989 ? mapAtomCommandResult(
4990 await upsertKeybinding({
4991 environmentId,
4992 input:
4993 previous && previous.key !== keybindingRule.key
4994 ? { ...keybindingRule, replace: previous }
4995 : keybindingRule,
4996 }),
4997 () => undefined,
4998 )
4999 : updateResult;
4953 },5000 },
4954 [5001 [
5002 allProjects,
5003 environmentById,
4955 environmentId,5004 environmentId,
4956 settings.projectSettingsOverrides,5005 removeKeybinding,
5006 settings,
4957 supportsProjectSettingsOverrides,5007 supportsProjectSettingsOverrides,
4958 updateProjectScriptSettings,5008 updateProjectScriptSettings,
4959 upsertKeybinding,5009 upsertKeybinding,