@@ -97,11 +97,14 @@ final class HookEventHandler {
9797 func handleUserPromptSubmit(
9898 payload: ClaudeHookPayload
9999 ) -> ( statusCode: Int , response: ClaudeHookResponse ) {
100+ let traceID = makeOperationID ( prefix: " ups " )
101+ let handleStartedAt = Date ( )
100102 lastActivityBySession [ payload. sessionID] = Date ( )
101103
102104 log (
103105 " [HookEventHandler] UserPromptSubmit triggered " ,
104106 fields: [
107+ " traceID " : traceID,
105108 " sessionID " : payload. sessionID,
106109 " autoRestoreEnabled " : String ( ClaudeHookPreferences . autoRestoreOnPromptSubmit) ,
107110 " cwd " : payload. cwd ?? " nil "
@@ -133,6 +136,7 @@ final class HookEventHandler {
133136 " [HookEventHandler] UserPromptSubmit binding verification failed " ,
134137 level: . warn,
135138 fields: [
139+ " traceID " : traceID,
136140 " sessionID " : payload. sessionID,
137141 " pid " : String ( state. pid) ,
138142 " tty " : state. tty ?? " nil "
@@ -156,23 +160,50 @@ final class HookEventHandler {
156160 title: state. title,
157161 capturedAt: state. createdAt
158162 )
163+ log (
164+ " [HookEventHandler] UserPromptSubmit binding resolved " ,
165+ level: . debug,
166+ fields: [
167+ " traceID " : traceID,
168+ " sessionID " : payload. sessionID,
169+ " windowID " : String ( state. windowID) ,
170+ " resolveDurationMs " : String ( elapsedMilliseconds ( since: handleStartedAt) )
171+ ]
172+ )
159173 } else if let terminalCtx = payload. terminalCtx, terminalCtx. hasUsefulContext {
174+ let terminalResolveStart = Date ( )
160175 identity = WindowManager . shared. findWindowByTerminalContext ( terminalCtx)
176+ let terminalResolveMs = elapsedMilliseconds ( since: terminalResolveStart)
161177 if let identity {
162178 log (
163- " [HookEventHandler] UserPromptSubmit no binding, resolved via terminal context " ,
179+ " [HookEventHandler] UserPromptSubmit resolved via terminal context " ,
164180 fields: [
181+ " traceID " : traceID,
165182 " sessionID " : payload. sessionID,
166183 " resolvedWindowID " : String ( identity. windowID) ,
167- " app " : identity. appName ?? " unknown "
184+ " app " : identity. appName ?? " unknown " ,
185+ " terminalResolveMs " : String ( terminalResolveMs)
186+ ]
187+ )
188+ } else {
189+ log (
190+ " [HookEventHandler] UserPromptSubmit terminal context resolve returned nil " ,
191+ level: . warn,
192+ fields: [
193+ " traceID " : traceID,
194+ " sessionID " : payload. sessionID,
195+ " terminalResolveMs " : String ( terminalResolveMs)
168196 ]
169197 )
170198 }
171199 } else {
172200 log (
173201 " [HookEventHandler] UserPromptSubmit no binding and no terminal context " ,
174202 level: . warn,
175- fields: [ " sessionID " : payload. sessionID]
203+ fields: [
204+ " traceID " : traceID,
205+ " sessionID " : payload. sessionID
206+ ]
176207 )
177208 return (
178209 200 ,
@@ -202,6 +233,7 @@ final class HookEventHandler {
202233 log (
203234 " [HookEventHandler] UserPromptSubmit window not on main screen " ,
204235 fields: [
236+ " traceID " : traceID,
205237 " sessionID " : payload. sessionID,
206238 " windowID " : String ( identity. windowID)
207239 ]
@@ -224,9 +256,31 @@ final class HookEventHandler {
224256 }
225257
226258 if record. isValid ( mainScreenFrame: mainScreen. frame) {
259+ let restoreStart = Date ( )
260+ log (
261+ " [HookEventHandler] UserPromptSubmit calling ToggleEngine.restore " ,
262+ level: . info,
263+ fields: [
264+ " traceID " : traceID,
265+ " windowID " : String ( identity. windowID) ,
266+ " preRestoreMs " : String ( elapsedMilliseconds ( since: handleStartedAt) )
267+ ]
268+ )
227269 let success = engine. restore (
228270 windowID: identity. windowID,
229- triggerSource: " user_prompt_submit "
271+ triggerSource: " user_prompt_submit " ,
272+ traceID: traceID
273+ )
274+ let restoreMs = elapsedMilliseconds ( since: restoreStart)
275+ log (
276+ " [HookEventHandler] UserPromptSubmit restore completed " ,
277+ level: success ? . info : . warn,
278+ fields: [
279+ " traceID " : traceID,
280+ " success " : String ( success) ,
281+ " restoreMs " : String ( restoreMs) ,
282+ " totalMs " : String ( elapsedMilliseconds ( since: handleStartedAt) )
283+ ]
230284 )
231285 if success {
232286 return (
@@ -252,16 +306,25 @@ final class HookEventHandler {
252306 )
253307 }
254308 } else {
255- // corrupted state(两个 frame 都在主屏),清除
256309 engine. clear ( windowID: identity. windowID)
310+ log (
311+ " [HookEventHandler] UserPromptSubmit toggle record corrupted, cleared " ,
312+ level: . warn,
313+ fields: [
314+ " traceID " : traceID,
315+ " windowID " : String ( identity. windowID)
316+ ]
317+ )
257318 }
258319 }
259320
260321 log (
261322 " [HookEventHandler] UserPromptSubmit no toggle state found " ,
262323 fields: [
324+ " traceID " : traceID,
263325 " sessionID " : payload. sessionID,
264- " windowID " : String ( identity. windowID)
326+ " windowID " : String ( identity. windowID) ,
327+ " totalMs " : String ( elapsedMilliseconds ( since: handleStartedAt) )
265328 ]
266329 )
267330 return (
@@ -274,98 +337,6 @@ final class HookEventHandler {
274337 )
275338 }
276339
277- // MARK: - Perform Restore
278-
279- private func performRestoreFromState(
280- payload: ClaudeHookPayload ,
281- toggleState: WindowState
282- ) -> ( statusCode: Int , response: ClaudeHookResponse ) {
283- let wm = WindowManager . shared
284-
285- guard let origFrame = toggleState. originalFrame,
286- let tgtFrame = toggleState. targetFrame else {
287- return ( 200 , ClaudeHookResponse ( ok: true , code: " no_frame_data " , message: " No frame data " , sessionID: payload. sessionID, handled: false ) )
288- }
289-
290- let savedState = WindowManager . SavedWindowState (
291- id: " \( toggleState. pid) _ \( toggleState. tty ?? " none " ) " ,
292- pid: toggleState. pid,
293- bundleIdentifier: toggleState. bundleIdentifier,
294- appName: toggleState. appName,
295- windowID: toggleState. windowID,
296- windowNumber: toggleState. axWindowNumber,
297- title: toggleState. title,
298- originalFrame: WindowManager . RectPayload ( origFrame) ,
299- targetFrame: WindowManager . RectPayload ( tgtFrame) ,
300- sourceSpaceIndex: toggleState. sourceSpace,
301- targetSpaceIndex: nil ,
302- sourceYabaiDisplayIndex: toggleState. sourceYabaiDisp,
303- sourceDisplaySpaceIndex: toggleState. sourceDispSpace,
304- sourceDisplayIndex: toggleState. sourceDisplay,
305- sourceDisplayID: nil ,
306- targetDisplayIndex: toggleState. targetDisplay,
307- restoreReason: toggleState. toggleReason,
308- sessionID: toggleState. sessionID,
309- savedAt: toggleState. toggledAt ?? Date ( )
310- )
311-
312- wm. hydrateMemory ( from: savedState, window: nil )
313-
314- // 验证找到的窗口确实在 targetFrame 附近
315- if let resolvedWindow = wm. lastWindowElement,
316- let resolvedFrame = wm. frame ( of: resolvedWindow) {
317- if !toggleState. isNearTarget ( currentFrame: resolvedFrame) {
318- log (
319- " [HookEventHandler] UserPromptSubmit restore aborted: window moved from target pos resolvedX= \( resolvedFrame. origin. x) resolvedY= \( resolvedFrame. origin. y) " ,
320- level: . warn,
321- fields: [ " sessionID " : payload. sessionID]
322- )
323- SessionWindowRegistry . shared. clearToggleState ( windowID: toggleState. windowID)
324- return (
325- 200 ,
326- ClaudeHookResponse (
327- ok: true , code: " window_moved_skip " ,
328- message: " Window position changed, skipping stale restore " ,
329- sessionID: payload. sessionID, handled: false
330- )
331- )
332- }
333- }
334-
335- log (
336- " [HookEventHandler] UserPromptSubmit restoring window " ,
337- fields: [
338- " sessionID " : payload. sessionID,
339- " pid " : String ( toggleState. pid) ,
340- " tty " : toggleState. tty ?? " nil " ,
341- " app " : toggleState. appName ?? " unknown " ,
342- " windowID " : String ( toggleState. windowID) ,
343- " originalFrame " : String ( describing: origFrame) ,
344- " targetFrame " : String ( describing: tgtFrame)
345- ]
346- )
347-
348- wm. restore (
349- operationID: makeOperationID ( prefix: " hook-restore " ) ,
350- triggerSource: " user_prompt_submit "
351- )
352-
353- SessionWindowRegistry . shared. reactivate ( sessionID: payload. sessionID)
354- SessionWindowRegistry . shared. clearToggleState ( windowID: toggleState. windowID)
355-
356- SessionWindowRegistry . shared. setLastEventDescription (
357- " UserPromptSubmit 恢复窗口: \( toggleState. appName ?? " Unknown " ) "
358- )
359- return (
360- 200 ,
361- ClaudeHookResponse (
362- ok: true , code: " window_restored " ,
363- message: " Window restored to original position " ,
364- sessionID: payload. sessionID, handled: true
365- )
366- )
367- }
368-
369340 // MARK: - Stop
370341
371342 func handleStop(
0 commit comments