Files
mdg-filius/packages/editor/test/documentationPaint.test.ts
julian.kiedaischandClaude Opus 5.5 ae10ee6c31 feat(editor): the dark scheme everywhere — the surface, documentation, mode buttons, program windows
The surface turns dark with light labels and cables and a faint light outline
round Java's icons; documentation rectangles become a tint of their colour and
texts are drawn towards light; the mode buttons use Java's pictures freed from
their grey ground (tools/dark-pictures/make.py); native controls follow
color-scheme; the desktop's program windows paint through Java-look tokens
whose light values are Java's own. The exported image and the report keep
Java's colours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 22:27:03 +00:00

79 lines
5.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// How the editor paints a documentation item, against the pixels Java's
// JDocuElement paints (packages/doc/test/documentation.java.json, DocuRun):
// a rectangle in the documentation mode and in the others, its border, the
// text's border; the focused text's frame; where an edge is taken hold of —
// and the editor's own dark paint, which Java has not.
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { darkRectangleBorder, darkRectangleFill, darkTextColor, edgesAt, fontFamily, holdFrame, rectangleBorder, rectangleFill, TEXT_BORDER, FOCUS_BORDER, FOCUS_BAND, textColor } from '../src/canvas/documentationPaint.js';
type Rgba = [number, number, number, number];
const java = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '../../doc/test/documentation.java.json'), 'utf8')) as {
readonly painted: Record<string, Record<string, Rgba>>;
readonly dropped: { readonly text: { readonly bounds: number[] }; readonly textUnfocused: { readonly bounds: number[] } };
};
const pink = { red: 1, green: 201 / 255, blue: 201 / 255, alpha: 1 };
const css = ([red, green, blue, alpha]: Rgba) => (alpha === 255 ? `rgb(${red}, ${green}, ${blue})` : `rgba(${red}, ${green}, ${blue}, ${alpha / 255})`);
describe("Java's paint of the documentation", () => {
it('fills a rectangle at an opacity of 200 in the documentation mode, else opaque and lighter; its border four fifths of its colour', () => {
const { rectangleEnabled, rectangleDisabled, rectangleFocused, textEnabled, textDisabled } = java.painted;
expect(rectangleFill(pink, true)).toBe(css(rectangleEnabled!['50,25']!));
expect(rectangleFill(pink, false)).toBe(css(rectangleDisabled!['50,25']!));
expect(rectangleBorder(pink)).toBe(css(rectangleEnabled!['1,25']!));
expect(rectangleBorder(pink)).toBe(css(rectangleDisabled!['1,25']!));
// The focus: Java's blue border and translucent band (its alpha 77 of 255 is 0.3).
expect(FOCUS_BORDER).toBe(css(rectangleFocused!['1,25']!));
const [red, green, blue, alpha] = rectangleFocused!['3,25']!;
expect(FOCUS_BAND).toBe(`rgba(${red}, ${green}, ${blue}, ${Math.round((alpha / 255) * 10) / 10})`);
// A text's border only in the documentation mode.
expect(TEXT_BORDER).toBe(css(textEnabled!['0,25']!));
expect(textDisabled!['0,25']![3]).toBe(0);
// Java's integer arithmetic: 255 + (c − 255) · 8 / 10 cut towards zero, c · 4 / 5 cut.
expect(rectangleFill({ red: 0, green: 1, blue: 7 / 255, alpha: 1 }, false)).toBe('rgb(51, 255, 57)');
expect(rectangleBorder({ red: 0, green: 1, blue: 7 / 255, alpha: 1 })).toBe('rgb(0, 204, 5)');
// Where rounding would differ: (3 − 255) · 8 / 10 = −201.6 is −201 in Java; 3 · 4 / 5 = 2.4 is 2, 254 · 4 / 5 = 203.2 is 203.
expect(rectangleFill({ red: 3 / 255, green: 254 / 255, blue: 1, alpha: 1 }, false)).toBe('rgb(54, 255, 255)');
expect(rectangleBorder({ red: 3 / 255, green: 254 / 255, blue: 4 / 255, alpha: 1 })).toBe('rgb(2, 203, 3)');
expect(textColor({ red: 0, green: 0, blue: 1, alpha: 0.5 })).toBe('rgba(0, 0, 255, 0.5)');
});
it('paints a rectangle in the dark scheme as a tint of its colour, stronger while documenting, and draws a text towards light', () => {
const grey = { red: 0.8, green: 0.8, blue: 0.8, alpha: 1 };
expect(darkRectangleFill(grey, true)).toBe('rgba(204, 204, 204, 0.28)');
expect(darkRectangleFill(grey, false)).toBe('rgba(204, 204, 204, 0.2)');
expect(darkRectangleBorder({ red: 1, green: 0, blue: 0, alpha: 1 })).toBe('rgba(255, 0, 0, 0.7)');
expect(darkTextColor({ red: 0, green: 0, blue: 0, alpha: 1 })).toBe('color-mix(in oklab, rgba(0, 0, 0, 1) 15%, var(--canvas-label))');
});
it("grows a focused text's frame as Java does, and finds the edges a drag takes", () => {
const [x, y, width, height] = java.dropped.textUnfocused.bounds as [number, number, number, number];
const [fx, fy, fw, fh] = java.dropped.text.bounds as [number, number, number, number];
expect(holdFrame({ kind: 'text', x, y, width, height }, true)).toEqual({ x: fx, y: fy, width: fw, height: fh });
expect(holdFrame({ kind: 'text', x, y, width, height }, false)).toEqual({ kind: 'text', x, y, width, height });
expect(holdFrame({ kind: 'rectangle', x, y, width, height }, true)).toEqual({ kind: 'rectangle', x, y, width, height });
const frame = { x: 0, y: 0, width: 100, height: 50 };
expect(edgesAt(frame, { x: 50, y: 25 }, 7)).toEqual({ left: false, right: false, top: false, bottom: false });
expect(edgesAt(frame, { x: 7, y: 25 }, 7)).toEqual({ left: true, right: false, top: false, bottom: false });
expect(edgesAt(frame, { x: 93, y: 49 }, 7)).toEqual({ left: false, right: true, top: false, bottom: true });
expect(edgesAt(frame, { x: 92, y: 8 }, 7)).toEqual({ left: false, right: false, top: false, bottom: false });
expect(edgesAt(frame, { x: 3, y: 3 }, 7)).toEqual({ left: true, right: false, top: true, bottom: false });
// Narrower than two bands: both edges, of which the drag takes the left (resizedFrame).
expect(edgesAt({ x: 0, y: 0, width: 10, height: 10 }, { x: 5, y: 5 }, 7)).toEqual({ left: true, right: true, top: true, bottom: true });
});
it("writes Java's logical fonts as the browser's", () => {
expect(['Dialog', 'SansSerif', 'Serif', 'Monospaced', 'DialogInput', 'Comic "Neue"'].map(fontFamily)).toEqual([
'sans-serif',
'sans-serif',
'serif',
'monospace',
'monospace',
'"Comic Neue", sans-serif',
]);
});
});