authorgravatar for mubinansari910@gmail.comMubin Ansari <mubinansari910@gmail.com> 2026-03-09 07:21:55+05:30
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-03-08 18:51:55-07:00
log1c43bad705bd1d953fd168aa0b852c8b2d83ee84
tree76257242983c6bc72f8a01afd13ccedcde414945
parent18b6590d2e490bd73ba968b094dc221ffb7c0f67
signature Signed by PGP key B5690EEEBB952194

Add `delete` option for project actions (#223)

Adds a delete button to the action edit dialog with a confirmation alert. Fixes pingdotgg/t3code#208.

2 files changed, 79 insertions(+), 1 deletions(-)

apps/web/src/components/ChatView.tsx+34
...@@ -1621,6 +1621,36 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -1621,6 +1621,36 @@ export default function ChatView({ threadId }: ChatViewProps) {
1621 },1621 },
1622 [activeProject, persistProjectScripts],1622 [activeProject, persistProjectScripts],
1623 );1623 );
1624 const deleteProjectScript = useCallback(
1625 async (scriptId: string) => {
1626 if (!activeProject) return;
1627 const nextScripts = activeProject.scripts.filter((script) => script.id !== scriptId);
1628
1629 const deletedName = activeProject.scripts.find((s) => s.id === scriptId)?.name;
1630
1631 try {
1632 await persistProjectScripts({
1633 projectId: activeProject.id,
1634 projectCwd: activeProject.cwd,
1635 previousScripts: activeProject.scripts,
1636 nextScripts,
1637 keybinding: null,
1638 keybindingCommand: commandForProjectScript(scriptId),
1639 });
1640 toastManager.add({
1641 type: "success",
1642 title: `Deleted action "${deletedName ?? "Unknown"}"`,
1643 });
1644 } catch (error) {
1645 toastManager.add({
1646 type: "error",
1647 title: "Could not delete action",
1648 description: error instanceof Error ? error.message : "An unexpected error occurred.",
1649 });
1650 }
1651 },
1652 [activeProject, persistProjectScripts],
1653 );
16241654
1625 const handleRuntimeModeChange = useCallback(1655 const handleRuntimeModeChange = useCallback(
1626 (mode: RuntimeMode) => {1656 (mode: RuntimeMode) => {
...@@ -3428,6 +3458,7 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -3428,6 +3458,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
3428 }}3458 }}
3429 onAddProjectScript={saveProjectScript}3459 onAddProjectScript={saveProjectScript}
3430 onUpdateProjectScript={updateProjectScript}3460 onUpdateProjectScript={updateProjectScript}
3461 onDeleteProjectScript={deleteProjectScript}
3431 onToggleDiff={onToggleDiff}3462 onToggleDiff={onToggleDiff}
3432 />3463 />
3433 </header>3464 </header>
...@@ -4008,6 +4039,7 @@ interface ChatHeaderProps {...@@ -4008,6 +4039,7 @@ interface ChatHeaderProps {
4008 onRunProjectScript: (script: ProjectScript) => void;4039 onRunProjectScript: (script: ProjectScript) => void;
4009 onAddProjectScript: (input: NewProjectScriptInput) => Promise<void>;4040 onAddProjectScript: (input: NewProjectScriptInput) => Promise<void>;
4010 onUpdateProjectScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void>;4041 onUpdateProjectScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void>;
4042 onDeleteProjectScript: (scriptId: string) => Promise<void>;
4011 onToggleDiff: () => void;4043 onToggleDiff: () => void;
4012}4044}
40134045
...@@ -4027,6 +4059,7 @@ const ChatHeader = memo(function ChatHeader({...@@ -4027,6 +4059,7 @@ const ChatHeader = memo(function ChatHeader({
4027 onRunProjectScript,4059 onRunProjectScript,
4028 onAddProjectScript,4060 onAddProjectScript,
4029 onUpdateProjectScript,4061 onUpdateProjectScript,
4062 onDeleteProjectScript,
4030 onToggleDiff,4063 onToggleDiff,
4031}: ChatHeaderProps) {4064}: ChatHeaderProps) {
4032 return (4065 return (
...@@ -4059,6 +4092,7 @@ const ChatHeader = memo(function ChatHeader({...@@ -4059,6 +4092,7 @@ const ChatHeader = memo(function ChatHeader({
4059 onRunScript={onRunProjectScript}4092 onRunScript={onRunProjectScript}
4060 onAddScript={onAddProjectScript}4093 onAddScript={onAddProjectScript}
4061 onUpdateScript={onUpdateProjectScript}4094 onUpdateScript={onUpdateProjectScript}
4095 onDeleteScript={onDeleteProjectScript}
4062 />4096 />
4063 )}4097 )}
4064 {activeProjectName && (4098 {activeProjectName && (
apps/web/src/components/ProjectScriptsControl.tsx+45-1
...@@ -14,7 +14,7 @@ import {...@@ -14,7 +14,7 @@ import {
14 SettingsIcon,14 SettingsIcon,
15 WrenchIcon,15 WrenchIcon,
16} from "lucide-react";16} from "lucide-react";
17import React, { type FormEvent, type KeyboardEvent, useMemo, useState } from "react";17import React, { type FormEvent, type KeyboardEvent, useCallback, useMemo, useState } from "react";
1818
19import {19import {
20 keybindingValueForCommand,20 keybindingValueForCommand,
...@@ -27,6 +27,15 @@ import {...@@ -27,6 +27,15 @@ import {
27} from "~/projectScripts";27} from "~/projectScripts";
28import { shortcutLabelForCommand } from "~/keybindings";28import { shortcutLabelForCommand } from "~/keybindings";
29import { isMacPlatform } from "~/lib/utils";29import { isMacPlatform } from "~/lib/utils";
30import {
31 AlertDialog,
32 AlertDialogClose,
33 AlertDialogDescription,
34 AlertDialogFooter,
35 AlertDialogHeader,
36 AlertDialogPopup,
37 AlertDialogTitle,
38} from "./ui/alert-dialog";
30import { Button } from "./ui/button";39import { Button } from "./ui/button";
31import {40import {
32 Dialog,41 Dialog,
...@@ -84,6 +93,7 @@ interface ProjectScriptsControlProps {...@@ -84,6 +93,7 @@ interface ProjectScriptsControlProps {
84 onRunScript: (script: ProjectScript) => void;93 onRunScript: (script: ProjectScript) => void;
85 onAddScript: (input: NewProjectScriptInput) => Promise<void> | void;94 onAddScript: (input: NewProjectScriptInput) => Promise<void> | void;
86 onUpdateScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void> | void;95 onUpdateScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void> | void;
96 onDeleteScript: (scriptId: string) => Promise<void> | void;
87}97}
8898
89function normalizeShortcutKeyToken(key: string): string | null {99function normalizeShortcutKeyToken(key: string): string | null {
...@@ -144,6 +154,7 @@ export default function ProjectScriptsControl({...@@ -144,6 +154,7 @@ export default function ProjectScriptsControl({
144 onRunScript,154 onRunScript,
145 onAddScript,155 onAddScript,
146 onUpdateScript,156 onUpdateScript,
157 onDeleteScript,
147}: ProjectScriptsControlProps) {158}: ProjectScriptsControlProps) {
148 const addScriptFormId = React.useId();159 const addScriptFormId = React.useId();
149 const [editingScriptId, setEditingScriptId] = useState<string | null>(null);160 const [editingScriptId, setEditingScriptId] = useState<string | null>(null);
...@@ -155,6 +166,7 @@ export default function ProjectScriptsControl({...@@ -155,6 +166,7 @@ export default function ProjectScriptsControl({
155 const [runOnWorktreeCreate, setRunOnWorktreeCreate] = useState(false);166 const [runOnWorktreeCreate, setRunOnWorktreeCreate] = useState(false);
156 const [keybinding, setKeybinding] = useState("");167 const [keybinding, setKeybinding] = useState("");
157 const [validationError, setValidationError] = useState<string | null>(null);168 const [validationError, setValidationError] = useState<string | null>(null);
169 const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
158170
159 const primaryScript = useMemo(() => {171 const primaryScript = useMemo(() => {
160 if (preferredScriptId) {172 if (preferredScriptId) {
...@@ -247,6 +259,13 @@ export default function ProjectScriptsControl({...@@ -247,6 +259,13 @@ export default function ProjectScriptsControl({
247 setDialogOpen(true);259 setDialogOpen(true);
248 };260 };
249261
262 const confirmDeleteScript = useCallback(() => {
263 if (!editingScriptId) return;
264 setDeleteConfirmOpen(false);
265 setDialogOpen(false);
266 void onDeleteScript(editingScriptId);
267 }, [editingScriptId, onDeleteScript]);
268
250 return (269 return (
251 <>270 <>
252 {primaryScript ? (271 {primaryScript ? (
...@@ -440,6 +459,16 @@ export default function ProjectScriptsControl({...@@ -440,6 +459,16 @@ export default function ProjectScriptsControl({
440 </form>459 </form>
441 </DialogPanel>460 </DialogPanel>
442 <DialogFooter>461 <DialogFooter>
462 {isEditing && (
463 <Button
464 type="button"
465 variant="destructive-outline"
466 className="mr-auto"
467 onClick={() => setDeleteConfirmOpen(true)}
468 >
469 Delete
470 </Button>
471 )}
443 <Button472 <Button
444 type="button"473 type="button"
445 variant="outline"474 variant="outline"
...@@ -455,6 +484,21 @@ export default function ProjectScriptsControl({...@@ -455,6 +484,21 @@ export default function ProjectScriptsControl({
455 </DialogFooter>484 </DialogFooter>
456 </DialogPopup>485 </DialogPopup>
457 </Dialog>486 </Dialog>
487
488 <AlertDialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}>
489 <AlertDialogPopup>
490 <AlertDialogHeader>
491 <AlertDialogTitle>Delete action "{name}"?</AlertDialogTitle>
492 <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
493 </AlertDialogHeader>
494 <AlertDialogFooter>
495 <AlertDialogClose render={<Button variant="outline" />}>Cancel</AlertDialogClose>
496 <Button variant="destructive" onClick={confirmDeleteScript}>
497 Delete action
498 </Button>
499 </AlertDialogFooter>
500 </AlertDialogPopup>
501 </AlertDialog>
458 </>502 </>
459 );503 );
460}504}