Skip to content

Commit 4c14cfd

Browse files
committed
make the tool calls editable
1 parent a6f57c3 commit 4c14cfd

File tree

1 file changed

+87
-4
lines changed

1 file changed

+87
-4
lines changed

src/Views/ToolApprovalModal.ts

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import { BaseApprovalModal } from "./BaseApprovalModal";
88
export class ToolApprovalModal extends BaseApprovalModal<ToolApprovalDecision> {
99
private toolName: string;
1010
private args: Record<string, any>;
11+
private editedQuery: string | null = null;
12+
private queryTextarea: HTMLTextAreaElement | null = null;
13+
private approveBtn: HTMLButtonElement | null = null;
1114

1215
constructor(app: App, toolName: string, args: Record<string, any>, modelName: string = "AI") {
1316
super(app, modelName);
@@ -126,6 +129,34 @@ export class ToolApprovalModal extends BaseApprovalModal<ToolApprovalDecision> {
126129
return "Approve and Execute";
127130
}
128131

132+
protected override renderActionButtons(container: HTMLElement): void {
133+
const buttonContainer = container.createDiv();
134+
buttonContainer.style.display = "flex";
135+
buttonContainer.style.gap = "8px";
136+
buttonContainer.style.justifyContent = "flex-end";
137+
buttonContainer.style.marginTop = "20px";
138+
139+
const cancelBtn = buttonContainer.createEl("button", { text: this.getCancelText() });
140+
this.styleCancelButton(cancelBtn);
141+
cancelBtn.onclick = () => {
142+
this.result = this.buildCancelledResult();
143+
this.resolveModalPromise(this.result);
144+
this.close();
145+
};
146+
147+
this.approveBtn = buttonContainer.createEl("button", { text: this.getApproveText() });
148+
this.styleApproveButton(this.approveBtn);
149+
150+
// Initial validation
151+
this.validateApproveButton();
152+
153+
this.approveBtn.onclick = () => {
154+
this.result = this.buildApprovedResult();
155+
this.resolveModalPromise(this.result);
156+
this.close();
157+
};
158+
}
159+
129160
protected buildApprovedResult(): ToolApprovalDecision {
130161
return {
131162
approvalId: this.toolName,
@@ -141,6 +172,27 @@ export class ToolApprovalModal extends BaseApprovalModal<ToolApprovalDecision> {
141172
};
142173
}
143174

175+
/**
176+
* Validate query and enable/disable approve button
177+
*/
178+
private validateApproveButton(): void {
179+
if (!this.approveBtn) return;
180+
181+
// For search tools, require non-empty query
182+
if ((this.toolName === "vault_search" || this.toolName === "web_search") && this.queryTextarea) {
183+
const query = this.queryTextarea.value.trim();
184+
const isValid = query.length > 0;
185+
this.approveBtn.disabled = !isValid;
186+
this.approveBtn.style.opacity = isValid ? "1" : "0.5";
187+
this.approveBtn.style.cursor = isValid ? "pointer" : "not-allowed";
188+
} else {
189+
// Other tools - always enabled
190+
this.approveBtn.disabled = false;
191+
this.approveBtn.style.opacity = "1";
192+
this.approveBtn.style.cursor = "pointer";
193+
}
194+
}
195+
144196
/**
145197
* Get modified arguments based on user selections
146198
*/
@@ -160,6 +212,14 @@ export class ToolApprovalModal extends BaseApprovalModal<ToolApprovalDecision> {
160212
};
161213
}
162214

215+
// For vault_search and web_search, include edited query if changed
216+
if ((this.toolName === "vault_search" || this.toolName === "web_search") && this.editedQuery) {
217+
return {
218+
...baseArgs,
219+
query: this.editedQuery,
220+
};
221+
}
222+
163223
return baseArgs;
164224
}
165225

@@ -198,10 +258,33 @@ export class ToolApprovalModal extends BaseApprovalModal<ToolApprovalDecision> {
198258

199259
// List for search queries
200260
if ((this.toolName === "vault_search" || this.toolName === "web_search") && query) {
201-
const list = container.createEl("ul");
202-
list.style.margin = "0 0 16px 20px";
203-
list.style.lineHeight = "1.5";
204-
list.createEl("li", { text: `"${query}"` });
261+
// Label for textarea
262+
const label = container.createEl("label", { text: "Search query:" });
263+
label.style.display = "block";
264+
label.style.marginBottom = "8px";
265+
label.style.fontWeight = "500";
266+
label.style.opacity = "0.7";
267+
268+
// Editable textarea for query
269+
this.queryTextarea = container.createEl("textarea");
270+
this.queryTextarea.value = query;
271+
this.queryTextarea.style.width = "100%";
272+
this.queryTextarea.style.minHeight = "80px";
273+
this.queryTextarea.style.padding = "8px";
274+
this.queryTextarea.style.borderRadius = "4px";
275+
this.queryTextarea.style.border = "1px solid var(--background-modifier-border)";
276+
this.queryTextarea.style.backgroundColor = "var(--background-secondary)";
277+
this.queryTextarea.style.color = "var(--text-normal)";
278+
this.queryTextarea.style.fontSize = "0.95em";
279+
this.queryTextarea.style.fontFamily = "var(--font-interface)";
280+
this.queryTextarea.style.resize = "vertical";
281+
this.queryTextarea.style.marginBottom = "16px";
282+
283+
// Track query changes
284+
this.queryTextarea.addEventListener("input", () => {
285+
this.editedQuery = this.queryTextarea!.value.trim();
286+
this.validateApproveButton();
287+
});
205288
} else if (this.toolName === "file_read") {
206289
// For file_read, still just one line since files are shown in selection below
207290
const noteEl = container.createEl("p", {

0 commit comments

Comments
 (0)