2424 */
2525define ( function ( require , exports , module ) {
2626
27- const SidebarTabs = require ( "view/SidebarTabs" ) ,
28- DocumentManager = require ( "document/DocumentManager" ) ,
29- CommandManager = require ( "command/CommandManager" ) ,
30- Commands = require ( "command/Commands" ) ,
31- ProjectManager = require ( "project/ProjectManager" ) ,
32- FileSystem = require ( "filesystem/FileSystem" ) ,
33- SnapshotStore = require ( "core-ai/AISnapshotStore" ) ,
34- Strings = require ( "strings" ) ,
35- StringUtils = require ( "utils/StringUtils" ) ,
36- marked = require ( "thirdparty/marked.min" ) ;
27+ const SidebarTabs = require ( "view/SidebarTabs" ) ,
28+ DocumentManager = require ( "document/DocumentManager" ) ,
29+ CommandManager = require ( "command/CommandManager" ) ,
30+ Commands = require ( "command/Commands" ) ,
31+ ProjectManager = require ( "project/ProjectManager" ) ,
32+ FileSystem = require ( "filesystem/FileSystem" ) ,
33+ SnapshotStore = require ( "core-ai/AISnapshotStore" ) ,
34+ PhoenixConnectors = require ( "core-ai/aiPhoenixConnectors" ) ,
35+ Strings = require ( "strings" ) ,
36+ StringUtils = require ( "utils/StringUtils" ) ,
37+ marked = require ( "thirdparty/marked.min" ) ;
3738
3839 let _nodeConnector = null ;
3940 let _isStreaming = false ;
4041 let _currentRequestId = null ;
4142 let _segmentText = "" ; // text for the current segment only
4243 let _autoScroll = true ;
4344 let _hasReceivedContent = false ; // tracks if we've received any text/tool in current response
44- const _previousContentMap = { } ; // filePath → previous content before edit, for undo support
4545 let _currentEdits = [ ] ; // edits in current response, for summary card
4646 let _firstEditInResponse = true ; // tracks first edit per response for initial PUC
4747 let _undoApplied = false ; // whether undo/restore has been clicked on any card
@@ -299,9 +299,7 @@ define(function (require, exports, module) {
299299 _firstEditInResponse = true ;
300300 _undoApplied = false ;
301301 SnapshotStore . reset ( ) ;
302- Object . keys ( _previousContentMap ) . forEach ( function ( key ) {
303- delete _previousContentMap [ key ] ;
304- } ) ;
302+ PhoenixConnectors . clearPreviousContentMap ( ) ;
305303 if ( $messages ) {
306304 $messages . empty ( ) ;
307305 }
@@ -543,7 +541,7 @@ define(function (require, exports, module) {
543541 } ) ;
544542
545543 // Capture pre-edit content for snapshot tracking
546- const previousContent = _previousContentMap [ edit . file ] ;
544+ const previousContent = PhoenixConnectors . getPreviousContent ( edit . file ) ;
547545 const isNewFile = ( edit . oldText === null && ( previousContent === undefined || previousContent === "" ) ) ;
548546
549547 // On first edit per response, insert initial PUC if needed.
@@ -1102,100 +1100,6 @@ define(function (require, exports, module) {
11021100 return str . replace ( / & / g, "&" ) . replace ( / " / g, """ ) . replace ( / < / g, "<" ) . replace ( / > / g, ">" ) ;
11031101 }
11041102
1105- // --- Edit application ---
1106-
1107- /**
1108- * Apply a single edit to a document buffer and save to disk.
1109- * Called immediately when Claude's Write/Edit is intercepted, so
1110- * subsequent Reads see the new content both in the buffer and on disk.
1111- * @param {Object } edit - {file, oldText, newText}
1112- * @return {$.Promise } resolves with {previousContent} for undo support
1113- */
1114- function _applySingleEdit ( edit ) {
1115- const result = new $ . Deferred ( ) ;
1116- const vfsPath = SnapshotStore . realToVfsPath ( edit . file ) ;
1117-
1118- function _applyToDoc ( ) {
1119- DocumentManager . getDocumentForPath ( vfsPath )
1120- . done ( function ( doc ) {
1121- try {
1122- const previousContent = doc . getText ( ) ;
1123- if ( edit . oldText === null ) {
1124- // Write (new file or full replacement)
1125- doc . setText ( edit . newText ) ;
1126- } else {
1127- // Edit — find oldText and replace
1128- const docText = doc . getText ( ) ;
1129- const idx = docText . indexOf ( edit . oldText ) ;
1130- if ( idx === - 1 ) {
1131- result . reject ( new Error ( Strings . AI_CHAT_EDIT_NOT_FOUND ) ) ;
1132- return ;
1133- }
1134- const startPos = doc . _masterEditor ?
1135- doc . _masterEditor . _codeMirror . posFromIndex ( idx ) :
1136- _indexToPos ( docText , idx ) ;
1137- const endPos = doc . _masterEditor ?
1138- doc . _masterEditor . _codeMirror . posFromIndex ( idx + edit . oldText . length ) :
1139- _indexToPos ( docText , idx + edit . oldText . length ) ;
1140- doc . replaceRange ( edit . newText , startPos , endPos ) ;
1141- }
1142- // Open the file in the editor and save to disk
1143- CommandManager . execute ( Commands . CMD_OPEN , { fullPath : vfsPath } ) ;
1144- SnapshotStore . saveDocToDisk ( doc ) . always ( function ( ) {
1145- result . resolve ( { previousContent : previousContent } ) ;
1146- } ) ;
1147- } catch ( err ) {
1148- result . reject ( err ) ;
1149- }
1150- } )
1151- . fail ( function ( err ) {
1152- result . reject ( err || new Error ( "Could not open document" ) ) ;
1153- } ) ;
1154- }
1155-
1156- if ( edit . oldText === null ) {
1157- // Write — file may not exist yet. Only create on disk if it doesn't
1158- // already exist, to avoid triggering "external change" warnings.
1159- const file = FileSystem . getFileForPath ( vfsPath ) ;
1160- file . exists ( function ( existErr , exists ) {
1161- if ( exists ) {
1162- // File exists — just open and set content, no disk write
1163- _applyToDoc ( ) ;
1164- } else {
1165- // New file — create on disk first so getDocumentForPath works
1166- file . write ( "" , function ( writeErr ) {
1167- if ( writeErr ) {
1168- result . reject ( new Error ( "Could not create file: " + writeErr ) ) ;
1169- return ;
1170- }
1171- _applyToDoc ( ) ;
1172- } ) ;
1173- }
1174- } ) ;
1175- } else {
1176- // Edit — file must already exist
1177- _applyToDoc ( ) ;
1178- }
1179-
1180- return result . promise ( ) ;
1181- }
1182-
1183- /**
1184- * Convert a character index in text to a {line, ch} position.
1185- */
1186- function _indexToPos ( text , index ) {
1187- let line = 0 , ch = 0 ;
1188- for ( let i = 0 ; i < index ; i ++ ) {
1189- if ( text [ i ] === "\n" ) {
1190- line ++ ;
1191- ch = 0 ;
1192- } else {
1193- ch ++ ;
1194- }
1195- }
1196- return { line : line , ch : ch } ;
1197- }
1198-
11991103 // --- Path utilities ---
12001104
12011105 /**
@@ -1214,43 +1118,7 @@ define(function (require, exports, module) {
12141118 return fullPath ;
12151119 }
12161120
1217- /**
1218- * Check if a file has unsaved changes in the editor and return its content.
1219- * Used by the node-side Read hook to serve dirty buffer content to Claude.
1220- */
1221- function getFileContent ( params ) {
1222- const vfsPath = SnapshotStore . realToVfsPath ( params . filePath ) ;
1223- const doc = DocumentManager . getOpenDocumentForPath ( vfsPath ) ;
1224- if ( doc && doc . isDirty ) {
1225- return { isDirty : true , content : doc . getText ( ) } ;
1226- }
1227- return { isDirty : false , content : null } ;
1228- }
1229-
1230- /**
1231- * Apply an edit to the editor buffer immediately (called by node-side hooks).
1232- * The file appears as a dirty tab so subsequent Reads see the new content.
1233- * @param {Object } params - {file, oldText, newText}
1234- * @return {Promise<{applied: boolean, error?: string}> }
1235- */
1236- function applyEditToBuffer ( params ) {
1237- const deferred = new $ . Deferred ( ) ;
1238- _applySingleEdit ( params )
1239- . done ( function ( result ) {
1240- if ( result && result . previousContent !== undefined ) {
1241- _previousContentMap [ params . file ] = result . previousContent ;
1242- }
1243- deferred . resolve ( { applied : true } ) ;
1244- } )
1245- . fail ( function ( err ) {
1246- deferred . resolve ( { applied : false , error : err . message || String ( err ) } ) ;
1247- } ) ;
1248- return deferred . promise ( ) ;
1249- }
1250-
12511121 // Public API
12521122 exports . init = init ;
12531123 exports . initPlaceholder = initPlaceholder ;
1254- exports . getFileContent = getFileContent ;
1255- exports . applyEditToBuffer = applyEditToBuffer ;
12561124} ) ;
0 commit comments