diff --git a/src-node/claude-code-agent.js b/src-node/claude-code-agent.js index c76f7ece71..b281878b3f 100644 --- a/src-node/claude-code-agent.js +++ b/src-node/claude-code-agent.js @@ -269,21 +269,33 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale) newText: input.tool_input.new_string }; editCount++; + let editResult; try { - await nodeConnector.execPeer("applyEditToBuffer", edit); + editResult = await nodeConnector.execPeer("applyEditToBuffer", edit); } catch (err) { console.warn("[Phoenix AI] Failed to apply edit to buffer:", err.message); + editResult = { applied: false, error: err.message }; } nodeConnector.triggerPeer("aiToolEdit", { requestId: requestId, toolId: myToolId, edit: edit }); + let reason; + if (editResult && editResult.applied === false) { + reason = "Edit FAILED: " + (editResult.error || "unknown error"); + } else { + reason = "Edit applied successfully via Phoenix editor."; + if (editResult && editResult.isLivePreviewRelated) { + reason += " The edited file is part of the active live preview." + + " Reload when ready with execJsInLivePreview: `location.reload()`"; + } + } return { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", - permissionDecisionReason: "Edit applied successfully via Phoenix editor." + permissionDecisionReason: reason } }; } @@ -342,21 +354,33 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale) newText: input.tool_input.content }; editCount++; + let writeResult; try { - await nodeConnector.execPeer("applyEditToBuffer", edit); + writeResult = await nodeConnector.execPeer("applyEditToBuffer", edit); } catch (err) { console.warn("[Phoenix AI] Failed to apply write to buffer:", err.message); + writeResult = { applied: false, error: err.message }; } nodeConnector.triggerPeer("aiToolEdit", { requestId: requestId, toolId: myToolId, edit: edit }); + let reason; + if (writeResult && writeResult.applied === false) { + reason = "Write FAILED: " + (writeResult.error || "unknown error"); + } else { + reason = "Write applied successfully via Phoenix editor."; + if (writeResult && writeResult.isLivePreviewRelated) { + reason += " The written file is part of the active live preview." + + " Reload when ready with execJsInLivePreview: `location.reload()`"; + } + } return { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", - permissionDecisionReason: "Write applied successfully via Phoenix editor." + permissionDecisionReason: reason } }; } diff --git a/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js b/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js index 4c03e8fbd8..cf054caa3d 100644 --- a/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js +++ b/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js @@ -548,7 +548,7 @@ function RemoteFunctions(config = {}) { if (this.trigger) { _trigger(this.elements[i], "highlight", 0); } - clearElementBackground(this.elements[i]); + clearElementHoverHighlight(this.elements[i]); } this.elements = []; @@ -583,19 +583,12 @@ function RemoteFunctions(config = {}) { return getHighlightMode() !== "click"; } - // helper function to clear element background highlighting - function clearElementBackground(element) { - if (element._originalBackgroundColor !== undefined) { - element.style.backgroundColor = element._originalBackgroundColor; - } else { - // only clear background if it's currently a highlight color, not if it's an original user style - const currentBg = element.style.backgroundColor; - if (currentBg === "rgba(0, 162, 255, 0.2)" || currentBg.includes("rgba(0, 162, 255")) { - element.style.backgroundColor = ""; - } - // if it's some other color, we just leave it as is - it's likely a user-defined style + // helper function to clear element hover outline highlighting + function clearElementHoverHighlight(element) { + if (element._originalHoverOutline !== undefined) { + element.style.outline = element._originalHoverOutline; } - delete element._originalBackgroundColor; + delete element._originalHoverOutline; } function onElementHover(event) { @@ -622,9 +615,10 @@ function RemoteFunctions(config = {}) { if (_hoverHighlight && shouldShowHighlightOnHover()) { _hoverHighlight.clear(); - // Store original background color to restore on hover out - element._originalBackgroundColor = element.style.backgroundColor; - element.style.backgroundColor = "rgba(0, 162, 255, 0.2)"; + // Store original outline to restore on hover out, then apply a blue border + element._originalHoverOutline = element.style.outline; + const outlineColor = element.hasAttribute(GLOBALS.DATA_BRACKETS_ID_ATTR) ? "#4285F4" : "#3C3F41"; + element.style.outline = `1px solid ${outlineColor}`; _hoverHighlight.add(element, false); @@ -646,7 +640,7 @@ function RemoteFunctions(config = {}) { // this is to check the user's settings, if they want to show the elements highlights on hover or click if (_hoverHighlight && shouldShowHighlightOnHover()) { _hoverHighlight.clear(); - clearElementBackground(element); + clearElementHoverHighlight(element); // dismiss the info box const infoBoxHandler = LivePreviewView.getToolHandler("InfoBox"); if (infoBoxHandler) { diff --git a/src/core-ai/aiPhoenixConnectors.js b/src/core-ai/aiPhoenixConnectors.js index 75c1c64db4..ee51f73c85 100644 --- a/src/core-ai/aiPhoenixConnectors.js +++ b/src/core-ai/aiPhoenixConnectors.js @@ -442,6 +442,19 @@ define(function (require, exports, module) { * @param {Object} params - {file, oldText, newText} * @return {Promise<{applied: boolean, error?: string}>} */ + function _isFileInLivePreview(filePath) { + const liveDetails = LiveDevMain.getLivePreviewDetails(); + if (!liveDetails || !liveDetails.liveDocument) { + return false; + } + const vfsPath = SnapshotStore.realToVfsPath(filePath); + const liveDocPath = liveDetails.liveDocument.doc.file.fullPath; + if (vfsPath === liveDocPath) { + return true; + } + return !!(liveDetails.liveDocument.isRelated && liveDetails.liveDocument.isRelated(vfsPath)); + } + function applyEditToBuffer(params) { const deferred = new $.Deferred(); _applySingleEdit(params) @@ -449,7 +462,10 @@ define(function (require, exports, module) { if (result && result.previousContent !== undefined) { _previousContentMap[params.file] = result.previousContent; } - deferred.resolve({ applied: true }); + deferred.resolve({ + applied: true, + isLivePreviewRelated: _isFileInLivePreview(params.file) + }); }) .fail(function (err) { deferred.resolve({ applied: false, error: err.message || String(err) });