mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-14 11:19:42 +02:00
Merge pull request #591: fix(sessions): restore a session's colour, pin and image watcher after a restart
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'aicodeman': patch
|
||||
---
|
||||
|
||||
A session's tab colour, pin and image watcher now survive a Codeman restart or crash. Recovery rebuilt each surviving pane with its name, auto-clear, auto-compact and auto-resume settings but not those three, so they reset to default and the next save overwrote the stored values.
|
||||
@@ -2,7 +2,7 @@ var __defProp = Object.defineProperty;
|
||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||||
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
|
||||
// node_modules/@mediapipe/tasks-vision/vision_bundle.mjs
|
||||
// ../wt-cli/node_modules/@mediapipe/tasks-vision/vision_bundle.mjs
|
||||
var t = "undefined" != typeof self ? self : {};
|
||||
function e() {
|
||||
throw Error("Invalid UTF8");
|
||||
|
||||
@@ -3717,6 +3717,17 @@ export class WebServer extends EventEmitter {
|
||||
if (savedState.flickerFilterEnabled !== undefined) {
|
||||
session.flickerFilterEnabled = savedState.flickerFilterEnabled;
|
||||
}
|
||||
// Tab colour, pin and the image watcher are user-set and persisted like the settings
|
||||
// above, but this path never read them back: after a restart every surviving pane came
|
||||
// back uncoloured and unpinned with its image watcher off, and the next persist
|
||||
// overwrote the saved values. `reapplyPersistedSessionState()` (the reboot-restore
|
||||
// path) already restores all three. The watcher flag has to be set before
|
||||
// `setupSessionListeners()` below, which reads it to decide whether to start one.
|
||||
if (savedState.color) session.setColor(savedState.color);
|
||||
if (savedState.pinned) session.restorePin(true, savedState.pinnedAt);
|
||||
if (savedState.imageWatcherEnabled !== undefined) {
|
||||
session.imageWatcherEnabled = savedState.imageWatcherEnabled;
|
||||
}
|
||||
// Respawn controller (not supported for external-CLI sessions)
|
||||
if (!isExternalCliMode(session.mode) && savedState.respawnEnabled && savedState.respawnConfig) {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @fileoverview The settings a user puts on a session survive a Codeman restart.
|
||||
*
|
||||
* `restoreMuxSessions()` (server.ts) rebuilds a Session around a surviving tmux pane from
|
||||
* `state.json`, and `reapplyPersistedSessionState()` does the same for a reboot restore. A
|
||||
* setting one of them forgets is silently reset on the next persist, which writes `toState()`
|
||||
* wholesale. Found live: after a restart a session came back with its name, auto-clear and
|
||||
* auto-compact but with its tab colour, pin and image watcher gone.
|
||||
*
|
||||
* `restoreMuxSessions()` cannot be reached under vitest (see test/session-model-recovery.test.ts),
|
||||
* so the wiring is pinned by a source check and the setters it relies on are driven for real.
|
||||
*/
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { Session } from '../src/session.js';
|
||||
|
||||
const SRC = fileURLToPath(new URL('../src', import.meta.url));
|
||||
const server = readFileSync(join(SRC, 'web', 'server.ts'), 'utf-8');
|
||||
|
||||
function functionBody(name: string): string {
|
||||
const start = server.indexOf(`async ${name}(`);
|
||||
expect(start, `${name} not found`).toBeGreaterThan(-1);
|
||||
const next = server.indexOf('\n /**', start);
|
||||
return server.slice(start, next === -1 ? undefined : next);
|
||||
}
|
||||
|
||||
describe('restoreMuxSessions() hands back every persisted user setting', () => {
|
||||
const body = functionBody('restoreMuxSessions');
|
||||
|
||||
it.each([
|
||||
['name', /name:\s*sessionName/],
|
||||
['name source', /nameSource:\s*savedState\?\.nameSource/],
|
||||
['colour', /session\.setColor\(savedState\.color\)/],
|
||||
['pin', /session\.restorePin\(true,\s*savedState\.pinnedAt\)/],
|
||||
['image watcher', /session\.imageWatcherEnabled\s*=\s*savedState\.imageWatcherEnabled/],
|
||||
['flicker filter', /session\.flickerFilterEnabled\s*=\s*savedState\.flickerFilterEnabled/],
|
||||
['auto-clear', /session\.setAutoClear\(/],
|
||||
['auto-compact', /session\.setAutoCompact\(/],
|
||||
['auto-resume', /session\.restoreAutoResume\(/],
|
||||
['nice priority', /session\.setNice\(/],
|
||||
])('%s', (_label, pattern) => {
|
||||
expect(body).toMatch(pattern);
|
||||
});
|
||||
|
||||
it('sets the image-watcher flag before the listeners read it', () => {
|
||||
expect(body.indexOf('session.imageWatcherEnabled = savedState.imageWatcherEnabled')).toBeLessThan(
|
||||
body.indexOf('await this.setupSessionListeners(session)')
|
||||
);
|
||||
});
|
||||
|
||||
it('restores everything the reboot-restore path restores', () => {
|
||||
const reapply = functionBody('reapplyPersistedSessionState');
|
||||
// Each persisted setting that path re-applies must also be re-applied here.
|
||||
for (const call of ['setColor(', 'restorePin(', 'imageWatcherEnabled =', 'flickerFilterEnabled =']) {
|
||||
expect(reapply, `reapply lacks ${call}`).toContain(call);
|
||||
expect(body, `restoreMuxSessions lacks ${call}`).toContain(call);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('the setters round-trip through toState()', () => {
|
||||
it('keeps colour, pin, image watcher and name source', () => {
|
||||
const original = new Session({ workingDir: '/tmp', mode: 'shell', name: 'custom name', nameSource: 'manual' });
|
||||
original.setColor('red');
|
||||
original.setPinned(true);
|
||||
original.imageWatcherEnabled = true;
|
||||
const state = original.toState();
|
||||
|
||||
const rebuilt = new Session({ workingDir: '/tmp', mode: 'shell', name: state.name, nameSource: state.nameSource });
|
||||
rebuilt.setColor(state.color!);
|
||||
rebuilt.restorePin(true, state.pinnedAt);
|
||||
rebuilt.imageWatcherEnabled = state.imageWatcherEnabled!;
|
||||
const after = rebuilt.toState();
|
||||
|
||||
expect(after).toMatchObject({
|
||||
name: 'custom name',
|
||||
nameSource: 'manual',
|
||||
color: 'red',
|
||||
pinned: true,
|
||||
pinnedAt: state.pinnedAt,
|
||||
imageWatcherEnabled: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user