From 9fd4969422f6f638c82d6fd85581d82a02c1e45b Mon Sep 17 00:00:00 2001 From: abose Date: Sun, 22 Feb 2026 23:22:13 +0530 Subject: [PATCH 1/4] fix: use outline border instead of blue overlay for edit mode hover highlight Replace the distracting blue background overlay (rgba(0,162,255,0.2)) with a subtle 1px outline border matching the click selection style. --- .../BrowserScripts/RemoteFunctions.js | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) 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) { From b453373842dfc4b99668cbc8915537111c8c9556 Mon Sep 17 00:00:00 2001 From: abose Date: Sun, 22 Feb 2026 23:58:11 +0530 Subject: [PATCH 2/4] fix: sometimes ai edits doesnt work as it cant find the corect string to replace --- src-node/claude-code-agent.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src-node/claude-code-agent.js b/src-node/claude-code-agent.js index c76f7ece71..ece873b6ae 100644 --- a/src-node/claude-code-agent.js +++ b/src-node/claude-code-agent.js @@ -269,21 +269,26 @@ 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 }); + const reason = (editResult && editResult.applied === false) + ? "Edit FAILED: " + (editResult.error || "unknown error") + : "Edit applied successfully via Phoenix editor."; return { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", - permissionDecisionReason: "Edit applied successfully via Phoenix editor." + permissionDecisionReason: reason } }; } From 6a7384521e6817b2519f7b625d8ddb2853422122 Mon Sep 17 00:00:00 2001 From: abose Date: Mon, 23 Feb 2026 10:06:19 +0530 Subject: [PATCH 3/4] chore: reload live preview on ai edits --- src/core-ai/AIChatPanel.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core-ai/AIChatPanel.js b/src/core-ai/AIChatPanel.js index a786b82cd8..51e3fd451a 100644 --- a/src/core-ai/AIChatPanel.js +++ b/src/core-ai/AIChatPanel.js @@ -905,6 +905,9 @@ define(function (require, exports, module) { // Record pre-edit content into pending snapshot and back-fill SnapshotStore.recordFileBeforeEdit(edit.file, previousContent, isNewFile); + // Reload live preview so the user sees the edit reflected immediately + CommandManager.execute(Commands.CMD_RELOAD_LIVE_PREVIEW); + // Find the oldest Edit/Write tool indicator for this file that doesn't // already have edit actions. This is more robust than matching by toolId // because the SDK with includePartialMessages may re-emit tool_use blocks From a010438c80afa332c8d5fdae1c7c5c42a9d929f1 Mon Sep 17 00:00:00 2001 From: abose Date: Mon, 23 Feb 2026 10:23:06 +0530 Subject: [PATCH 4/4] fix: report edit failures to AI and hint live preview reload Edit/Write hooks now return actual error messages instead of false success. When the edited file is part of the active live preview, the response hints the AI to reload via execJsInLivePreview. --- src-node/claude-code-agent.js | 29 ++++++++++++++++++++++++----- src/core-ai/AIChatPanel.js | 3 --- src/core-ai/aiPhoenixConnectors.js | 18 +++++++++++++++++- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src-node/claude-code-agent.js b/src-node/claude-code-agent.js index ece873b6ae..b281878b3f 100644 --- a/src-node/claude-code-agent.js +++ b/src-node/claude-code-agent.js @@ -281,9 +281,16 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale) toolId: myToolId, edit: edit }); - const reason = (editResult && editResult.applied === false) - ? "Edit FAILED: " + (editResult.error || "unknown error") - : "Edit applied successfully via Phoenix editor."; + 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", @@ -347,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/core-ai/AIChatPanel.js b/src/core-ai/AIChatPanel.js index 51e3fd451a..a786b82cd8 100644 --- a/src/core-ai/AIChatPanel.js +++ b/src/core-ai/AIChatPanel.js @@ -905,9 +905,6 @@ define(function (require, exports, module) { // Record pre-edit content into pending snapshot and back-fill SnapshotStore.recordFileBeforeEdit(edit.file, previousContent, isNewFile); - // Reload live preview so the user sees the edit reflected immediately - CommandManager.execute(Commands.CMD_RELOAD_LIVE_PREVIEW); - // Find the oldest Edit/Write tool indicator for this file that doesn't // already have edit actions. This is more robust than matching by toolId // because the SDK with includePartialMessages may re-emit tool_use blocks 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) });