@zgeoff/atc 1.0.1 → 1.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zgeoff/atc",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Terminal control tower for Claude Code sessions",
5
5
  "homepage": "https://github.com/zgeoff/atc#readme",
6
6
  "bugs": "https://github.com/zgeoff/atc/issues",
@@ -84,12 +84,21 @@ export class ScreenModel {
84
84
  });
85
85
  }
86
86
 
87
- // The terminal parses asynchronously; the replay waits for every recorded
88
- // byte to land in the buffer before serializing.
87
+ // The terminal parses asynchronously, and bytes recorded while a flush is
88
+ // awaited re-arm it — so the replay drains until no newer write is
89
+ // pending. Serializing earlier would omit bytes already streamed live to
90
+ // clients, and the replay's leading clear would erase them from the
91
+ // client's screen for good.
89
92
  async renderReplay(): Promise<string> {
90
- await this.flushed;
93
+ let pending: Promise<void>;
91
94
 
92
- return RESET_INPUT_MODES + this.serializer.serialize() + this.renderInputModes();
95
+ do {
96
+ pending = this.flushed;
97
+
98
+ await pending;
99
+ } while (pending !== this.flushed);
100
+
101
+ return RESET_INPUT_MODES + this.renderVisibleScreen() + this.renderInputModes();
93
102
  }
94
103
 
95
104
  updateDims(cols: number, rows: number): void {
@@ -100,6 +109,21 @@ export class ScreenModel {
100
109
  this.term.dispose();
101
110
  }
102
111
 
112
+ // A session on the alternate screen serializes as the normal buffer, a
113
+ // buffer switch, then the alternate buffer. The switch clears nothing on a
114
+ // terminal already in alternate mode — and the client always is, since its
115
+ // chrome runs there — so the normal-buffer paint would linger under the
116
+ // alternate one, showing through every cell the serializer skips as blank.
117
+ // The replay therefore keeps only what the switch precedes: the content of
118
+ // the buffer the session is actually showing.
119
+ private renderVisibleScreen(): string {
120
+ const serialized = this.serializer.serialize();
121
+ const altSwitch = '\u001B[?1049h\u001B[H';
122
+ const switchAt = serialized.lastIndexOf(altSwitch);
123
+
124
+ return switchAt === -1 ? serialized : serialized.slice(switchAt + altSwitch.length);
125
+ }
126
+
103
127
  private renderInputModes(): string {
104
128
  let out = '';
105
129