feat(editor): F4.6d the web browser and web server on a computer's desktop

The oracle BrowserWindowRun uses Java's browser and web server windows as a
person does; the room keeps a browser window per computer and sends a
program window's view, the start names the person's language. The editor
shows the page in a sandboxed frame, links and forms go to the room, the web
server has its button, virtual hosts and lines. Better than Java: relative
images and links count from the page's folder, a mark stays in the page, the
error page from Java's template, the virtual hosts' file with complete rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 16:09:05 +00:00
co-authored by Claude Opus 5.5
parent f0e7c73ec7
commit aa54e5400e
31 changed files with 2773 additions and 55 deletions
+28 -1
View File
@@ -2,7 +2,7 @@
// it refuses, each with its reason.
import { describe, expect, it } from 'vitest';
import { CLIENT_MESSAGE_KINDS, HOST_MESSAGE_KINDS, isTopic, parseClientMessage, parseHostMessage } from '../src/index.js';
import { BROWSER_START, browserTitle, CLIENT_MESSAGE_KINDS, HOST_MESSAGE_KINDS, htmlTitle, isTopic, parseClientMessage, parseHostMessage, type BrowserView } from '../src/index.js';
describe("a client's messages", () => {
it('reads each kind', () => {
@@ -16,6 +16,9 @@ describe("a client's messages", () => {
];
expect(messages.map((each) => each.kind)).toEqual([...CLIENT_MESSAGE_KINDS]);
for (const each of messages) expect(parseClientMessage(each)).toEqual({ message: each });
// A start in a language, and the language dropped where it is none.
for (const language of ['de', 'en']) expect(parseClientMessage({ v: 1, kind: 'start', language })).toEqual({ message: { v: 1, kind: 'start', language } });
expect(parseClientMessage({ v: 1, kind: 'stop', language: 'en' })).toEqual({ message: { v: 1, kind: 'stop' } });
});
it('names what is wrong', () => {
@@ -27,6 +30,8 @@ describe("a client's messages", () => {
expect(code({ v: 1, kind: 'sub', topics: 'activity' })).toBe('badMessage');
expect(code({ v: 1, kind: 'sub', topics: ['activity', 'trace:'] })).toBe('badTopic');
expect(code({ v: 1, kind: 'unsub', topics: ['app:node/paint'] })).toBe('badTopic');
expect(code({ v: 1, kind: 'start', language: 'fr' })).toBe('badMessage');
expect(code({ v: 1, kind: 'start', language: null })).toBe('badMessage');
expect(code({ v: 1, kind: 'speed', value: '50' })).toBe('badMessage');
expect(code({ v: 1, kind: 'speed', value: Number.NaN })).toBe('badMessage');
expect(code({ v: 1, kind: 'input', node: 'n', app: 'terminal', action: 'enter', args: {} })).toBe('badMessage');
@@ -47,3 +52,25 @@ describe("a host's messages", () => {
expect(parseHostMessage({ v: 0, kind: 'state' })).toEqual({ error: { code: 'badVersion', detail: 'version 0' } });
});
});
describe("the browser window's view", () => {
it("reads an HTML page's first title as it is written, none where it is empty", () => {
expect(htmlTitle('<html><head><TITLE lang="de">Links &amp; mehr</TITLE><title>Zwei</title></head></html>')).toBe('Links &amp; mehr');
expect(htmlTitle('<title>\nRouting-Tabelle\n </title >')).toBe('\nRouting-Tabelle\n ');
expect(htmlTitle('<title><b>Fett</b> und mehr</title>')).toBe('Fett und mehr');
expect(htmlTitle('<title> </title>')).toBeUndefined();
expect(htmlTitle('<titles>Nein</titles><p>Kein Titel</p>')).toBeUndefined();
});
it("titles the window with the program's name, then the address being loaded, then the page's title once it came", () => {
const pageTitle = (page: BrowserView['page']) => (page.kind === 'html' ? htmlTitle(page.html) : page.kind === 'unreachable' ? 'Fehler' : undefined);
expect(browserTitle(BROWSER_START, 'Webbrowser', pageTitle)).toBe('Webbrowser');
const shown: BrowserView = { address: 'http://a/', loading: 'http://a/', arrived: true, page: { kind: 'html', html: '<title>A</title>', images: {} } };
expect(browserTitle(shown, 'Webbrowser', pageTitle)).toBe('A');
// The next load: its address, while the page before still shows.
expect(browserTitle({ ...shown, loading: 'http://b/', arrived: false }, 'Webbrowser', pageTitle)).toBe('http://b/');
// A page without a title keeps the address.
expect(browserTitle({ ...shown, page: { kind: 'text', text: 'x' } }, 'Webbrowser', pageTitle)).toBe('http://a/');
expect(browserTitle({ ...shown, page: { kind: 'unreachable' } }, 'Webbrowser', pageTitle)).toBe('Fehler');
});
});