diff --git a/client/index.html b/client/index.html index cffa112e5..9affa6e08 100644 --- a/client/index.html +++ b/client/index.html @@ -7,20 +7,9 @@ content="width=device-width, initial-scale=1.0, maximum-scale=1" /> - - -
- - diff --git a/client/src/components/features/code-editor.tsx b/client/src/components/features/code-editor.tsx index 92dc1d20b..e7e66f200 100644 --- a/client/src/components/features/code-editor.tsx +++ b/client/src/components/features/code-editor.tsx @@ -335,7 +335,7 @@ export function CodeEditor({ }, fontSize: Math.round(initial.fs), lineHeight: Math.round(initial.lh), - fontFamily: "JetBrains Mono, Consolas, Monaco, monospace", + fontFamily: '"JetBrains Mono Variable", "JetBrains Mono", Consolas, Monaco, monospace', wordWrap: "on", scrollBeyondLastLine: false, automaticLayout: true, diff --git a/client/src/index.css b/client/src/index.css index 77866ab90..2fd2bb46b 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -1,4 +1,3 @@ -@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap"); @import "./styles/theme-tokens.css"; @import "tailwindcss" source(none); @source "../index.html"; @@ -214,9 +213,9 @@ html, body { --sidebar-accent-foreground: hsl(142 86% 28%); --sidebar-border: hsl(0 0% 15%); --sidebar-ring: hsl(142 86% 28%); - --font-sans: Inter, system-ui, sans-serif; + --font-sans: "Inter Variable", Inter, system-ui, sans-serif; --font-serif: Georgia, serif; - --font-mono: JetBrains Mono, Consolas, Monaco, monospace; + --font-mono: "JetBrains Mono Variable", "JetBrains Mono", Consolas, Monaco, monospace; --radius: 0.5rem; /* Unified font-size scale — all sizes now scale with --ui-font-scale */ --fs-sm: calc(14px * var(--ui-font-scale)); diff --git a/client/src/main.tsx b/client/src/main.tsx index 8998cc53e..2f98a6113 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -3,6 +3,9 @@ import "./lib/monaco-error-suppressor"; // Geändert von @/lib/... import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker"; import { createRoot } from "react-dom/client"; import App from "./App"; +// Self-hosted fonts: the page must not depend on third-party hosts to load. +import "@fontsource-variable/inter"; +import "@fontsource-variable/jetbrains-mono"; import "./index.css"; import { getCurrentFontScale, increaseFontScale, decreaseFontScale } from "./lib/font-scale-utils"; import { isMac } from "./lib/platform"; diff --git a/client/src/styles/theme-tokens.css b/client/src/styles/theme-tokens.css index f35cb20ad..cba853bb5 100644 --- a/client/src/styles/theme-tokens.css +++ b/client/src/styles/theme-tokens.css @@ -130,8 +130,8 @@ /* ═══════════════════════════════════════════════════════════ FONT FAMILIES (constant, no scaling) ═══════════════════════════════════════════════════════════ */ - --font-code: "JetBrains Mono", Consolas, Monaco, monospace; - --font-sans: var(--font-sans, Inter, system-ui, sans-serif); + --font-code: "JetBrains Mono Variable", "JetBrains Mono", Consolas, Monaco, monospace; + --font-sans: var(--font-sans, "Inter Variable", Inter, system-ui, sans-serif); } /* ═══════════════════════════════════════════════════════════ diff --git a/e2e/self-contained-page-load.spec.ts b/e2e/self-contained-page-load.spec.ts new file mode 100644 index 000000000..bee0ffcc5 --- /dev/null +++ b/e2e/self-contained-page-load.spec.ts @@ -0,0 +1,24 @@ +import { test, expect } from '@playwright/test'; + +// The page must load from the UnoSim origin alone. A blocking third-party +// stylesheet or script (formerly Google Fonts and a Replit banner) left the page +// blank until its host answered, which made E2E runs flaky and breaks filtered +// school networks. + +test('the start page loads without any third-party request', async ({ page, baseURL }) => { + const ownHost = new URL(baseURL ?? 'http://localhost:3000').host; + const thirdParty: string[] = []; + page.on('request', (request) => { + const url = new URL(request.url()); + // HTTP and WebSocket requests to the UnoSim host are first-party; blob:/data: never leave the page. + if (['http:', 'https:', 'ws:', 'wss:'].includes(url.protocol) && url.host !== ownHost) { + thirdParty.push(request.url()); + } + }); + + await page.goto('/'); + await expect(page.getByRole('button', { name: /start simulation/i })).toBeVisible({ timeout: 15000 }); + await page.evaluate(() => document.fonts.ready); + + expect(thirdParty).toEqual([]); +}); diff --git a/e2e/visual-full-context.spec.ts-snapshots/01-serial-hello-world-context-linux.png b/e2e/visual-full-context.spec.ts-snapshots/01-serial-hello-world-context-linux.png index ce30b4e0a..b42c1efd5 100644 Binary files a/e2e/visual-full-context.spec.ts-snapshots/01-serial-hello-world-context-linux.png and b/e2e/visual-full-context.spec.ts-snapshots/01-serial-hello-world-context-linux.png differ diff --git a/package-lock.json b/package-lock.json index 9ea7fdbef..2588a766b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@fontsource-variable/inter": "5.3.0", + "@fontsource-variable/jetbrains-mono": "5.3.0", "@radix-ui/react-alert-dialog": "^1.1.7", "@radix-ui/react-checkbox": "^1.1.5", "@radix-ui/react-dialog": "^1.1.7", @@ -1527,6 +1529,24 @@ "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", "license": "MIT" }, + "node_modules/@fontsource-variable/inter": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.3.0.tgz", + "integrity": "sha512-OupL48va4JNofb97w6NYeF9S7W/kHNKM0Er8Dem5nqi4jeOLrVJDoE8tZEpnMJmtkvNbB1EIPPwHcdkF6b1oUA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource-variable/jetbrains-mono": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/jetbrains-mono/-/jetbrains-mono-5.3.0.tgz", + "integrity": "sha512-F32xpS2NsGYoQi2ADSkKTgpJj7ozajsGgDJ8woTnqjmIB+dxDIqImjl4pXZVEExu8UFZ2ndhmX18EBS/hdz3Lw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", diff --git a/package.json b/package.json index dcad1715d..0bbeb93c1 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,8 @@ "prepare": "husky" }, "dependencies": { + "@fontsource-variable/inter": "5.3.0", + "@fontsource-variable/jetbrains-mono": "5.3.0", "@radix-ui/react-alert-dialog": "^1.1.7", "@radix-ui/react-checkbox": "^1.1.5", "@radix-ui/react-dialog": "^1.1.7",