tabby-agentdeck 0.2.1 → 0.3.0

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.
@@ -27,6 +27,8 @@ export declare class AgentDeckService {
27
27
  private observer;
28
28
  private editing;
29
29
  private renderQueued;
30
+ /** Shift 물리 키가 눌려 있나 — IME 조합을 거친 Enter 는 shiftKey 가 지워져 온다 */
31
+ private shiftDownAt;
30
32
  /** rAF 가 멈춘 상황(창 가려짐)을 대비한 렌더 폴백 타이머 */
31
33
  private renderTimer;
32
34
  /** 직전에 적용한 폭 — 같은 값이면 DOM 을 다시 건드리지 않는다 */
@@ -213,6 +215,56 @@ export declare class AgentDeckService {
213
215
  * 브라우저 기본 붙여넣기여야 하고, 우리가 가로채면 아예 못 붙여넣게 된다.
214
216
  */
215
217
  private claimCtrlVKey;
218
+ /**
219
+ * Home / End 를 Tabby 핫키 테이블에서 걷어내 xterm 순정 경로로 돌려준다.
220
+ *
221
+ * 증상: 한글 마지막 음절이 조합 중일 때 Home/End 를 누르면 그 음절이 **커서를 따라간다**
222
+ * (2026-09-08 사용자 보고). 방향키는 멀쩡하다 — 그 차이가 곧 원인이다.
223
+ *
224
+ * 방향키는 핫키가 아니라서 xterm 의 키 경로를 그대로 탄다. 거기서는 조합 중에 아무 키나
225
+ * 들어오면 `CompositionHelper.keydown` 이 `_finalizeComposition(false)` 로 조합을
226
+ * **동기 전송**하고(`tabby-terminal/dist/index.js:44098`, `:44120` triggerDataEvent) 그다음
227
+ * 방향키를 보낸다 — 글자 먼저, 이동 나중이라 순서가 맞는다.
228
+ *
229
+ * Home/End 는 그 경로를 못 탄다. Tabby 가 `hotkeys.home` / `hotkeys.end` 로 선점해서
230
+ * `tabby-local/dist/index.js:613` 이 `sendInput('')` 를 **즉시** 쏘기 때문이다.
231
+ * xterm 을 거치지 않으니 조합 확정은 `compositionend` 경로로 밀리는데, 그쪽
232
+ * `_finalizeComposition(true)` 는 `setTimeout(..., 0)` 로 **비동기 전송**이다
233
+ * (`tabby-terminal/dist/index.js:44110`). 결국 커서 이동이 먼저 나가고 음절이 한 틱 뒤에
234
+ * 도착한다 — 앱은 옮겨간 자리에 글자를 찍는다.
235
+ *
236
+ * 그래서 고치는 방법은 핸들러를 더 얹는 게 아니라 **핫키를 놓아주는 것**이다. 비워두면
237
+ * xterm 이 Home/End 를 방향키와 똑같이 처리한다(조합 확정 → 키 전송). 되돌리려면
238
+ * `agentDeck.releaseHomeEnd: false` 후 설정에서 다시 지정하면 된다.
239
+ */
240
+ private releaseHomeEndHotkey;
241
+ /**
242
+ * Shift+Enter / Ctrl+Enter 를 document **캡처 단계**에서 직접 줄바꿈으로 바꾼다.
243
+ *
244
+ * 핫키(`agentdeck-newline`)에만 맡겼더니 한글을 치다 누르면 절반쯤 줄바꿈이 아니라
245
+ * **전송**이 됐다 (2026-09-08 사용자 보고: "50% 정도는 그냥 enter 로 들어간다").
246
+ * 진단로그에 증거가 남아 있다 — 오발신 순간 `enter-label` 이 발화했는데
247
+ * (`.agentdeck-diag.log` 00:54:59 `enter-label text="지금도 그랬"`), 그 핸들러는
248
+ * 수식키가 하나라도 눌려 있으면 맨 앞에서 빠져나간다. 즉 그 Enter 는 **shiftKey 가
249
+ * 지워진 채** 도착했다. 한글 IME 가 조합을 확정하며 Enter 를 한 번 삼키고, 뒤이어
250
+ * 내보내는 이벤트에는 Shift 상태를 싣지 않는 것이다. Shift 가 없으니 `Shift-Enter`
251
+ * 핫키에 매칭될 리 없고, xterm 은 평범한 Enter 로 0x0D 를 PTY 에 흘린다 —
252
+ * 쓰다 만 문장이 그대로 전송된다.
253
+ *
254
+ * 그래서 이벤트가 알려주는 수식키를 믿지 않고 **Shift 눌림을 우리가 따로 센다**.
255
+ * Shift 자체의 keydown/keyup 은 조합과 무관하게 그대로 오므로 IME 를 타지 않는다.
256
+ * 세어 둔 값이 썩는 것(키업 유실)을 막으려고 창 포커스가 나가거나, Shift 없이 평범한
257
+ * 글자가 들어오거나, 오래 묵으면(STALE_MS) 스스로 잊는다.
258
+ *
259
+ * 조합 중(`isComposing`/keyCode 229)인 Enter 는 손대지 않는다. 그건 "이 음절을
260
+ * 확정해라" 는 신호고 여기서 막으면 마지막 글자가 확정되지 못한 채 줄만 넘어간다.
261
+ * 확정이 끝나면 Enter 가 한 번 더 오니 그때 잡으면 글자 순서도 어긋나지 않는다.
262
+ *
263
+ * 캡처에서 전파를 끊는 이유는 claimCtrlVKey 와 같다 — 뒤에 걸린 경로(xterm 커스텀 키
264
+ * 핸들러 → pushKeyEvent, core 의 document 리스너, xterm 이 Enter 를 0x0D 로 흘리는
265
+ * 기본 동작)를 통째로 죽여야 줄바꿈이 정확히 한 번이 된다.
266
+ */
267
+ private claimShiftEnterKey;
216
268
  /**
217
269
  * Enter 를 눌러 프롬프트를 보낼 때, 그 프롬프트 원문을 작업 라벨로 삼는다.
218
270
  *
@@ -433,6 +485,12 @@ export declare class AgentDeckService {
433
485
  private disableBackgroundThrottling;
434
486
  private tick;
435
487
  private render;
488
+ /**
489
+ * 헤더 오른쪽의 상태별 집계 — `⏸ 2 ● 3 ○ 1` 처럼 0 이 아닌 상태만 급한 순서로 놓는다.
490
+ * 총 개수만 보이던 자리다. 세션을 여럿 띄우면 "몇 개가 나를 기다리나" 가 먼저 보여야 한다.
491
+ * 동적 텍스트는 textContent 로만 넣는다 (innerHTML 금지).
492
+ */
493
+ private renderHeadCount;
436
494
  private renderTab;
437
495
  /** 더블클릭 -> 작업 이름 인라인 편집 */
438
496
  private startLabelEdit;
package/dist/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { AgentDeckService } from './deck.service';
2
2
  import { AgentDeckProfileService } from './profile.service';
3
3
  import { WorkNotifyService } from './notify.service';
4
+ import { AgentDeckAlertService } from './alert.service';
5
+ import { AgentDeckRecoveryService } from './recovery.service';
4
6
  import './styles.scss';
5
7
  export * from './api';
6
8
  export default class AgentDeckModule {
7
- constructor(deck: AgentDeckService, profiles: AgentDeckProfileService, notify: WorkNotifyService);
9
+ constructor(deck: AgentDeckService, profiles: AgentDeckProfileService, notify: WorkNotifyService, alert: AgentDeckAlertService, recovery: AgentDeckRecoveryService);
8
10
  }