feat(editor): F4.6e the e-mail program and e-mail server on a computer's desktop

The oracle MailWindowRun uses Java's e-mail windows as a person does and
answers their dialogs. The core sets and saves the program's account,
deletes a mail, checks a form as Java's window does, and gives the server
its domain, new and removed accounts and the window's own line; the room
sends both programs' views and takes their actions; the editor has both
windows. Better than Java: a name field for a new account, a user name the
server has is refused, deleting hits the folder shown, a new domain goes
into the accounts file at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 16:44:36 +00:00
co-authored by Claude Opus 5.5
parent aa54e5400e
commit 11f13bca84
33 changed files with 2841 additions and 52 deletions
+17 -7
View File
@@ -124,7 +124,7 @@ import { Terminal } from './apps/terminal.js';
import { firewallLogPage, forwardingTablePage, ripTablePage, routerIndexPage } from './apps/routerPages.js';
import { applyFirewallSettings, firewallSettingsPage } from './apps/firewallSettings.js';
import { parentPath, VirtualFileSystem } from '../model/virtualFileSystem.js';
import { EMAIL_SERVER_ACCOUNTS_FILE, serverAccountsFileText } from './emailAccountsFile.js';
import { clientAccountFileText, EMAIL_CLIENT_ACCOUNT_FILE, EMAIL_SERVER_ACCOUNTS_FILE, serverAccountsFileText } from './emailAccountsFile.js';
import { GNUTELLA_FOLDER, GnutellaPeer } from './apps/gnutella.js';
import {
PRODUCTION_TRACE_RETENTION,
@@ -1203,18 +1203,19 @@ export class NetworkRuntimeEngine {
}
/** An e-mail server's accounts as a run changed them, into the snapshot (and so into the project saved from it). */
private setEmailServerAccounts(nodeId: Id, accounts: readonly EmailServerAccount[]): void {
private setEmailServerAccounts(nodeId: Id, accounts: readonly EmailServerAccount[], domain: string): void {
const servers = new Map(this.stateValue.topologySnapshot.emailServersByNodeId);
const server = servers.get(nodeId);
if (server === undefined) return;
servers.set(nodeId, { ...server, accounts });
servers.set(nodeId, { ...server, accounts, domain });
this.stateValue.topologySnapshot = withSnapshot(this.stateValue.topologySnapshot, { emailServersByNodeId: servers });
this.writeEmailServerAccountsFile(nodeId, server.domain, accounts);
this.writeEmailServerAccountsFile(nodeId, domain, accounts);
}
/**
* The server's account file (EmailServer.kontenSpeichern), written from its
* accounts at its start and whenever they change. Java makes it empty at
* accounts at its start and whenever they or its domain change (Java writes
* a new domain only with the next change of its accounts). Java makes it empty at
* its start when it is missing (and reads it), and writes it when an
* account is made or removed, after a POP3 command, after an SMTP mail was
* taken and when it stops — but before the notice to a local sender, which
@@ -1305,7 +1306,7 @@ export class NetworkRuntimeEngine {
this,
nodeId,
emailServer,
(accounts) => this.setEmailServerAccounts(nodeId, accounts),
(accounts, domain) => this.setEmailServerAccounts(nodeId, accounts, domain),
() => this.networkInterfaces(nodeId)[0]?.ipAddress ?? 'null',
this.wallClock,
),
@@ -1324,7 +1325,16 @@ export class NetworkRuntimeEngine {
return;
}
case 'emailClient':
this.emailClientsByNodeId.set(nodeId, new EmailClient(this, nodeId, snapshot.emailClientsByNodeId.get(nodeId)!, (folders) => this.setEmailClientFolders(nodeId, folders)));
this.emailClientsByNodeId.set(
nodeId,
new EmailClient(
this,
nodeId,
snapshot.emailClientsByNodeId.get(nodeId)!,
(folders) => this.setEmailClientFolders(nodeId, folders),
(account) => this.nodeFiles(nodeId).writeTextFile(EMAIL_CLIENT_ACCOUNT_FILE, clientAccountFileText(account)),
),
);
return;
}
}