tabby-agentdeck 1.1.3 → 1.2.1

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.
Files changed (91) hide show
  1. package/CHANGELOG.md +150 -127
  2. package/LICENSE +21 -21
  3. package/README.ko.md +52 -590
  4. package/README.md +52 -562
  5. package/dist/accountBrowser.d.ts +4 -0
  6. package/dist/accountSession.d.ts +22 -0
  7. package/dist/accounts.d.ts +50 -0
  8. package/dist/agents.d.ts +15 -0
  9. package/dist/codexHooks.d.ts +23 -0
  10. package/dist/codexMeta.d.ts +87 -0
  11. package/dist/config.d.ts +110 -4
  12. package/dist/cursorVisibility.d.ts +33 -1
  13. package/dist/deck.service.d.ts +197 -20
  14. package/dist/devReload.service.d.ts +48 -0
  15. package/dist/devReload.stub.d.ts +19 -0
  16. package/dist/dock.d.ts +15 -0
  17. package/dist/i18n/af.d.ts +83 -0
  18. package/dist/i18n/bg.d.ts +83 -0
  19. package/dist/i18n/cs.d.ts +83 -0
  20. package/dist/i18n/da.d.ts +83 -0
  21. package/dist/i18n/de.d.ts +83 -0
  22. package/dist/i18n/en.d.ts +83 -0
  23. package/dist/i18n/es.d.ts +83 -0
  24. package/dist/i18n/fr.d.ts +83 -0
  25. package/dist/i18n/hr.d.ts +83 -0
  26. package/dist/i18n/id.d.ts +83 -0
  27. package/dist/i18n/it.d.ts +83 -0
  28. package/dist/i18n/ja.d.ts +83 -0
  29. package/dist/i18n/ko.d.ts +83 -0
  30. package/dist/i18n/pl.d.ts +83 -0
  31. package/dist/i18n/pt.d.ts +83 -0
  32. package/dist/i18n/ru.d.ts +83 -0
  33. package/dist/i18n/sidebar/af.d.ts +80 -0
  34. package/dist/i18n/sidebar/bg.d.ts +80 -0
  35. package/dist/i18n/sidebar/cs.d.ts +80 -0
  36. package/dist/i18n/sidebar/da.d.ts +80 -0
  37. package/dist/i18n/sidebar/de.d.ts +80 -0
  38. package/dist/i18n/sidebar/es.d.ts +80 -0
  39. package/dist/i18n/sidebar/fr.d.ts +80 -0
  40. package/dist/i18n/sidebar/hr.d.ts +80 -0
  41. package/dist/i18n/sidebar/id.d.ts +80 -0
  42. package/dist/i18n/sidebar/it.d.ts +80 -0
  43. package/dist/i18n/sidebar/ja.d.ts +80 -0
  44. package/dist/i18n/sidebar/pl.d.ts +80 -0
  45. package/dist/i18n/sidebar/pt.d.ts +80 -0
  46. package/dist/i18n/sidebar/ru.d.ts +80 -0
  47. package/dist/i18n/sidebar/sr.d.ts +80 -0
  48. package/dist/i18n/sidebar/sv.d.ts +80 -0
  49. package/dist/i18n/sidebar/tr.d.ts +80 -0
  50. package/dist/i18n/sidebar/uk.d.ts +80 -0
  51. package/dist/i18n/sidebar/zh-CN.d.ts +80 -0
  52. package/dist/i18n/sidebar/zh-TW.d.ts +80 -0
  53. package/dist/i18n/sr.d.ts +83 -0
  54. package/dist/i18n/sv.d.ts +83 -0
  55. package/dist/i18n/tr.d.ts +83 -0
  56. package/dist/i18n/uk.d.ts +83 -0
  57. package/dist/i18n/zh-CN.d.ts +83 -0
  58. package/dist/i18n/zh-TW.d.ts +83 -0
  59. package/dist/i18n.d.ts +3634 -0
  60. package/dist/index.d.ts +3 -1
  61. package/dist/index.js +109 -2
  62. package/dist/keybind.d.ts +121 -0
  63. package/dist/meta.d.ts +83 -0
  64. package/dist/nav.d.ts +29 -0
  65. package/dist/notify.service.d.ts +161 -1
  66. package/dist/project-root.d.ts +1 -1
  67. package/dist/reload.service.d.ts +57 -0
  68. package/dist/sessionLedger.d.ts +11 -6
  69. package/dist/sessionLedger.service.d.ts +3 -0
  70. package/dist/sessionMailbox.d.ts +31 -0
  71. package/dist/sessionSearch.d.ts +41 -0
  72. package/dist/sessionSearchPanel.d.ts +36 -0
  73. package/dist/sessionSlots.d.ts +9 -0
  74. package/dist/settings.component.d.ts +134 -8
  75. package/dist/sidebarI18n.d.ts +9 -0
  76. package/dist/sidebarLocales.d.ts +1 -0
  77. package/dist/status.service.d.ts +30 -2
  78. package/dist/statusLine.d.ts +27 -0
  79. package/dist/update.d.ts +19 -5
  80. package/dist/update.service.d.ts +29 -20
  81. package/dist/viewPanel.d.ts +13 -3
  82. package/dist/viewer.d.ts +2 -0
  83. package/hooks/agentdeck-codex-notify.ps1 +28 -25
  84. package/hooks/agentdeck-label.js +53 -53
  85. package/hooks/agentdeck-mailbox.mjs +103 -0
  86. package/hooks/agentdeck-notify.ps1 +43 -14
  87. package/hooks/agentdeck-status.ps1 +33 -33
  88. package/hooks/agentdeck-statusline.mjs +351 -0
  89. package/package.json +72 -68
  90. package/updater/agentdeck-update.ps1 +153 -130
  91. package/dist/index.js.map +0 -1
@@ -1,4 +1,5 @@
1
1
  import { NgZone } from '@angular/core';
