Both were only in the auto-memory store, where they are recalled by relevance rather than read when the matching skill loads. measure-startup-requests gains the counterpart to its login-based capture: timing one module graph by importing it straight off the dev server, which needs no session. The `.catch` on that import is the whole trick — an ES graph is fetched and instantiated before any of it is evaluated, so a module that throws for want of a session still gives a valid number. The CKEditor figures are kept as the worked example, since they are what identified the module graph rather than `editor.create()` as the "first text note is slow" cost. inspecting-the-running-app gains the `window.glob.appContext` summoning tip, the NixOS system-chromium executablePath (its downloaded browsers fail on libX11), a Linux port kill next to the PowerShell one, and the pitfall that once produced a wrong diagnosis: an async re-render between the evaluate() that reads computed styles and the screenshot() can wipe the state being studied, which reads as the CSS never having painted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3.9 KiB
Inspecting the running app
Some UI questions cannot be answered from the stylesheets — which rule actually won, why a menu
landed where it did, whether a blur is real or a flat tint. Reasoning about the cascade from the
sources is unreliable enough to have produced wrong diagnoses more than once, because the load
order in a hand-built test page does not match the app's. Measure getComputedStyle in a real
instance instead.
Boot a login-free instance on the e2e fixture
Run the server against the e2e fixture document, entirely in memory, so there is no production build, no password, and no risk to the user's own data:
cd apps/server && NODE_ENV=development TRILIUM_ENV=dev TRILIUM_PORT=37999 \
TRILIUM_DATA_DIR=spec/db \
TRILIUM_DOCUMENT_PATH=../../packages/trilium-core/src/test/fixtures/document.db \
TRILIUM_INTEGRATION_TEST=memory TRILIUM_RESOURCE_DIR=src npx tsx ./src/main.ts
TRILIUM_INTEGRATION_TEST=memory keeps every write in RAM, so spec/db stays clean. There is no
login screen. Append /?mobile to the URL to force the mobile layout.
Do not assume the user's own instance is usable instead. Ports 8080 and 37840 are typically a share-only server and the installed Trilium, not the repo build — and two processes can share one data directory, which makes cross-process writes look like cache corruption.
Driving it
Use Playwright imported by absolute path
(file:///…/node_modules/playwright/index.mjs) — a script written into the scratchpad cannot
resolve playwright by name.
window.glob is exposed, so most UI can be summoned without clicking through to it:
glob.appContext.triggerCommand("showOptions"), "openInTreePopup" (with
{ noteIdOrPath, hoistedNoteId }), "showDeleteNotesDialog", …, and glob.froca for note lookups.
Fixture gotchas:
- It opens on a protected note, so click another note first.
- It runs the new layout, so there is no ribbon — the attributes editor opens from the
… attributesbutton in.status-bar. - On NixOS, Playwright's downloaded browsers fail on libX11. Launch the system one instead:
chromium.launch({ executablePath: "/etc/profiles/per-user/<user>/bin/chromium" })— the same trick as the untrackedapps/server/playwright.config.nixos.ts.
Stopping it
Killing the backgrounded npx tsx wrapper leaves the node child alive and still holding the port,
so the next boot fails with "Port 37999 is already in use". Kill the listener:
fuser -k 37999/tcp # Linux; or: ss -tlnp 'sport = :37999' to find the pid first
Get-NetTCPConnection -LocalPort 37999 -State Listen | % { Stop-Process -Id $_.OwningProcess -Force }
What to measure once it is up
- A rule that seems not to apply — dump
getComputedStyleon the element, then scan the stylesheets withel.matches(rule.selectorText)to find what actually set the property. If no rule matches, suspect a global selector from the CKEditor theme CSS, which Vite injects app-wide the first time a text note renders (see the ckeditor5-plugin-development skill,references/conventions.md). That is the usual cause of "this UI breaks only after opening a note". - A mispositioned or self-dimming fixed-position menu — walk the ancestors for
transform,filterandcontainer-typerather than reading the stylesheets; any of them creates a containing block and a stacking context (see "Dropdown menus and the backdrop blur" inSKILL.md).
Transient state can vanish between reading it and screenshotting it. An async re-render between
the evaluate() that dumps computed styles and the later screenshot() can wipe the state you are
studying (fancytree-active, a hover class, an open menu), which reads as "the CSS never painted"
and has produced a wrong diagnosis before. Re-assert the state at screenshot time, and pixel-sample
with pngjs (available through the e2e require) rather than eyeballing the image.