From 962cc346e6af3530b2dca21b82bc8bfc7140b1c7 Mon Sep 17 00:00:00 2001 From: raiseCatError <315733358+raiseCatError@users.noreply.github.com> Date: Thu, 22 Oct 2026 16:20:48 +0530 Subject: [PATCH 1/4] Browser preview Console (#466) and /doctor document rendering (#462) C in the Chromium preview lists what the page reported for the loaded document: script exceptions, console messages, failed loads and HTTP errors, CSP refusals and NMSh's own refusals. Entries come from the Runtime and Log events every page already receives, only for a preview opened with a Console and only from the page's default worlds, and are redacted before they are kept (the preview token, query values, credentials, likely secrets, controls). The Console only reads: scripts and network rules are untouched, nothing is evaluated, copying shows the exact text first, and Enter goes to a line only for an unchanged HTML document. A Console page keeps its first events until a listener attaches, so the first load's errors are not lost. /doctor gains a Documents section: the browser previews would use, the Mermaid found (version, compatibility warning), the Mermaid setting and why diagrams would not be drawn, gathered without launching a browser or reading the bundle. --- CHANGELOG.md | 2 + docs/architecture/document-source-map.md | 1 + docs/design/browser-engine.md | 13 + docs/design/browser.md | 2 +- docs/design/diagrams.md | 4 +- docs/design/document-workspace.md | 14 +- docs/development/v024-ledger.md | 3 + docs/testing/v024-editor-physical-qa.md | 6 + src/app/TerminalApp.ts | 22 +- src/browser/console.ts | 353 +++++++++++++++++++++++ src/browser/documentPreview.ts | 17 +- src/browser/engine/chrome.ts | 14 + src/browser/engine/page.ts | 55 +++- src/browser/host.ts | 6 +- src/browser/session.ts | 19 +- src/browser/types.ts | 11 + src/diagrams/library.ts | 21 +- src/doctor/documents.ts | 81 ++++++ src/viewer/DocumentWorkspace.ts | 14 +- src/viewer/EditorPanel.ts | 32 +- src/viewer/consoleDialog.ts | 204 +++++++++++++ tests/browserConsole.test.ts | 159 ++++++++++ tests/browserConsoleChrome.test.ts | 130 +++++++++ tests/browserConsoleView.test.ts | 340 ++++++++++++++++++++++ tests/doctorDocuments.test.ts | 121 ++++++++ 25 files changed, 1626 insertions(+), 18 deletions(-) create mode 100644 src/browser/console.ts create mode 100644 src/doctor/documents.ts create mode 100644 src/viewer/consoleDialog.ts create mode 100644 tests/browserConsole.test.ts create mode 100644 tests/browserConsoleChrome.test.ts create mode 100644 tests/browserConsoleView.test.ts create mode 100644 tests/doctorDocuments.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 620ca7c9..d3e165a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Added +- **Console for the browser preview** ([#466](https://github.com/raiseCatError/notMyShell/issues/466); [design](docs/design/document-workspace.md#console-runtime-diagnostics-of-the-browser-preview)). In the Chromium preview of a Markdown or HTML document, **`C`** lists what the page itself reported for the document now loaded: script exceptions, `console` errors, warnings and messages, failed loads and HTTP errors, Content Security Policy refusals and requests NMSh refused. It works like Problems — newest first, Tab filters by severity, typing searches, Shift+↑↓ marks, Del clears, Ctrl+C shows exactly what would be copied before copying — and Enter goes to the line in the file when that is certain (an HTML document unchanged since the page loaded); otherwise it says why. The Console only reads: it never turns scripts or the network on (with scripts off, only loading and policy messages appear), accepts no expressions, and ignores `/browser` pages and the Mermaid renderer. Entries stay in memory for the current load, bounded, with the preview's address, query values, tokens and likely secrets redacted before they are kept. The status line counts errors and warnings. +- **`/doctor` reports document rendering** ([#462](https://github.com/raiseCatError/notMyShell/issues/462)). A Documents section names the Chrome or Chromium the previews would use (its version once a browser surface has checked it this session, and whether it is running), the Mermaid it found (path, version, a warning for a major version other than the tested 12), the Mermaid setting and, when diagrams would not be drawn, why. Nothing is launched and the Mermaid bundle is not read, so it says what was found, not that drawing works. - **Mermaid diagrams in Markdown** ([#462](https://github.com/raiseCatError/notMyShell/issues/462); [design](docs/design/diagrams.md)). A ```` ```mermaid ```` fence is drawn as a real diagram — flowcharts, sequence, class, state, ER, Gantt, pie, journey, git graph, mind map, timeline, quadrant, XY, requirement, Sankey, block and C4 diagrams — in Preview (NMSh's renderer and the Chromium preview), Split's preview pane, the viewer and Hybrid's inactive blocks, with the image backend your terminal supports (Kitty graphics, iTerm2, Sixel, or character art); `i` opens it in the image viewer to zoom and pan. NMSh uses the Mermaid you installed (`npm install -g mermaid`, or `NMSH_MERMAID`) inside the real browser it already drives, and downloads nothing. The page that draws runs no document script, loads nothing from the network or disk and returns only pixels; Mermaid's security settings are locked against directives. Settings → General → **Mermaid diagrams**: *On request* (default: press `D`, or F1 › Diagrams while editing — opening a document never starts a browser), *Automatic*, or *Off*. The fence stays the source: in Hybrid the block under the caret is its exact text, edits are drawn after a pause, and anything not drawn (not asked yet, no browser or Mermaid, a syntax error, too large, a timeout) shows the fence as written with `(diagram not drawn: reason)`. Drawings are cached by content and theme, bounded in number, bytes and time, and the renderer page closes when no document shows a diagram. - **Hybrid Markdown editing** ([#460](https://github.com/raiseCatError/notMyShell/issues/460)). **F3** in a Markdown document (or `H` from the read-only views) shows it *rendered* — headings, emphasis, links, lists, tables, quotes, code — except for the block your caret (or selection) is in, which shows its exact Markdown source and is fully editable. Move on and it renders again. It is another presentation of the same buffer: every key is the editor's (letters always type; snippets, list/table helpers, undo/redo, hunk review, recovery drafts and Save As all work as in the source editor), the caret lives in source positions, and switching views never changes the text. The smallest safe block is revealed — the whole list for a nested item, the whole table, the whole fence — and a selection shows every block it touches as source. The view is anchored to a source line, so revealing a block does not move what is above it. F3 again returns to the source editor; in the split view (F2) F3 switches the text pane between source and hybrid. Limits: Markdown files only (not HTML); images appear as `[image: alt]`; Up/Down move by source line; pointer input is not handled. - **Expandable `
`** ([#462](https://github.com/raiseCatError/notMyShell/issues/462)). The native Markdown/HTML viewer now shows `
……
` as a disclosure: `▸ Summary` when closed (hiding its content), `▾ Summary` with the content indented when open — in the rendered view, the preview, the split's preview pane and the hybrid view. GitHub-style details, with a blank line and Markdown inside and `
` in a later block, are understood (nested ones too); the `open` attribute sets the starting state. **`z`** toggles the summary at or below the top of the screen, **`Z`** all summaries in view; choices are kept by summary text, survive scrolling, resizing and edits above, and never change the file. Scripts, handlers and unknown tags in a details block are inert text. Not yet: pointer clicks (the viewer has no pointer support), and toggling inside the hybrid view (it shows each details as written: closed unless `open`). diff --git a/docs/architecture/document-source-map.md b/docs/architecture/document-source-map.md index 72738d2b..488a43d3 100644 --- a/docs/architecture/document-source-map.md +++ b/docs/architecture/document-source-map.md @@ -15,6 +15,7 @@ TextBuffer position (line, UTF-16 column) ⇄ Block.from / Block.to (source li ## What is built on it now - Heading index (`headingsOf`, `DocumentIndex.headings`): level, plain text, source line, depth (nearest shallower heading) and GitHub-style slug. +- The browser preview's Console (#466) goes to a line only for an HTML document whose text is unchanged since the page loaded (Chrome's 0-based line and UTF-16 column are the buffer's); a Markdown page is generated, so its lines are never offered as the file's. - `/view file.md#heading`, the viewer's `#` heading prompt with Backspace to go back, and the editor's *Go to heading…* / *Back* (jumps are `SourceAnchor`s). ## Heading anchors: GitHub-style, and how Jekyll/Kramdown differs diff --git a/docs/design/browser-engine.md b/docs/design/browser-engine.md index d898824a..0e074ec5 100644 --- a/docs/design/browser-engine.md +++ b/docs/design/browser-engine.md @@ -86,6 +86,19 @@ clears the selection. The world is recreated after a navigation. also read with `Target.getTargetInfo` after each load and every 500 ms while the page is open. Titles and addresses shown to the person have C0, C1 and bidi controls stripped. +### Console (#466) + +A preview opened with `console: {documentName}` (the document workspace's preview; never `/browser` tabs, never the diagram +renderer) turns DevTools events the page already receives — `Runtime` and `Log` are enabled on every page for other reasons, so no +domain is added — into `console` events: `Runtime.exceptionThrown`, `Runtime.consoleAPICalled` and `Log.entryAdded` (failed loads and +HTTP errors, CSP refusals, interventions), plus NMSh's own refusals of a request. `Runtime` events count only from the page's default +worlds (tracked from `executionContextCreated` with `isDefault`, cleared with the document), so NMSh's find world and any isolated +world never report. Each event becomes a redacted `ConsoleEntry` (`src/browser/console.ts`) before it leaves the page object: the +preview's address and token become the document's name or a path beside it, other addresses lose credentials, query values and +fragments, likely secrets are replaced, controls are stripped and text is bounded before any pattern runs. Nothing is evaluated, +no object is fetched (`description` only), scripts and network rules are untouched. A cross-document commit emits `document`; the +page holds its first 200 console events until a listener attaches, because the first load starts inside `open()`. + ### Dialogs, popups, downloads `alert` is accepted; `confirm`, `prompt` and `beforeunload` are dismissed. Each emits `dialog` with the message. A download is denied and diff --git a/docs/design/browser.md b/docs/design/browser.md index 7cfd0537..0f1721a6 100644 --- a/docs/design/browser.md +++ b/docs/design/browser.md @@ -39,4 +39,4 @@ no Puppeteer, no Electron, no WebSocket client, no bundled browser. ## Verified by -`tests/browserEngineChrome.test.ts` (a real Chrome 156 here: lazy launch and clean shutdown, idle stop and relaunch, URL policy, redirects, screencast, CSS animation advancing between frames, zoom, input into a form and a link click, wheel, find with scripts off, scripts toggle, popups/dialogs/downloads, remote refusal, cookie isolation, closing), `tests/browserPreviewServer.test.ts`, `tests/browserEngineProtocol.test.ts`, `tests/browserPanel.test.ts`, `tests/browserSurface.test.ts`, `tests/documentPreview.test.ts`. Chrome-dependent tests skip when no browser can start. +`tests/browserEngineChrome.test.ts` (a real Chrome 156 here: lazy launch and clean shutdown, idle stop and relaunch, URL policy, redirects, screencast, CSS animation advancing between frames, zoom, input into a form and a link click, wheel, find with scripts off, scripts toggle, popups/dialogs/downloads, remote refusal, cookie isolation, closing), `tests/browserPreviewServer.test.ts`, `tests/browserEngineProtocol.test.ts`, `tests/browserPanel.test.ts`, `tests/browserSurface.test.ts`, `tests/documentPreview.test.ts`; the document Console: `tests/browserConsole.test.ts`, `tests/browserConsoleView.test.ts`, `tests/browserConsoleChrome.test.ts`. Chrome-dependent tests skip when no browser can start. diff --git a/docs/design/diagrams.md b/docs/design/diagrams.md index f9625027..b42db9e2 100644 --- a/docs/design/diagrams.md +++ b/docs/design/diagrams.md @@ -108,6 +108,8 @@ NMSh: bounded PNG decoder (graphics service) ──► Kitty placeholders │ iT ## Not verified, and remaining #462 work - Physical Ghostty and Zed QA: **NOT RUN** (added to `docs/testing/v024-editor-physical-qa.md`). -- `/doctor` does not report Mermaid's location or version yet. +- `/doctor` reports (Documents section) the browser it would use, the Mermaid it found (path, version, a warning for a major other + than the tested 12), the setting and, when diagrams would not be drawn, the reason in the service's own order. It launches nothing and + does not read the bundle (`probeMermaid` opens and `fstat`s it), so it says what was found, never that drawing works. - Remaining #462 items outside diagrams: following `#fragment` links inside a rendered document, outline refinements (the optional persistent outline rail), pointer toggling of `
` (and hybrid toggling). diff --git a/docs/design/document-workspace.md b/docs/design/document-workspace.md index cd2c0011..32804117 100644 --- a/docs/design/document-workspace.md +++ b/docs/design/document-workspace.md @@ -70,9 +70,21 @@ Limits: code fences are recognised by a line scan (indented code blocks and fenc - **Currency:** checking is off until Problems is first opened. Then `ProblemsRunner` observes the buffer: each change bumps a revision and schedules a run (500 ms); a run snapshots the lines, aborts any run in flight, and its result is dropped if the revision moved. Opening Problems, Ctrl+R, a save and a Save As re-check (files the document names may have changed; no watchers). The runner is disposed with the editor, on reload and on exit, so no timer or observer outlives the document. - **Surface:** the dialog lists findings by severity then line with counts and the number of external links not checked; Tab cycles all / problems / errors, typing filters on the message, Del dismisses (until the next report), Esc leaves. In source and preview a jump also scrolls the read-only view to the line; hybrid and the split follow the caret. - **Independent review (opus subagent, security focus) and what changed:** (1–3) a dangling symlink, or a symlink to a folder this user cannot read, was told apart from other links outside the root (it leaked whether the outside target exists) and `readText` could be raced into following a link or blocking on a FIFO — the path is now walked component by component from the root, each link resolved (even dangling ones, via `readlink`) and confined before the walk continues, and the read opens with `O_NOFOLLOW|O_NONBLOCK`, `fstat`s a regular file and re-checks the real path; (4) heading checks in another file read it once per path and count against a bound; (5) quadratic regexes on hostile lines (80k characters took 17–38 s) — every quantifier is bounded, lines over 20,000 characters are scanned only that far and say so, and CSS is read by a linear scanner; (6) readers of reload, plain Save As-and-close now release the runner; (7) a failing checker no longer throws into the editor; (8–10) false positives and negatives: HTML comments and code spans across lines, TOML front matter, definitions on the next line or in quotes, escaped destinations, `url()` in CSS comments and strings, `url(#id)` checked against ids in HTML, ``, ids inside `