feat(editor): F4.6a simulation mode and a computer's desktop

Java's mode buttons start and stop the room, now in a worker of the
editor's page (LocalSimHost), with Java's speed slider. While the
simulation runs the design is locked as in Java: no palette, no sheet,
nothing moves, no undo. Cables that carry a frame light up in Java's
green, and a tap on a computer opens its desktop: Java's title, the
software installation first, then the installed programs in the order
of Java's list for the language, a taskbar with the network icon and
the network as the simulation runs it. The room sends that over a new
topic, node:<id>. Desktops stay over a return to the design and show
again at the next start. The programs' windows follow from F4.6b.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 13:41:41 +00:00
co-authored by Claude Opus 5.5
parent 6e97e3565d
commit dfd5ec03ce
44 changed files with 1190 additions and 41 deletions
+24
View File
@@ -306,4 +306,28 @@ describe('a simulation room', () => {
expect(ofKind(got, 'error')).toHaveLength(1);
expect(ofKind(got, 'state').at(-1)).toMatchObject({ mode: 'simulation' });
});
it("sends a computer's network as it runs, anew when DHCP changes it, and nothing before a start", () => {
const { got, send, run, node } = setUp(exampleDoc('java-examples/einfaches_rechnernetz_komplett.fls'));
const notebook = node('Notebook 1');
const server = node('Server');
send({ kind: 'sub', topics: [`node:${notebook.id}`, `node:${server.id}`] });
expect(ofKind(got, 'node')).toEqual([]);
send({ kind: 'start' });
run(20_000);
const sent = ofKind(got, 'node').filter((each) => each.node === notebook.id);
expect(sent.length).toBeGreaterThanOrEqual(2);
expect(sent.at(-1)!.settings).toMatchObject({ subnetMask: '255.255.255.0', dnsServer: '192.168.0.100', macAddress: expect.stringMatching(/^([0-9A-F]{2}:){5}[0-9A-F]{2}$/) });
expect(sent.at(-1)!.settings.ipAddress).toMatch(/^192\.168\.0\.\d+$/);
// Sent when it changes, not every tick.
expect(new Set(sent.map((each) => JSON.stringify(each.settings))).size).toBe(sent.length);
expect(ofKind(got, 'node').filter((each) => each.node === server.id).map((each) => each.settings)).toEqual([
{ ipAddress: '192.168.0.100', subnetMask: '255.255.255.0', defaultGateway: '', dnsServer: '192.168.0.100', macAddress: expect.any(String) },
]);
// A client that subscribes again gets it again.
const before = got.length;
send({ kind: 'sub', topics: [`node:${server.id}`] });
expect(ofKind(got.slice(before), 'node')).toHaveLength(1);
});
});