Files
Classroom_Desktop/tools/lab/wonderink-zoom-check
ekklesiaandClaude Opus 5.5 48b3097c43 feat: a lesson in Wonder Ink is the size the browser shows it, and zooms
Teachers found lessons in Wonder Ink too small next to the ordinary
browser. Measured by asking each page for devicePixelRatio: Wonder Ink
follows the display scale exactly (dpr 1.5 at 150%), and Chrome is
scaled TWICE (dpr 2.25) — under Plasma it follows the compositor itself
and classroom-browser also forces UI_SCALE, a flag from the sway session.

The browser is left as it is. Wonder Ink's pages start at zoom =
UI_SCALE, the room's own number (1.0 at 100%), and measured afterwards
match the browser: dpr 2.25 and 853 px against 2.25 and 854. The window
is not zoomed; Qt scales it once, correctly.

Ctrl+Plus/=/keypad, Ctrl+Minus, Ctrl+0, Ctrl+wheel and the right-click
menu set one level for every tab, remembered as LEVEL= in its own file,
relative to the room. Two actions on one key are ambiguous to Qt and it
then does neither — Ctrl+Minus did nothing until each command became one
action with all its keys, which also fixes Ctrl+Shift+Tab.

The engine profile is built by one function, which the lab's checks now
use: a check with its own profile showed every worksheet blank.

The lab fences 10.10.0.0/16 as well: the development host reaches the
real Elmer over Tailscale, and the lab provisions a queue pointing at it.
Four lab checks written for older code are corrected; one had failed on
every run since 0.2.3.

tools/lab/wonderink-zoom-check: 18 checks. Lab: converged 120, print 39,
worksheets 24, all passing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-23 19:04:58 -05:00

159 lines
5.6 KiB
Bash
Executable File