2
+ import { LocaleService } from 'tabby-core';
2
3
  import { AppService, ConfigService, HostWindowService, HotkeysService, PlatformService, ProfilesService } from 'tabby-core';
3
4
  import { WorkStatusService } from './status.service';
4
5
  import { WorkNotifyService } from './notify.service';
@@ -60,8 +61,20 @@ export declare class AgentDeckService {
60
61
  private notify;
61
62
  private ledger;
62
63
  private zone;
64
+ private locale;
65
+ private readonly sessionSlots;
66
+ private pendingSessionOpens;
67
+ private syncSessionSlots;
63
68
  private sidebar;
64
69
  private listEl;
70
+ /** 사이드바 하단 "지금 이 탭" 줄 (모델·계정·한도). 목록과 버튼 줄 사이에 고정된다 */
71
+ private nowEl;
72
+ private accountPopup;
73
+ private accountPopupTab;
74
+ private accountSwitchBusy;
75
+ /** 지난 세션 서랍 — 목록 **밖**, "지금 이 탭" 줄 바로 위. 펼치면 위로 자란다 */
76
+ private resumeEl;
77
+ private historySearchPanel;
65
78
  private windowEl;
66
79
  private mainEl;
67
80
  private observer;
@@ -134,6 +147,35 @@ export declare class AgentDeckService {
134
147
  private lastSidebarW;
135
148
  /** 직전에 적용한 도킹 방향 — 크기가 같아도 방향이 바뀌면 다시 그려야 한다 */
136
149
  private lastDock;
150
+ /**
151
+ * 판정 결과를 pane 마다 **짧게** 기억한다 — `profileForPane` 은 화면에 글자를 쓸 때마다
152
+ * 불리는 자리라(커서 보정의 `enabled`, 키보드, 주기 검사) 매번 처음부터 다시 정하면
153
+ * ① 답이 판마다 흔들려 화면이 깜빡이고 ② 버퍼 60줄 훑기가 write 마다 돈다.
154
+ *
155
+ * 무효화 배선을 따로 두지 않고 **짧은 유통기한**으로 끝낸다 — 훅이 다른 에이전트를
156
+ * 말하면 그 다음 판(최대 PROFILE_MEMO_MS 뒤)에 반영되고, 사람 눈에는 즉시나 다름없다.
157
+ * 세션(pty)이 갈리면 그 자체로 무효다 — 같이 적어 두고 비교한다.
158
+ */
159
+ private profileMemo;
160
+ /**
161
+ * 회귀가 못 박아 둔 pane/tab 의 에이전트 (`__agentdeck.pinAgent`).
162
+ *
163
+ * **제품 동작에는 아무도 여기에 쓰지 않는다** — 비어 있는 Map 이고 `profileForPane` 이
164
+ * `size` 만 본다. 회귀가 끝나면 프로브가 스스로 비운다(`pinAgent(target, null)`).
165
+ * 배포본에 남는 것을 허용하는 이유는 `judge`·`agentOf` 와 같다 — 검증 도구가 제품 규칙의
166
+ * 사본을 들지 않게 하려면 제품이 창구를 열어 주는 편이 싸다.
167
+ */
168
+ private pinnedAgents;
169
+ /**
170
+ * 지금 못 박혀 있는 개수 — `WeakMap` 에는 `size` 가 없다.
171
+ *
172
+ * 굳이 `WeakMap` 인 이유: 이 진단구는 배포 번들에도 실리는데, 프로브가 예외로 죽어
173
+ * `pinAgent(target, null)` 을 못 부르면 강한 Map 은 그 pane 과 xterm 버퍼를 영원히 붙든다.
174
+ * 세는 값만 따로 두면 평소 경로(`profileForPane`)는 정수 비교 한 번으로 끝난다.
175
+ */
176
+ private pinnedCount;
177
+ /** 직전 창 박스의 왼쪽 위 (`left:top`) — 크기가 같아도 자리가 옮겨지면 다시 앉혀야 한다 */
178
+ private lastBoxKey;
137
179
  /** 경계선 드래그(크기)와 헤더 드래그(도킹 방향)를 맡는다 */
138
180
  private dock;
139
181
  private refitQueued;
@@ -198,7 +240,11 @@ export declare class AgentDeckService {
198
240
  private lastPasteAt;
199
241
  /** 이 시각까지는 PTY 로 나가는 입력을 진단 로그에 남긴다 (붙여넣기가 두 번 들어가는지 보려고) */
200
242
  private pasteProbeUntil;
201
- constructor(app: AppService, config: ConfigService, status: WorkStatusService, hotkeys: HotkeysService, profiles: ProfilesService, hostWindow: HostWindowService, platform: PlatformService, notify: WorkNotifyService, ledger: SessionLedgerService, zone: NgZone);
243
+ constructor(app: AppService, config: ConfigService, status: WorkStatusService, hotkeys: HotkeysService, profiles: ProfilesService, hostWindow: HostWindowService, platform: PlatformService, notify: WorkNotifyService, ledger: SessionLedgerService, zone: NgZone, locale?: LocaleService | null);
244
+ private get sidebarLang();
245
+ private ui;
246
+ private uiMarkup;
247
+ private refreshSidebarLanguage;
202
248
  init(): void;
203
249
  /**
204
250
  * 같은 키로는 한 번만 남기는 진단.
@@ -335,6 +381,47 @@ export declare class AgentDeckService {
335
381
  * 감시 루프도 돌지 않는다. 그 셋이 예전에 화면을 깨뜨린 장본인이다.
336
382
  */
337
383
  private announceSizeOnOpen;
384
+ /**
385
+ * 우리 새 탭 키와 겹치는 순정 `new-tab` 키만 걷어낸다. 기본은 Ctrl+T / ⌘+T다.
386
+ *
387
+ * 순정 처리자(`tabby-local` LocalTerminalModule → `terminal.openTab()`)는 같은 `hotkey$` 를
388
+ * 구독하고 있어서, 두 id 에 같은 키가 매여 있으면 HotkeysService 가 하나만 고르긴 하지만
389
+ * (길이 같은 후보 중 config 삽입 순서 앞의 것 — `ensurePasteHotkey` 주석) 그게 어느 쪽일지는
390
+ * 저장 순서에 달려 있다. 표에서 떼는 것이 유일하게 확실한 방법이다.
391
+ *
392
+ * `claimNewTabKey: false` 면 건드리지 않는다 — 그때는 우리 핸들러도 무시하므로(hotkey$ 구독)
393
+ * 순정 그대로다. 게이트를 켰다 끄면 뗀 키는 되살리지 않는다: 사람이 손으로 바꾼 표를 우리가
394
+ * 다시 덮는 것이 더 나쁘고, 설정 창에서 한 줄이면 복구된다.
395
+ */
396
+ private ensureNewTabHotkey;
397
+ private ensureSplitHotkeys;
398
+ /**
399
+ * 순정 `close-pane`(포커스된 분할 패널 닫기)이 비어 있으면 `closePaneKey` 로 채운다.
400
+ *
401
+ * 왜 필요한가 — Tabby 기본표는 `close-pane: []` 로 온다(config.yaml hotkeys). 패널을 닫는
402
+ * 다른 길은 우클릭 컨텍스트 메뉴인데 agentdeck 이 짧은 우클릭을 복사/붙여넣기로 가져가
403
+ * (`claimRightClick`) 길게 눌러야만 메뉴가 뜬다. 셸에 `exit` 를 치는 방법만 남는다.
404
+ *
405
+ * 채우기만 하고 빼앗지는 않는다 — `ensureNewTabHotkey` 와 달리 순정 처리자와 경쟁하는
406
+ * 것이 아니라 순정 처리자를 **쓰는** 것이므로 표에 키를 넣어 주면 끝이다. 사람이 이미
407
+ * 무언가 매어 두었으면(비어 있지 않으면) 그대로 둔다. `closePaneKey` 가 빈 문자열이면 건너뛴다.
408
+ */
409
+ private ensureClosePaneHotkey;
410
+ /**
411
+ * 우리 복구 키와 겹치는 순정 `rename-tab` 키만 걷어낸다. 기본은 Ctrl+R이다.
412
+ *
413
+ * 방식과 이유는 `ensureNewTabHotkey` 와 같다 — 같은 키가 두 id 에 매여 있으면 어느 쪽이
414
+ * 발화할지 저장 순서에 달리므로 표에서 떼는 것만이 확실하다. 탭 이름 바꾸기는 사이드바
415
+ * 더블클릭이 주 경로라 키를 잃어도 아쉽지 않다.
416
+ *
417
+ * 옛 기본값 이관 — 2026-09-17 전에는 기본이 `Ctrl-Shift-U` 였고 Tabby 는 기본값도 config.yaml 에
418
+ * 그대로 써 두므로, 사람이 손대지 않은 표에도 `['Ctrl-Shift-U']` 가 남아 있다. **정확히 옛 기본값
419
+ * 하나뿐일 때만** 새 기본값으로 바꾼다 — 다른 값이면 사람이 고른 것이니 건드리지 않는다.
420
+ *
421
+ * `claimRepairKey: false` 면 건드리지 않고 우리 핸들러도 무시한다(hotkey$ 구독). 게이트를 켰다
422
+ * 끄면 뗀 키는 되살리지 않는다 — `ensureNewTabHotkey` 와 같은 판단.
423
+ */
424
+ private ensureRepairHotkey;
338
425
  /**
339
426
  * Ctrl+V 를 **핫키 테이블에서 걷어낸다** (실제 처리는 claimCtrlVKey 가 한다).
340
427
  *
@@ -604,15 +691,36 @@ export declare class AgentDeckService {
604
691
  /**
605
692
  * 이 탭에서 도는 에이전트 CLI 가 뭔지 — 상태 감지 패턴과 이미지 붙여넣기 키가 앱마다 다르다.
606
693
  *
607
- * 프로세스 트리를 먼저 본다 (`session.getChildProcesses()`). npm 전역 래퍼를 거치면
608
- * 커맨드가 node/pwsh 로만 보일 수 있어서, 그때는 탭 제목으로 한 번 더 본다.
609
- * 어느 조각을 어떤 순서로 보는지는 `agents.ts` 의 프로필이 정한다 — 이 함수에 남은 일은
610
- * pane 에서 근거(프로세스 이름·제목)를 모으는 것뿐이다.
694
+ * **훅이 말해 준 값이 추측보다 앞선다** (`notify.agentOf`). 훅 스크립트는
695
+ * `-Agent claude|codex|gemini` 로 불리므로 자기가 누구인지 알고 있다. 명령줄(WMI)·탭 제목
696
+ * 추측은 훅이 없는 탭의 폴백이고 흔들린다: npm 전역 래퍼로 띄운 gemini 는 자식이
697
+ * `node.exe` 하나뿐이고 제목에도 이름이 없었다(2026-09-08 실측).
611
698
  *
612
- * `via` 를 함께 돌려주는 이유: 나중에 "왜 codex 로 잡혔나" 를 가르려면 프로세스에서
613
- * 나온 판정인지 제목(신뢰도가 낮다)에서 나온 판정인지가 유일한 단서다.
699
+ * 단 **프로세스 이름이 에이전트를 확실히 가리키면 그것이 훅보다 앞선다.** 훅 값은 보고한
700
+ * 세션이 살아 있을 때만 사실이고, 그 세션이 끝났다고 알려 주는 훅은 없다 — 같은 탭에서
701
+ * claude 를 끄고 훅 없는 codex 를 띄우면 옛 값이 남는다. (자식이 없는 것은 근거로 쓰지 않는다 —
702
+ * 빈 목록은 믿을 만한 종료 신호가 아니다. 그래서 claude 를 끄고 맨 셸로 남은 탭은 다음 훅 보고 전까지 옛 값을 든다.)
703
+ *
704
+ * 순서: 프로세스 이름(확실할 때) → 훅 → 그 pid 의 명령줄(WMI) → 탭 제목. 어느 조각으로
705
+ * 무엇을 알아보는지는 `agents.ts` 의 프로필이 정한다 — 이 함수는 근거를 모으기만 한다.
706
+ *
707
+ * `via` 를 함께 돌려주는 이유: 나중에 "왜 codex 로 잡혔나" 를 가르려면 훅이 말한 것인지
708
+ * 프로세스에서 나온 판정인지 제목(신뢰도가 낮다)에서 나온 판정인지가 유일한 단서다.
614
709
  */
615
710
  private detectAgentApp;
711
+ /**
712
+ * 훅이 고정해 둔 **상태**도 함께 버린다 — 정체(`forgetAgent`)를 버리는 자리에서 같이 부른다.
713
+ *
714
+ * 정체만 버리면 절반이다. 같은 탭에서 claude 를 끄고 다른 CLI 를 띄우면 `hookAgent` 는
715
+ * 지워지지만 `status` 는 여전히 `pinned` 라(`setManual` 이 세운다) 새 CLI 의 출력이
716
+ * `setAuto` 로 아무리 들어와도 전부 무시된다 — 배지가 **끝난 세션의 마지막 상태**(보통
717
+ * `완료`나 `진행중`)에 박혀 있게 된다. 훅이 없는 CLI 로 갈아탔으면 그 탭에는 배지를
718
+ * 되살릴 길이 영영 없다.
719
+ *
720
+ * `unpin` 은 상태를 idle 로 되돌린다 — 지금 그 탭에서 도는 것은 **다른 프로그램**이고,
721
+ * 옛 세션의 상태를 물려받을 이유가 없다. 새 CLI 에도 훅이 있으면 첫 보고가 다시 고정한다.
722
+ */
723
+ private forgetHookStatus;
616
724
  /**
617
725
  * 그 pid 들의 명령줄을 한 줄로 이어 돌려준다 (식별 폴백 전용).
618
726
  *
@@ -649,6 +757,8 @@ export declare class AgentDeckService {
649
757
  * 영영 매칭되지 않는다.
650
758
  */
651
759
  private profileForPane;
760
+ /** `profileForPane` 의 본체 — 캐시를 거치지 않는 실제 판정 */
761
+ private resolveProfileForPane;
652
762
  private profileForTab;
653
763
  /**
654
764
  * pane.sendInput 을 한 번 감싸 PTY 로 나간 입력을 진단 로그에 남긴다.
@@ -728,6 +838,24 @@ export declare class AgentDeckService {
728
838
  private applyOpacity;
729
839
  /** 지금 사이드바가 붙어 있는 방향 (설정값이 망가져 있으면 오른쪽으로 본다) */
730
840
  private get dockSide();
841
+ /**
842
+ * 배치의 기준이 되는 **눈에 보이는** 창 영역 — viewport 와 `.window` 의 교집합.
843
+ *
844
+ * `windowEl.clientWidth` 를 그대로 쓰면 안 된다. `.window` 의 박스가 viewport 보다 넓어져
845
+ * 있는 화면이 실제로 나온다 (2026-09-14 실측: 설정 탭에서 터미널 오른쪽에 100px 쯤 빈 띠가
846
+ * 남고, 사이드바는 그만큼 화면 밖으로 밀려 검색창·줄이 오른쪽에서 잘렸다). 그 폭으로 나누면
847
+ * 사이드바 몫의 일부가 화면 밖에 떨어지고, 화면 안에서는 아무도 안 쓰는 띠로 보인다.
848
+ * 보이는 범위로 잘라 두면 창이 어떤 크기든 그 안에서만 나눈다.
849
+ */
850
+ private visibleBox;
851
+ /**
852
+ * 절대 배치의 기준점 — 컨테이닝 블록(위치 지정 조상의 **패딩 박스**) 왼쪽 위를 viewport 좌표로.
853
+ *
854
+ * `right: 0` 으로 가장자리에 붙이던 것을 실좌표(`left`)로 바꾼 이유가 이것이다 —
855
+ * 그 `0` 이 어느 박스의 오른쪽인지가 화면마다 다르고, 그 박스가 화면보다 넓으면
856
+ * 사이드바가 화면 밖에 앉는다. 기준점을 재서 좌표를 직접 주면 어느 쪽이든 같은 자리다.
857
+ */
858
+ private anchorOrigin;
731
859
  /**
732
860
  * 사이드바가 차지할 크기를 px 로 정한다 — 좌/우면 폭, 상/하면 높이.
733
861
  *
@@ -893,29 +1021,48 @@ export declare class AgentDeckService {
893
1021
  private renderPlan;
894
1022
  private render;
895
1023
  /**
896
- * 그룹 끝에 **지난 세션** 줄을 붙인다 — `⟲ 지난 세션 5` 한 줄(기본 접힘)과, 펴면 그 목록.
1024
+ * 사이드바 하단의 "지금 이 탭" 줄 — 활성 탭에서 도는 모델·계정·컨텍스트%·한도.
897
1025
  *
898
- * 살아 있는 탭 뒤에 오는 이유: 사이드바의 주인공은 지금 도는 작업이고 이건 그 밑의 서랍이다.
899
- * 검색·상태필터가 걸려 있을 때는 아예 그리지 않는다 — 그 둘은 **탭**을 좁히는 장치라
900
- * (`tabMatchesFilter`) 죽은 세션에는 적용되지 않는데, 걸러진 화면에 안 걸러진 줄이 섞이면
901
- * "검색 결과" 라는 말이 깨진다.
1026
+ * **활성 탭 하나만 그린다.** 줄마다 붙이지 않는 이유는 폭이다 — 200px 사이드바에서
1027
+ * 모델 이름과 한도 세 칸은 제목·상태 배지와 자리를 다툰다. 대신 탭을 바꾸면 다시 그려지는데,
1028
+ * 그건 따로 배선할 것도 없다(`activeTabChange$` 가 이미 `scheduleRender` 를 부른다).
1029
+ *
1030
+ * 값이 없으면 **줄을 통째로 접는다**(`hidden`). "알 수 없음" 을 그려 두면 자리만 차지하면서
1031
+ * "statusLine 래퍼가 안 걸렸다" 와 "걸렸는데 아직 첫 보고가 안 왔다" 가 화면에서 같아진다.
902
1032
  */
903
- private renderResume;
1033
+ private showAccountPicker;
1034
+ private renderNow;
1035
+ private renderMetaGauges;
904
1036
  /**
905
- * 이 줄 묶음이 덮는 그룹 키들.
1037
+ * 사이드바 바닥의 **지난 세션** 서랍 — `⟲ 지난 세션 128` 한 줄(기본 접힘)과, 펴면 그 목록.
1038
+ *
1039
+ * **목록(`.ad-list`) 밖, "지금 이 탭" 줄 바로 위에 고정된다.**
1040
+ * 예전에는 그룹마다 하나씩 목록 안에 달려 있었는데, 탭이 몇 개만 되어도 서랍이 화면 밖으로
1041
+ * 밀려나 "있는 줄도 몰랐다". 바닥에 붙여 두면 언제나 같은 자리에 있고, 펼치면 위로 자란다
1042
+ * (2026-09-14 유저 지시).
1043
+ *
1044
+ * **줄 수를 자르지 않는다** — 기록해 둔 세션은 전부 들어가고, 서랍은 다섯 줄 남짓만 보이는
1045
+ * 스크롤 상자다 (2026-09-14 유저: "기껏 저장한걸 굳이 안쓸 이유는 없을 듯").
1046
+ * 그러니 `resumeListLimit`·`resumeListDays` 의 기본값은 0(무제한)이다.
906
1047
  *
907
- * 헤더를 그리는 화면에서는 `group.key` 하나지만, **그룹이 하나뿐이면 사이드바는 헤더를
908
- * 안 그리고 목록을 평면 한 덩이로 만든다**(`renderPlan`) — 그때 `group.key` 는 `null`(기타)이
909
- * 되어 어떤 세션도 안 걸린다. 프로젝트를 하나만 열어 둔 화면이 가장 흔하므로 그대로 두면
910
- * 기능이 평소에 안 보인다. 그래서 **그 덩이에 실제로 들어 있는 탭들의 키**를 모아 쓴다.
1048
+ * 검색어가 걸려 있으면 **여기에도 적용한다**. 서랍이 목록 안에 있던 시절에는 검색 중에
1049
+ * 서랍을 통째로 감췄는데(걸러진 화면에 안 걸러진 줄이 섞이면 "검색 결과" 가 깨진다),
1050
+ * 이제는 늘 보이는 자리라 감추면 "검색하면 지난 세션이 사라진다" 가 된다.
911
1051
  */
912
- private resumeKeysOf;
1052
+ private renderResumeDrawer;
913
1053
  /** 지금 탭이 들고 있는 세션 — `세션id -> 탭 식별용 문자열`. 되돌아올 탭은 `resumeTabs` 가 들고 있다 */
914
1054
  private liveSessionIds;
915
1055
  /** 지난 세션 서랍이 펴져 있나. `collapsedGroups` 와 **반대로 펼침을 적는다** — 기본이 접힘이라서 */
916
1056
  private isResumeExpanded;
917
1057
  private toggleResumeExpanded;
918
- private renderResumeHead;
1058
+ /**
1059
+ * 서랍 머리줄을 채운다 (엘리먼트는 사이드바를 만들 때 한 번 만들어 둔 것을 다시 쓴다).
1060
+ *
1061
+ * 머리줄만 `innerHTML` 을 다시 쓰지 않고 자리만 채우는 이유 — 여기에는 클릭 핸들러가
1062
+ * 붙어 있고, 렌더마다 노드를 갈아치우면 핸들러도 매번 다시 걸어야 한다.
1063
+ * 펼침 표시는 `▴`/`▾` 가 아니라 **위로 열린다는 방향** 그대로 쓴다.
1064
+ */
1065
+ private fillResumeHead;
919
1066
  private renderResumeRow;
920
1067
  /**
921
1068
  * 필터에 걸린 것이 하나도 없을 때의 한 줄.
@@ -973,6 +1120,19 @@ export declare class AgentDeckService {
973
1120
  * 동적 텍스트는 textContent 로만 넣는다 (innerHTML 금지).
974
1121
  */
975
1122
  private renderHeadCount;
1123
+ /**
1124
+ * 제목 줄 머리에 에이전트 표식을 붙인다 — 어느 줄이 Claude 고 Codex 고 Gemini 인지
1125
+ * 글자를 읽지 않고 갈리게 (2026-09-14 요청: "뭐가 코덱스고 뭐가 클로드인지 구분이 안돼").
1126
+ *
1127
+ * **제목 텍스트를 건드리지 않는다.** 표식을 제목 문자열에 이어 붙이면 `.ad-title` 의
1128
+ * textContent 가 바뀌어 그것으로 줄을 찾는 회귀 프로브(`tools/probe-resume.js`)가 엉뚱한
1129
+ * 줄을 짚는다. 그래서 **글자가 없는 자식 노드**(빈 span + 인라인 SVG)로만 얹는다 —
1130
+ * 툴팁도 SVG 안 `<title>` 이 아니라 그 span 의 `title` 속성에 건다(같은 이유).
1131
+ *
1132
+ * 못 알아본 탭(보통 셸)에는 아무것도 그리지 않는다. 셋 중 하나를 고를 근거가 없을 때
1133
+ * 기본값을 그리면 "Claude 인 줄 알았는데 셸" 이라는 오독을 만든다.
1134
+ */
1135
+ private decorateAgent;
976
1136
  private renderTab;
977
1137
  /**
978
1138
  * 키보드가 지금 어디를 갖고 있나 — **`document.activeElement` 가 유일한 출처다.**
@@ -1049,6 +1209,23 @@ export declare class AgentDeckService {
1049
1209
  * 헤더에서는 넘기지 않는다 — 접었다 펴 보는 것은 아직 목록을 훑는 중이다.
1050
1210
  */
1051
1211
  private activateNav;
1212
+ /**
1213
+ * `Ctrl-N` — 사이드바에 **보이는 순서**로 N 번째 세션으로 간다 (`pickJumpTarget`).
1214
+ *
1215
+ * ↑↓ 와 달리 훑어보기가 아니라 **바로 전환**이다. 사람이 번호를 누르는 것은 이미 어디로
1216
+ * 갈지 정했다는 뜻이고, 포커스만 옮기면 Enter 를 한 번 더 눌러야 해서 키 두 번짜리
1217
+ * `Ctrl-L` + ↑↓ 와 다를 것이 없어진다.
1218
+ *
1219
+ * **키보드 내비게이션 모드에 들어가지 않는다.** 목록에 포커스를 주면 그 다음 타이핑이
1220
+ * 터미널로 안 가고(`navMode === 'list'`) 사람은 방금 연 세션에 곧바로 치려던 참이다.
1221
+ * 그래서 `navFocus` 만 그 줄로 맞춰 둔다 — 이어서 `Ctrl-L` 을 누르면 방금 간 줄에서
1222
+ * 훑기가 시작된다.
1223
+ *
1224
+ * 사이드바를 껐거나(`enabled: false`) 키보드 조작을 끈 사람(`keyboardNav: false`)에게는
1225
+ * 이 키도 없다 — `navReady` 하나로 판정한다. 사이드바가 안 보이는데 "보이는 순서로 N 번째"
1226
+ * 는 말이 되지 않고, 끈 기능의 키만 살아 있으면 터미널이 `Ctrl-N` 을 영영 못 받는다.
1227
+ */
1228
+ private jumpToRow;
1052
1229
  /**
1053
1230
  * Esc — **보이는 것부터 되돌린다.** 걸린 검색어·상태 필터가 있으면 그것을 먼저 지우고
1054
1231
  * 포커스는 사이드바에 남기고, 지울 것이 없을 때 비로소 터미널로 나간다.
@@ -0,0 +1,48 @@
1
+ import { AppService, ConfigService, HotkeysService, NotificationsService } from 'tabby-core';
2
+ import { AgentDeckReloadService } from './reload.service';
3
+ /**
4
+ * 개발 빌드에만 들어 있는 표식 — 새 dist 에 이게 없으면 리로드하지 않는다.
5
+ * 배포 빌드(스텁)나 다른 세션이 기능 없이 덮어쓴 dist 로 갈아타면 감시 코드가 없어
6
+ * 그 뒤로 빌드를 해도 아무 일이 없기 때문이다(2026-09-14 실측: 동시 빌드가 기능 없는 dist 로 덮었다).
7
+ * `tools/check-release.js` 는 반대로 배포 빌드에 이게 **없어야** 통과시킨다.
8
+ */
9
+ export declare const DEV_BUILD_MARKER = "agentdeck-dev-reload";
10
+ /**
11
+ * 개발 설치인가 — 배포본에는 `src/`·`webpack.config.js` 가 없다(`package.json` 의 `files`).
12
+ * `__dirname` 은 `<플러그인>/dist` 이고 junction 설치면 node 가 소스 트리 실경로로 풀어 준다.
13
+ */
14
+ export declare function isDevInstall(): boolean;
15
+ /** 핫키 선언 — 스텁은 빈 배열을 돌려주므로 핫키 id 문자열이 배포 번들에 남지 않는다 */
16
+ export declare function devHotkeys(): {
17
+ id: string;
18
+ name: string;
19
+ }[];
20
+ /**
21
+ * **개발용 라이브 리로드** — `npm run build` 만 하면 세션을 살린 채 새 플러그인으로 갈아탄다.
22
+ *
23
+ * 리로드 자체(복구 토큰·창 리로드·탭 재부착)는 `reload.service.ts` 가 한다 — 자동 업데이트도
24
+ * 같은 것을 쓰기 때문에 배포본에 실린다. **여기 남은 것은 "언제 부를까" 뿐이다**:
25
+ * `dist/index.js` 감시와 핫키. 그 둘은 개발 설치에서만 존재한다(배포 번들에서는 스텁으로 빠진다).
26
+ */
27
+ export declare class AgentDeckDevReloadService {
28
+ private app;
29
+ private config;
30
+ private hotkeys;
31
+ private notifications;
32
+ private reloader;
33
+ /** 설정 화면이 개발자 옵션을 보일지 가르는 값 — 스텁은 항상 false */
34
+ readonly available: boolean;
35
+ private watcher;
36
+ private debounce;
37
+ private startMtime;
38
+ constructor(app: AppService, config: ConfigService, hotkeys: HotkeysService, notifications: NotificationsService, reloader: AgentDeckReloadService);
39
+ init(): void;
40
+ /** 개발자 옵션이 켜져 있나 */
41
+ enabled(): boolean;
42
+ /** 설정 화면의 `지금 리로드` 버튼과 핫키가 부르는 입구. 실제로 예약됐으면 `true` */
43
+ reload(reason: string): Promise<boolean>;
44
+ /** 파일 안에 개발 빌드 표식이 있나 — 400KB 한 번 읽는 값이라 빌드마다 해도 싸다 */
45
+ private isDevBuild;
46
+ /** `dist/index.js` 가 바뀌면(= npm run build 완료) 리로드 */
47
+ private watch;
48
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * npm 배포 빌드용 빈 껍데기 — `devReload.service.ts` 대신 끼워진다(webpack `--env release`).
3
+ *
4
+ * 개발용 라이브 리로드는 GitHub 소스에는 있지만 배포 번들에는 **코드째로 없어야** 한다.
5
+ * import 하는 쪽(index.ts·hotkeys.ts·settings.component.ts)은 그대로 두고 모듈만 바꾸므로
6
+ * 공개하는 이름과 모양은 원본과 같게 맞춘다. 원본의 문자열 표식이 여기 들어오면
7
+ * `tools/check-release.js` 가 배포를 막는다 — 이 파일에 그 문자열을 쓰지 말 것.
8
+ */
9
+ export declare function isDevInstall(): boolean;
10
+ export declare function devHotkeys(): {
11
+ id: string;
12
+ name: string;
13
+ }[];
14
+ export declare class AgentDeckDevReloadService {
15
+ readonly available = false;
16
+ init(): void;
17
+ enabled(): boolean;
18
+ reload(_reason: string): Promise<void>;
19
+ }
package/dist/dock.d.ts CHANGED
@@ -15,6 +15,19 @@ export interface DockHost {
15
15
  sidebar: HTMLElement;
16
16
  /** 사이드바가 얹혀 있는 `.window` */
17
17
  windowEl: HTMLElement;
18
+ /**
19
+ * 배치의 기준이 되는 **보이는** 창 영역 (viewport ∩ `.window`). 없으면 `.window` 박스를 쓴다.
20
+ *
21
+ * 이게 없으면 `.window` 가 화면보다 넓어져 있을 때 경계선을 끌 때마다 **화면 밖까지 센 폭**이
22
+ * 저장된다 — 사용자는 200px 를 만들었는데 설정에는 500px 이 들어가고, 그 차이만큼 사이드바가
23
+ * 잘려 보인다 (2026-09-14). 부르는 쪽(deck.service)이 같은 기준을 쥐고 있으므로 여기로 받는다.
24
+ */
25
+ getBox?(): {
26
+ left: number;
27
+ top: number;
28
+ width: number;
29
+ height: number;
30
+ };
18
31
  getDock(): DockSide;
19
32
  setDock(side: DockSide): void;
20
33
  /**
@@ -40,6 +53,8 @@ export declare class DockController {
40
53
  private dragFrom;
41
54
  private hoverSide;
42
55
  constructor(host: DockHost);
56
+ /** 창 영역 — `getBox` 가 있으면 그 값(보이는 범위), 없으면 `.window` 박스 */
57
+ private box;
43
58
  /** 사이드바가 만들어진 뒤 한 번 부른다 */
44
59
  install(): void;
45
60
  /** 도킹 방향이 바뀌면 핸들 위치도 반대편으로 옮겨야 한다 */
@@ -0,0 +1,83 @@
1
+ /** Afrikaans */
2
+ declare const _default: {
3
+ 'layout.title': string;
4
+ 'layout.desc': string;
5
+ 'reset.title': string;
6
+ 'reset.desc': string;
7
+ 'reset.now': string;
8
+ 'reset.default': string;
9
+ 'reset.btn': string;
10
+ 'dock.left': string;
11
+ 'dock.right': string;
12
+ 'dock.top': string;
13
+ 'dock.bottom': string;
14
+ 'size.width': string;
15
+ 'size.height': string;
16
+ 'size.auto': string;
17
+ 'norecover.title': string;
18
+ 'norecover.desc': string;
19
+ 'resume.title': string;
20
+ 'resume.desc': string;
21
+ 'agents.head': string;
22
+ 'agents.intro': string;
23
+ 'agents.unsupported.title': string;
24
+ 'agents.unsupported.desc': string;
25
+ 'state.on': string;
26
+ 'state.off': string;
27
+ 'state.half': string;
28
+ 'btn.install': string;
29
+ 'btn.remove': string;
30
+ 'claude.title': string;
31
+ 'claude.desc': string;
32
+ 'codex.title': string;
33
+ 'codex.desc': string;
34
+ 'codex.disabled': string;
35
+ 'root.head': string;
36
+ 'root.intro': string;
37
+ 'root.use.title': string;
38
+ 'root.use.desc': string;
39
+ 'root.name.title': string;
40
+ 'root.cwd.title': string;
41
+ 'root.cwd.desc': string;
42
+ 'root.browse': string;
43
+ 'root.command.title': string;
44
+ 'diag.head': string;
45
+ 'diag.intro': string;
46
+ 'diag.screen.title': string;
47
+ 'diag.screen.desc': string;
48
+ 'diag.collect.title': string;
49
+ 'diag.collect.desc': string;
50
+ 'diag.made': string;
51
+ 'diag.btn': string;
52
+ 'dev.head': string;
53
+ 'dev.use.title': string;
54
+ 'dev.use.desc': string;
55
+ 'dev.now.title': string;
56
+ 'dev.now.desc': string;
57
+ 'dev.btn': string;
58
+ footer: string;
59
+ 'keys.head': string;
60
+ 'keys.intro': string;
61
+ 'keys.change': string;
62
+ 'keys.default': string;
63
+ 'keys.cancel': string;
64
+ 'keys.force': string;
65
+ 'keys.press': string;
66
+ 'keys.conflict': string;
67
+ 'keys.unbound': string;
68
+ 'keys.stock': string;
69
+ 'keys.digit': string;
70
+ 'keys.fixed': string;
71
+ 'keys.item.newtab': string;
72
+ 'keys.item.jump': string;
73
+ 'keys.item.focus': string;
74
+ 'keys.item.view': string;
75
+ 'keys.item.repair': string;
76
+ 'keys.item.splitright': string;
77
+ 'keys.item.splitbottom': string;
78
+ 'keys.item.closepane': string;
79
+ 'keys.item.newline': string;
80
+ 'keys.item.toggle': string;
81
+ 'keys.item.viewmode': string;
82
+ };
83
+ export default _default;
@@ -0,0 +1,83 @@
1
+ /** Български */
2
+ declare const _default: {
3
+ 'layout.title': string;
4
+ 'layout.desc': string;
5
+ 'reset.title': string;
6
+ 'reset.desc': string;
7
+ 'reset.now': string;
8
+ 'reset.default': string;
9
+ 'reset.btn': string;
10
+ 'dock.left': string;
11
+ 'dock.right': string;
12
+ 'dock.top': string;
13
+ 'dock.bottom': string;
14
+ 'size.width': string;
15
+ 'size.height': string;
16
+ 'size.auto': string;
17
+ 'norecover.title': string;
18
+ 'norecover.desc': string;
19
+ 'resume.title': string;
20
+ 'resume.desc': string;
21
+ 'agents.head': string;
22
+ 'agents.intro': string;
23
+ 'agents.unsupported.title': string;
24
+ 'agents.unsupported.desc': string;
25
+ 'state.on': string;
26
+ 'state.off': string;
27
+ 'state.half': string;
28
+ 'btn.install': string;
29
+ 'btn.remove': string;
30
+ 'claude.title': string;
31
+ 'claude.desc': string;
32
+ 'codex.title': string;
33
+ 'codex.desc': string;
34
+ 'codex.disabled': string;
35
+ 'root.head': string;
36
+ 'root.intro': string;
37
+ 'root.use.title': string;
38
+ 'root.use.desc': string;
39
+ 'root.name.title': string;
40
+ 'root.cwd.title': string;
41
+ 'root.cwd.desc': string;
42
+ 'root.browse': string;
43
+ 'root.command.title': string;
44
+ 'diag.head': string;
45
+ 'diag.intro': string;
46
+ 'diag.screen.title': string;
47
+ 'diag.screen.desc': string;
48
+ 'diag.collect.title': string;
49
+ 'diag.collect.desc': string;
50
+ 'diag.made': string;
51
+ 'diag.btn': string;
52
+ 'dev.head': string;
53
+ 'dev.use.title': string;
54
+ 'dev.use.desc': string;
55
+ 'dev.now.title': string;
56
+ 'dev.now.desc': string;
57
+ 'dev.btn': string;
58
+ footer: string;
59
+ 'keys.head': string;
60
+ 'keys.intro': string;
61
+ 'keys.change': string;
62
+ 'keys.default': string;
63
+ 'keys.cancel': string;
64
+ 'keys.force': string;
65
+ 'keys.press': string;
66
+ 'keys.conflict': string;
67
+ 'keys.unbound': string;
68
+ 'keys.stock': string;
69
+ 'keys.digit': string;
70
+ 'keys.fixed': string;
71
+ 'keys.item.newtab': string;
72
+ 'keys.item.jump': string;
73
+ 'keys.item.focus': string;
74
+ 'keys.item.view': string;
75
+ 'keys.item.repair': string;
76
+ 'keys.item.splitright': string;
77
+ 'keys.item.splitbottom': string;
78
+ 'keys.item.closepane': string;
79
+ 'keys.item.newline': string;
80
+ 'keys.item.toggle': string;
81
+ 'keys.item.viewmode': string;
82
+ };
83
+ export default _default;
@@ -0,0 +1,83 @@
1
+ /** Čeština */
2
+ declare const _default: {
3
+ 'layout.title': string;
4
+ 'layout.desc': string;
5
+ 'reset.title': string;
6
+ 'reset.desc': string;
7
+ 'reset.now': string;
8
+ 'reset.default': string;
9
+ 'reset.btn': string;
10
+ 'dock.left': string;
11
+ 'dock.right': string;
12
+ 'dock.top': string;
13
+ 'dock.bottom': string;
14
+ 'size.width': string;
15
+ 'size.height': string;
16
+ 'size.auto': string;
17
+ 'norecover.title': string;
18
+ 'norecover.desc': string;
19
+ 'resume.title': string;
20
+ 'resume.desc': string;
21
+ 'agents.head': string;
22
+ 'agents.intro': string;
23
+ 'agents.unsupported.title': string;
24
+ 'agents.unsupported.desc': string;
25
+ 'state.on': string;
26
+ 'state.off': string;
27
+ 'state.half': string;
28
+ 'btn.install': string;
29
+ 'btn.remove': string;
30
+ 'claude.title': string;
31
+ 'claude.desc': string;
32
+ 'codex.title': string;
33
+ 'codex.desc': string;
34
+ 'codex.disabled': string;
35
+ 'root.head': string;
36
+ 'root.intro': string;
37
+ 'root.use.title': string;
38
+ 'root.use.desc': string;
39
+ 'root.name.title': string;
40
+ 'root.cwd.title': string;
41
+ 'root.cwd.desc': string;
42
+ 'root.browse': string;
43
+ 'root.command.title': string;
44
+ 'diag.head': string;
45
+ 'diag.intro': string;
46
+ 'diag.screen.title': string;
47
+ 'diag.screen.desc': string;
48
+ 'diag.collect.title': string;
49
+ 'diag.collect.desc': string;
50
+ 'diag.made': string;
51
+ 'diag.btn': string;
52
+ 'dev.head': string;
53
+ 'dev.use.title': string;
54
+ 'dev.use.desc': string;
55
+ 'dev.now.title': string;
56
+ 'dev.now.desc': string;
57
+ 'dev.btn': string;
58
+ footer: string;
59
+ 'keys.head': string;
60
+ 'keys.intro': string;
61
+ 'keys.change': string;
62
+ 'keys.default': string;
63
+ 'keys.cancel': string;
64
+ 'keys.force': string;
65
+ 'keys.press': string;
66
+ 'keys.conflict': string;
67
+ 'keys.unbound': string;
68
+ 'keys.stock': string;
69
+ 'keys.digit': string;
70
+ 'keys.fixed': string;
71
+ 'keys.item.newtab': string;
72
+ 'keys.item.jump': string;
73
+ 'keys.item.focus': string;
74
+ 'keys.item.view': string;
75
+ 'keys.item.repair': string;
76
+ 'keys.item.splitright': string;
77
+ 'keys.item.splitbottom': string;
78
+ 'keys.item.closepane': string;
79
+ 'keys.item.newline': string;
80
+ 'keys.item.toggle': string;
81
+ 'keys.item.viewmode': string;
82
+ };
83
+ export default _default;