clitrigger 0.2.31 → 0.2.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/electron/main.cjs CHANGED
@@ -55,6 +55,15 @@ function attachImeWindowLogging(win, label) {
55
55
  imeDebugLog(label, { event: ev, visible: win.isVisible(), focused: win.isFocused() });
56
56
  });
57
57
  }
58
+ // Key arrival at the Chromium layer, before renderer dispatch. Composition
59
+ // events are the first renderer-side log point, so when TSF is stranded the
60
+ // log goes silent — this line distinguishes "key never reached webContents"
61
+ // from "key arrived but composition never started".
62
+ win.webContents.on('before-input-event', (_e, input) => {
63
+ if (!imeDebugEnabled) return;
64
+ if (input.type !== 'keyDown' || input.isAutoRepeat) return;
65
+ imeDebugLog(label, { event: 'key', key: input.key, code: input.code });
66
+ });
58
67
  }
59
68
 
60
69
  function readOrInitConfig() {
@@ -279,12 +288,21 @@ function createWindow(port) {
279
288
  mainWindow.webContents.on('did-create-window', (childWin) => {
280
289
  blockOffOriginNav(childWin.webContents);
281
290
  wireTerminalZoom(childWin.webContents, 'popout');
291
+ // A focus that FOLLOWS a real OS blur (lock screen, alt-tab) leaves the
292
+ // Windows TSF IME context stranded, so composition breaks — Hangul commits
293
+ // as detached jamo and the candidate window jumps to the corner — even
294
+ // though webContents.isFocused() still reads stale-true (ime-debug
295
+ // 2026-07-11). Force a rebind on post-blur refocus; keep skipping the
296
+ // blur-less redundant focus, whose focus() would instead reset a healthy
297
+ // TSF context (the original corner-jump this guard was added to prevent).
298
+ let wasBlurred = false;
299
+ childWin.on('blur', () => { wasBlurred = true; });
282
300
  childWin.on('focus', () => {
283
- // Skip when webContents already holds focus — a redundant programmatic
284
- // focus() resets the Windows IME (TSF) caret context, which is the
285
- // prime suspect for the candidate window jumping to the screen corner.
286
- if (childWin.isDestroyed() || childWin.webContents.isFocused()) return;
287
- imeDebugLog('popout', { event: 'focus-bridge' });
301
+ if (childWin.isDestroyed()) return;
302
+ const rebind = wasBlurred;
303
+ wasBlurred = false;
304
+ if (!rebind && childWin.webContents.isFocused()) return;
305
+ imeDebugLog('popout', { event: 'focus-bridge', rebind });
288
306
  childWin.webContents.focus();
289
307
  });
290
308
  attachImeWindowLogging(childWin, 'popout');
@@ -303,13 +321,20 @@ function createWindow(port) {
303
321
  // Windows lock-screen / screensaver hands the native HWND keyboard focus
304
322
  // off to the lock UI; on resume it doesn't always return to webContents,
305
323
  // leaving every input (SessionForm, SessionTerminal) dead until the user
306
- // minimizes and restores. Re-focus webContents on window focus — but only
307
- // when it doesn't already hold focus: a redundant programmatic focus()
308
- // resets the Windows IME (TSF) caret context (candidate window jumps to
309
- // the screen corner).
324
+ // minimizes and restores. Re-focus webContents on window focus — but a
325
+ // redundant programmatic focus() (no preceding blur) resets the Windows IME
326
+ // (TSF) caret context (candidate window jumps to the corner), so gate the
327
+ // rebind on an actual blur having happened first: after a real blur→focus
328
+ // cycle the TSF context is stranded and needs the rebind even though
329
+ // isFocused() reads stale-true (ime-debug 2026-07-11).
330
+ let mainWasBlurred = false;
331
+ mainWindow.on('blur', () => { mainWasBlurred = true; });
310
332
  mainWindow.on('focus', () => {
311
- if (mainWindow.isDestroyed() || mainWindow.webContents.isFocused()) return;
312
- imeDebugLog('mainWindow', { event: 'focus-bridge' });
333
+ if (mainWindow.isDestroyed()) return;
334
+ const rebind = mainWasBlurred;
335
+ mainWasBlurred = false;
336
+ if (!rebind && mainWindow.webContents.isFocused()) return;
337
+ imeDebugLog('mainWindow', { event: 'focus-bridge', rebind });
313
338
  mainWindow.webContents.focus();
314
339
  });
315
340
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clitrigger",
3
- "version": "0.2.31",
3
+ "version": "0.2.33",
4
4
  "description": "Spawn isolated git worktrees and orchestrate Claude/Gemini/Codex CLI tasks in parallel from a web UI.",
5
5
  "keywords": [
6
6
  "claude",