#!/bin/bash
# How large is a lesson in Wonder Ink, and do the keys change it?
#
# Runs the INSTALLED classroom-wonderink under a real QtWebEngine
# (offscreen), builds its window the way main() does, and asks the page
# itself what it is rendering at. Keys are delivered through Qt's own key
# path to the focused page — the same shortcut map a keyboard reaches —
# because the lab's injected keyboard does not reach the nested session.
# A physical Ctrl+Plus is a hardware check.
#
# tools/lab/wonderink-zoom-check
set -uo pipefail
NAME="${LAB_NAME:-classroom-v2-lab}"
COMMAND="${WONDERINK:-/usr/local/bin/classroom-wonderink}"
docker exec -i \
-e QT_QPA_PLATFORM=offscreen \
-e QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox --disable-gpu" \
-e COMMAND="$COMMAND" \
"$NAME" bash -c 'export XDG_RUNTIME_DIR=$(mktemp -d) HOME=$(mktemp -d); python3 -' <<'PY'
import importlib.util, os, sys
from importlib.machinery import SourceFileLoader
loader = SourceFileLoader("wonderink", os.environ["COMMAND"])
spec = importlib.util.spec_from_loader("wonderink", loader)
ink = importlib.util.module_from_spec(spec)
loader.exec_module(ink)
from PyQt6.QtCore import QEventLoop, Qt, QTimer
from PyQt6.QtTest import QTest
from PyQt6.QtWebEngineCore import QWebEngineProfile
from PyQt6.QtWidgets import QApplication
app = QApplication(sys.argv[:1])
passed = failed = 0
def check(label, condition, detail=""):
global passed, failed
if condition:
print(f" \033[32mPASS\033[0m {label}")
passed += 1
else:
print(f" \033[31mFAIL\033[0m {label}" + (f" [{detail}]" if detail else ""))
failed += 1
def wait(ms):
loop = QEventLoop()
QTimer.singleShot(ms, loop.quit)
loop.exec()
def page_dpr(view):
answer = []
view.page().runJavaScript("window.devicePixelRatio", answer.append)
for _ in range(50):
if answer:
return answer[0]
wait(100)
return None
PAGE = "data:text/html,<title>lesson</title><p>Noah built an ark.</p>"
KEY = {
"Ctrl+=": (Qt.Key.Key_Equal, Qt.KeyboardModifier.ControlModifier),
"Ctrl+Plus": (Qt.Key.Key_Plus, Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier),
"keypad Ctrl+Plus": (Qt.Key.Key_Plus, Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.KeypadModifier),
"Ctrl+Minus": (Qt.Key.Key_Minus, Qt.KeyboardModifier.ControlModifier),
"keypad Ctrl+Minus": (Qt.Key.Key_Minus, Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.KeypadModifier),
"Ctrl+0": (Qt.Key.Key_0, Qt.KeyboardModifier.ControlModifier),
"Ctrl+Shift+Tab": (Qt.Key.Key_Backtab, Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier),
}
def press(window, name):
target = QApplication.focusWidget() or window.current()
QTest.keyClick(target, *KEY[name])
wait(400)
def open_window(room_zoom):
profile = QWebEngineProfile(f"zoomcheck{room_zoom}", app)
window = ink.WonderInk(profile, room_zoom=room_zoom)
window.resize(1280, 800)
window.show()
for _ in range(3):
window.open_tab(PAGE)
wait(2500)
window.current().setFocus()
wait(300)
return window
def factors(window):
return [round(window._tabs.widget(i).zoomFactor(), 3) for i in range(window._tabs.count())]
print("1. a room's normal size is the browser's")
for room in (1.0, 1.5, 2.0):
ink.SETTINGS.unlink(missing_ok=True)
window = open_window(room)
check(f"UI_SCALE {room}: every tab starts at {room}", factors(window) == [room] * 3, factors(window))
window.close()
wait(300)
print()
print("2. the keys a teacher knows, at UI_SCALE 1.5")
ink.SETTINGS.unlink(missing_ok=True)
window = open_window(1.5)
for name, level in (("Ctrl+=", 1.1), ("Ctrl+Plus", 1.25), ("keypad Ctrl+Plus", 1.5),
("Ctrl+Minus", 1.25), ("keypad Ctrl+Minus", 1.1), ("Ctrl+0", 1.0)):
press(window, name)
check(f"{name} makes it {round(level * 100)}% in every tab",
window.level() == level and factors(window) == [round(1.5 * level, 3)] * 3,
f"level={window.level()} factors={factors(window)}")
press(window, "Ctrl+Plus")
dpr = page_dpr(window.current())
check("and the page itself renders at it", dpr is not None and abs(dpr - 1.65) < 0.01, dpr)
print()
print("3. new tabs, and the next time Wonder Ink opens")
view = window.open_tab(PAGE)
wait(1500)
check("a new tab starts at the teacher's size", round(view.zoomFactor(), 3) == 1.65, view.zoomFactor())
check("the level is saved on its own", ink.SETTINGS.read_text().strip() == "LEVEL=1.1",
ink.SETTINGS.read_text())
check("in its own file, not the web profile",
"wonderink-shell" not in str(ink.SETTINGS) and ink.SETTINGS.name == "wonderink.conf")
index = window._tabs.currentIndex()
press(window, "Ctrl+Shift+Tab")
check("Ctrl+Shift+Tab still goes back a tab", window._tabs.currentIndex() == index - 1,
f"{index} -> {window._tabs.currentIndex()}")
window.close()
wait(300)
window = open_window(2.0)
check("reopened in a room at 2.0, the teacher's 110% is kept",
window.level() == 1.1 and factors(window) == [2.2] * 3, factors(window))
window.close()
ink.SETTINGS.write_text("LEVEL=banana\n")
check("a damaged setting is the normal size", ink.load_level() == 1.0)
ink.SETTINGS.write_text("LEVEL=40\n")
check("an absurd one too", ink.load_level() == 1.0)
window = open_window(3.0)
press(window, "keypad Ctrl+Plus"); press(window, "keypad Ctrl+Plus"); press(window, "keypad Ctrl+Plus")
check("a large room zoomed further never passes the engine's limit",
max(factors(window)) <= 5.0, factors(window))
window.close()
print()
print(f"{passed} passed, {failed} failed")
sys.exit(1 if failed else 0)
PY