paris-immersion 0.1.3 → 0.1.4

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/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import { serveCommand } from './commands/serve.js';
12
12
  const program = new Command()
13
13
  .name('paris')
14
14
  .description('Paris Immersion — CLI do aluno (login, workspace, tarefas)')
15
- .version('0.1.3');
15
+ .version('0.1.4');
16
16
  program
17
17
  .command('login')
18
18
  .description('Autenticar com sua conta Paris (device-code OR --token direto)')
@@ -68,11 +68,15 @@ export async function startRelayAgent(opts) {
68
68
  let closed = false;
69
69
  let reconnect;
70
70
  let killTimer;
71
+ // Tamanho atual do pty (spawnCoachPty começa 80x24). Usado pra detectar resize
72
+ // "no-op" (reconexão com mesma dimensão) e forçar o Claude a repintar.
73
+ let ptyCols = 80;
74
+ let ptyRows = 24;
71
75
  // Mantém o Claude vivo por uma janela após o viewer sair, pra que uma
72
76
  // reconexão (ex.: Safari derruba o ws periodicamente) REATACHE na MESMA sessão
73
77
  // em vez de reiniciar o curso. O resize que o viewer manda ao reconectar faz a
74
78
  // TUI do Claude repintar a tela atual.
75
- const PTY_GRACE_MS = 60_000;
79
+ const PTY_GRACE_MS = 600_000; // 10min — aluno que sai no meio e volta reatacha na MESMA sessão
76
80
  const killPty = () => {
77
81
  if (pty) {
78
82
  try {
@@ -106,6 +110,28 @@ export async function startRelayAgent(opts) {
106
110
  });
107
111
  pty.onExit?.(() => killPty());
108
112
  };
113
+ // Força o Claude a repintar a tela INTEIRA (não só os diffs). Necessário quando
114
+ // um viewer reconecta com um xterm em branco e manda o MESMO tamanho — sem
115
+ // mudança de tamanho não há SIGWINCH, então o Claude não redesenha e a tela
116
+ // fica vazia/quebrada. Um "nudge" (uma linha a menos e volta) gera o SIGWINCH.
117
+ const forceRepaint = () => {
118
+ if (!pty || ptyCols < 1 || ptyRows < 2)
119
+ return;
120
+ try {
121
+ pty.resize(ptyCols, ptyRows - 1);
122
+ }
123
+ catch {
124
+ /* noop */
125
+ }
126
+ setTimeout(() => {
127
+ try {
128
+ pty?.resize(ptyCols, ptyRows);
129
+ }
130
+ catch {
131
+ /* noop */
132
+ }
133
+ }, 80);
134
+ };
109
135
  const connect = () => {
110
136
  if (closed)
111
137
  return;
@@ -148,13 +174,20 @@ export async function startRelayAgent(opts) {
148
174
  }
149
175
  else if (msg.type === TYPE_RESIZE) {
150
176
  const [cols, rows] = msg.body.toString('utf8').split(',').map(Number);
151
- if (Number.isFinite(cols) && Number.isFinite(rows)) {
177
+ if (Number.isFinite(cols) && Number.isFinite(rows) && cols > 0 && rows > 0) {
178
+ const same = cols === ptyCols && rows === ptyRows;
179
+ ptyCols = cols;
180
+ ptyRows = rows;
152
181
  try {
153
182
  pty.resize(cols, rows);
154
183
  }
155
184
  catch {
156
185
  /* race de resize */
157
186
  }
187
+ // Mesma dimensão (reconexão) não dispara SIGWINCH → o Claude não
188
+ // repinta e a tela fica em branco/quebrada. Força o repaint.
189
+ if (same)
190
+ forceRepaint();
158
191
  }
159
192
  }
160
193
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paris-immersion",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Paris Immersion CLI — login, install workspace, status, task control. Para alunos das imersões da Paris Group.",
5
5
  "homepage": "https://parisgroup.ai",
6
6
  "repository": {