From b92cd6c453da227f5e0518abc33d79283e239fdb Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 30 Aug 2026 19:26:35 +0300 Subject: [PATCH] docs(claude): carry the live-inspection recipes into the skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../references/inspecting-the-running-app.md | 17 +++++++++++++++ .../skills/measure-startup-requests/SKILL.md | 21 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/.claude/skills/building-client-ui/references/inspecting-the-running-app.md b/.claude/skills/building-client-ui/references/inspecting-the-running-app.md index a8b68b8732..ab2441fc3c 100644 --- a/.claude/skills/building-client-ui/references/inspecting-the-running-app.md +++ b/.claude/skills/building-client-ui/references/inspecting-the-running-app.md @@ -31,17 +31,28 @@ 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 `… attributes` button in `.status-bar`. +- **On NixOS, Playwright's downloaded browsers fail on libX11.** Launch the system one instead: + `chromium.launch({ executablePath: "/etc/profiles/per-user//bin/chromium" })` — the same + trick as the untracked `apps/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: +```bash +fuser -k 37999/tcp # Linux; or: ss -tlnp 'sport = :37999' to find the pid first +``` + ```powershell Get-NetTCPConnection -LocalPort 37999 -State Listen | % { Stop-Process -Id $_.OwningProcess -Force } ``` @@ -57,3 +68,9 @@ Get-NetTCPConnection -LocalPort 37999 -State Listen | % { Stop-Process -Id $_.Ow - **A mispositioned or self-dimming fixed-position menu** — walk the ancestors for `transform`, `filter` and `container-type` rather than reading the stylesheets; any of them creates a containing block and a stacking context (see "Dropdown menus and the backdrop blur" in `SKILL.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. diff --git a/.claude/skills/measure-startup-requests/SKILL.md b/.claude/skills/measure-startup-requests/SKILL.md index 326b14bc01..60cd79e925 100644 --- a/.claude/skills/measure-startup-requests/SKILL.md +++ b/.claude/skills/measure-startup-requests/SKILL.md @@ -37,6 +37,27 @@ node .claude/skills/measure-startup-requests/analyze-requests.mjs diff /@fs//…/file.ts`. +- Navigate a blank page to that origin, `setContent("")`, then take `performance.now()` + around `await import(url).catch(() => {})`. +- **The `.catch` is what makes this work.** An ES module graph is fully fetched and instantiated + before any of it is evaluated, so a module whose *evaluation* throws without a session ("Logged in + session not found") still yields a valid download-and-instantiate measurement. +- Constructing an editor in that page needs `{ licenseKey: "GPL" }`, or `create()` throws + `license-key-missing`. + +This is how the "first text note is slow" delay was attributed to the module graph rather than to +the editor (dev server, 2026-08): `packages/ckeditor5/src/index.ts` ~290 ms / 216 modules and +`EditableText.tsx` ~355 ms / 328 modules, against `PopupEditor.create()` at 83 ms first and ~25 ms +after. That is why the idle preload (`preloadCommonNoteTypes` in `note_types.tsx`) is the lever — +and why the 428 KB emoji `definitionsUrl` fetch is not a suspect: `EmojiRepository.init()` fires it +without returning the promise, so it never blocks `create()`. + ## Interpreting results - **Dev-mode numbers, not production.** The dev server serves unbundled ES modules (~500+ script