thinkpool-pair 0.3.1 → 0.3.5
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/bridge.mjs +35 -7
- package/package.json +1 -1
package/bridge.mjs
CHANGED
|
@@ -135,7 +135,9 @@ const announce = () =>
|
|
|
135
135
|
channel.send({ type: 'broadcast', event: 'bridge', payload: {
|
|
136
136
|
v: 2, name, repo: repoLabel, branch,
|
|
137
137
|
agents: installedAgents,
|
|
138
|
-
|
|
138
|
+
// cols/rows: the PTY's one true size — web viewers render this grid and
|
|
139
|
+
// scale it to their own page instead of voting to reflow it.
|
|
140
|
+
terms: [...terms.entries()].map(([id, t]) => ({ id, cmd: t.cmd, alive: true, cols: t.term.cols, rows: t.term.rows })),
|
|
139
141
|
} })
|
|
140
142
|
|
|
141
143
|
// One flush timer batches every terminal's pending bytes (~35ms cadence).
|
|
@@ -149,12 +151,32 @@ const flushAll = () => {
|
|
|
149
151
|
}
|
|
150
152
|
const flushTimer = setInterval(flushAll, 35)
|
|
151
153
|
|
|
154
|
+
// ── shared-PTY geometry ───────────────────────────────────────────
|
|
155
|
+
// The room's view is the product. DEFAULT: the shared PTY is PINNED to a
|
|
156
|
+
// standard 120×32 — one source resolution chosen for web legibility
|
|
157
|
+
// (scale ≈1 on a laptop pane), regardless of the host's window. A huge
|
|
158
|
+
// host window made viewers' text microscopic; a tiny one broke their
|
|
159
|
+
// layout — both ends of the same bug (2026-06-10). The host's local
|
|
160
|
+
// terminal just doesn't use its extra space; below 120×32 it wraps
|
|
161
|
+
// oddly locally, the room stays clean.
|
|
162
|
+
// TP_COLS/TP_ROWS pin a different size. TP_FOLLOW=1 restores
|
|
163
|
+
// follow-the-host-window (floored), for solo screen-mirror use.
|
|
164
|
+
const FLOOR_COLS = 100, FLOOR_ROWS = 28
|
|
165
|
+
const FOLLOW = process.env.TP_FOLLOW === '1'
|
|
166
|
+
const PIN_COLS = parseInt(process.env.TP_COLS, 10) || (FOLLOW ? null : 120)
|
|
167
|
+
const PIN_ROWS = parseInt(process.env.TP_ROWS, 10) || (FOLLOW ? null : 32)
|
|
168
|
+
const attachedDims = () => ({
|
|
169
|
+
cols: PIN_COLS || Math.max(process.stdout.columns || FLOOR_COLS, FLOOR_COLS),
|
|
170
|
+
rows: PIN_ROWS || Math.max(process.stdout.rows || FLOOR_ROWS, FLOOR_ROWS),
|
|
171
|
+
})
|
|
172
|
+
|
|
152
173
|
function openTerm({ id, cmd, args = [], attached = false, cols, rows }) {
|
|
153
174
|
if (terms.has(id)) return
|
|
175
|
+
const ad = attached ? attachedDims() : null
|
|
154
176
|
const term = pty.spawn(cmd, args, {
|
|
155
177
|
name: 'xterm-256color',
|
|
156
|
-
cols: cols || (attached ?
|
|
157
|
-
rows: rows || (attached ?
|
|
178
|
+
cols: cols || (attached ? ad.cols : (PIN_COLS || 120)),
|
|
179
|
+
rows: rows || (attached ? ad.rows : (PIN_ROWS || 32)),
|
|
158
180
|
cwd: process.cwd(), env: process.env,
|
|
159
181
|
})
|
|
160
182
|
const entry = { term, cmd, attached, scrollback: '', buf: '' }
|
|
@@ -192,10 +214,14 @@ process.stdin.on('data', d => {
|
|
|
192
214
|
if (t) t.term.write(d.toString('utf8'))
|
|
193
215
|
else if (d.includes(3)) shutdown() // Ctrl-C once detached/headless
|
|
194
216
|
})
|
|
195
|
-
// attached terminal follows the host's TTY size
|
|
217
|
+
// attached terminal follows the host's TTY size — but never below the
|
|
218
|
+
// floor, and never at all when pinned. Re-announce so viewers rescale.
|
|
196
219
|
process.stdout.on('resize', () => {
|
|
197
220
|
const t = attachedId && terms.get(attachedId)
|
|
198
|
-
if (t)
|
|
221
|
+
if (!t) return
|
|
222
|
+
const { cols, rows } = attachedDims()
|
|
223
|
+
if (t.term.cols === cols && t.term.rows === rows) return
|
|
224
|
+
try { t.term.resize(cols, rows); announce() } catch { /* noop */ }
|
|
199
225
|
})
|
|
200
226
|
|
|
201
227
|
channel
|
|
@@ -205,10 +231,12 @@ channel
|
|
|
205
231
|
if (t) t.term.write(payload.data)
|
|
206
232
|
})
|
|
207
233
|
.on('broadcast', { event: 'resize' }, ({ payload }) => {
|
|
208
|
-
// headless terms are web-sized (last writer wins); the attached term is
|
|
234
|
+
// headless terms are web-sized (last writer wins); the attached term is
|
|
235
|
+
// host-sized. Floor at 80 cols — one narrow phone viewer must not reflow
|
|
236
|
+
// the shared PTY into a 40-col column for everyone (2026-06-10 bug).
|
|
209
237
|
if (!payload?.cols || !payload?.rows) return
|
|
210
238
|
const t = terms.get(payload.term)
|
|
211
|
-
if (t && !t.attached) { try { t.term.resize(payload.cols, payload.rows) } catch { /* noop */ } }
|
|
239
|
+
if (t && !t.attached) { try { t.term.resize(Math.max(payload.cols, 80), Math.max(payload.rows, 20)); announce() } catch { /* noop */ } }
|
|
212
240
|
})
|
|
213
241
|
.on('broadcast', { event: 'term-open' }, ({ payload }) => {
|
|
214
242
|
if (!payload?.id || !payload?.cmd) return
|
package/package.json
CHANGED