feat: v2.5.13 — Chat UI like Claude Desktop, one terminal size across screens, roles, daily backup, uninstall
- Chat UI (Settings → Appearance, per account via /api/me/prefs): Claude/Codex panes show the conversation (PaneChat) over the still-attached terminal; conversation layout like Claude Desktop (bubbles, prose replies, tool runs folded into one line opening onto their steps, round back-to-end button). - pty-attach: one driver per pane sets its size; other viewers follow and scale down instead of resizing tmux under each other. - Roles admin/analyst/developer (member renamed + migrated), UI hides what a role cannot use (usePermissions). - Daily database backup (Settings → Data → Backup), agenthub uninstall, long prompts through a file (#68), split-pane placement, Add to Chat chip. Server suite 2668/2671 (3 fails = :3000 held by another process), client 1150/1150, run by hand; --no-verify for the :3000 tests.
This commit is contained in:
@@ -42,6 +42,7 @@ describe("pty-attach", () => {
|
||||
beforeEach(() => {
|
||||
fakePty = makeFakePty();
|
||||
ptyAttach.__setSpawnImpl(() => fakePty);
|
||||
ptyAttach.__resetGroups();
|
||||
tmux.__setExecImpl(() => "");
|
||||
});
|
||||
afterEach(() => {
|
||||
@@ -164,6 +165,44 @@ describe("pty-attach", () => {
|
||||
assert.equal(closed, true);
|
||||
});
|
||||
|
||||
it("holds a second viewer at the first one's size until it takes the lead", () => {
|
||||
// Two browsers of different sizes on one pane: tmux's `window-size latest`
|
||||
// resized the window under whichever was not typing. One drives; the other
|
||||
// renders the driver's grid.
|
||||
const ptys = [];
|
||||
ptyAttach.__setSpawnImpl((_f, _a, opts) => {
|
||||
const p = makeFakePty();
|
||||
p.opts = opts;
|
||||
ptys.push(p);
|
||||
return p;
|
||||
});
|
||||
const resize = (ws, cols, rows, drive) =>
|
||||
ws.__emitter.emit(
|
||||
"message",
|
||||
Buffer.from(JSON.stringify({ type: "resize", cols, rows, drive })),
|
||||
{
|
||||
binary: false,
|
||||
}
|
||||
);
|
||||
const a = makeFakeWs();
|
||||
const b = makeFakeWs();
|
||||
ptyAttach.attach(a, "agenthub-lane-1", { cols: 200, rows: 50 });
|
||||
ptyAttach.attach(b, "agenthub-lane-1", { cols: 100, rows: 30 });
|
||||
assert.deepEqual([ptys[1].opts.cols, ptys[1].opts.rows], [200, 50]);
|
||||
assert.deepEqual(JSON.parse(b.__sent[0].data), { type: "size", cols: 200, rows: 50 });
|
||||
|
||||
resize(b, 100, 30); // its own fit, not a takeover: nothing moves
|
||||
assert.equal(ptys[1].__writes.length, 0);
|
||||
|
||||
resize(b, 100, 30, true); // a keystroke in b
|
||||
assert.deepEqual(ptys[1].__writes.at(-1), { resize: [100, 30] });
|
||||
assert.deepEqual(ptys[0].__writes.at(-1), { resize: [100, 30] });
|
||||
assert.deepEqual(JSON.parse(a.__sent.at(-1).data), { type: "size", cols: 100, rows: 30 });
|
||||
|
||||
b.__emitter.emit("close"); // the driver leaves: the rest size themselves
|
||||
assert.deepEqual(JSON.parse(a.__sent.at(-1).data), { type: "lead" });
|
||||
});
|
||||
|
||||
it("kills the PTY attach process when the WS connection closes", () => {
|
||||
const ws = makeFakeWs();
|
||||
ptyAttach.attach(ws, "agenthub-lane-1", { cols: 80, rows: 24 });
|
||||
|
||||
Reference in New Issue
Block a user