Skip to content
| Marketplace
Sign in
Visual Studio Code>Other>NerdulatorNew to Visual Studio Code? Get it now.
Nerdulator

Nerdulator

Nerdulator

|
1 install
| (0) | Free
Nerdulate anything in your editor. Select text, right click, and see everything deterministically derivable from it.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Nerdulator — VS Code extension

Select text in the editor, right click, choose Nerdulate Selection, and see everything deterministically derivable from it in the side bar.

Version 0.1.0. VS Code 1.85 or later. A port of the Nerdulator Chrome extension 0.1.0, and a sibling to it rather than a fork: the two render the same shape from the same API and should never disagree about what an input means.


Installing it

For development:

  1. Open this folder in VS Code
  2. Press F5 (the Run Nerdulator launch configuration)
  3. In the window that opens, select some text, right click, Nerdulate Selection

There is no build step and no runtime dependency. npm install is only needed for npm run typecheck and npm run package; the extension itself runs straight from source.

To install a build into your own VS Code:

npm install
npm run package
code --install-extension nerdulator-0.1.0.vsix

Using it

Right click a selection Nerdulate Selection
Ctrl+Alt+N / Cmd+Alt+N the same, on whatever is selected
Command palette Nerdulator: Nerdulate Something, which asks
The Nerdulator icon in the Activity Bar the panel, with an input box
‹ in the panel's title bar back, one step through the walk
↗ in the panel's title bar the current reading on nerdulator.com

The keybinding and the palette command work with no selection: they fall back to the word under the cursor, because "select it first" is an unhelpful answer to someone whose cursor is already sitting on the UUID they want. The context menu entry is gated on editorHasSelection, so that path always has a real selection. Multiple cursors are all inspected, joined by newlines, which is what a copy would have produced.


The one architectural rule

The extension contains no Nerdulator intelligence. It does not know what a UUID is, how to balance an equation or which of three readings of "1066" is best. It sends text to https://nerdulator.com/api/v1 and renders whatever comes back.

        website ─┐
  Chrome extension ┤
                 ├─→ deterministic core (22 engines)
   this extension ┘        ↑
                           └── one implementation, one set of answers

There is exactly one place that decides what an input means. An engine added to the site appears here with no change to this code. If you find yourself writing if (reading === 'uuid'), something has gone wrong.

src/api/nerdulator.js is the only file that makes a request. Nothing else knows the API exists.


What renders

The API returns one shape whatever the subject, so this renders that shape rather than the subject:

readings[]            one per interpretation, best first
  name, reading       "Number", "integer"
  match               exact | normalised | fallback
  summary             a sentence
  url                 the full page on nerdulator.com
  facts[]             label, value, value_type, note?, approximate?
  relations[]         relation?, label, target{ reading, canonical, value, url }

A fact whose value points at another object is itself clickable, and the remaining edges, neighbouring values and cross-readings, sit under a heading below. Clicking either inspects the target, which is how the graph is walked.

The API reports a linked fact twice, once in facts and once in relations, because they are the same edge seen from two sides. Rendering both put "Next major 1.0.0" in a row and again as a chip directly beneath it, which reads as a bug even though the data is correct. The fact's value becomes the link and the duplicate chip is dropped, so the thing you click is the thing you are reading.

Things done deliberately

match: "fallback" is shown, and labelled. The word engine returns a character count for almost any string, so "flibbertigibbet" does get an answer. That is a real result and is displayed, but saying "Not recognised. Inspected as plain text" is the honest framing, because the site did not recognise it, it counted the characters. It is a 200, not an error.

Several readings are presented as all correct. "1066" is genuinely an integer, a year and a colour. The panel says "3 readings. All of them are correct" rather than picking one, because picking is exactly what the product does not do.

engine is never shown. It is an internal grouping, "reference", "counting", "linear", and means nothing to a reader. The public reading id is shown instead, and that one is stable.

value_type only decides alignment. Numbers are right-aligned and prose is not. It is never used to reformat a value: the API already rendered it.

Fact labels are treated as prose. The API is explicit that label and note are written for people and get reworded. Nothing here keys off them.


What is different from the Chrome version, and why

The rendering is a port and reads almost line for line. Everything around it moved, because the two hosts are shaped differently in four ways that matter.

1. The host fetches, not the panel

In Chrome the side panel fetched for itself: an extension page is an ordinary origin, the API sends Access-Control-Allow-Origin: *, and the service worker had no reason to be involved.

Here the request is made by the extension host, in Node. That has three consequences, all of them improvements:

  • The webview needs no network capability at all. Its content security policy has no connect-src, so it could not make a request if something in it tried. Every byte it renders arrived by postMessage. That is the analogue of the Chrome version shipping with no host permission, and it is stronger, because there the panel really could fetch and simply chose not to need to.
  • There is no CORS involved, because Node has no origin.
  • The API client is callable from anywhere in the extension, not only from the surface that happens to be visible.

2. The host owns the state

A Chrome side panel persists while it is open, so it kept its own history and current. A VS Code webview view is destroyed as soon as it is hidden, collapsed, or dragged into another container.

So the host holds the walk and the webview is a pure renderer that can be thrown away and rebuilt from one message. retainContextWhenHidden is deliberately not set: it would keep a hidden DOM alive at a real memory cost to buy back state the host already has.

3. The panel is the editor's colour, not the site's

The Chrome panel defined a light and a dark palette of its own and chose with prefers-color-scheme, because a browser side panel has no theme to belong to. This one sits in the side bar next to the explorer, where brand teal against somebody's Solarized or high contrast theme looks like a foreign object. Every colour in media/main.css is a --vscode-* variable, so the panel is whatever theme the reader chose and high contrast works without a special case.

The teal survives in the marketplace icon. The Activity Bar icon is a flat monochrome SVG because VS Code tints it by using the shape as a mask: a colour there would be discarded and a gradient would come out as one solid block.

4. There is no header inside the panel

Back and Open in Nerdulator are view title actions, where VS Code puts view actions, driven by the nerdulator.canGoBack context key. The view's own title bar already says NERDULATOR, so a second header would repeat the name and take a line from a surface that is usually too narrow already.


ETag revalidation, which the browser did for free

Relations use GET /v1/objects/{reading}/{canonical} rather than posting to /inspect again, for the reasons the API was designed around:

  1. A POST is not cacheable by anything. Every step of a walk would reach the server.
  2. A revalidated request is refunded against the rate limit, so walking back through the graph costs nothing.
  3. The target already carries reading and canonical, the two path segments.

In Chrome, points 1 and 2 arrived with no code at all: the panel called fetch and the browser's HTTP cache answered a repeat visit. Node's fetch has no cache. Ported naively, the one property the endpoint exists for would have quietly stopped happening, and every step back through the graph would have re-run an inspection on a mobile-class CPU.

So src/api/nerdulator.js keeps the smallest thing that restores it: the ETag and the body of each GET, an If-None-Match header, and the stored body returned on a 304. Bounded to 200 entries, memory only, never written to disk because the keys are things somebody looked at, and cleared when the window closes.

It revalidates rather than holding a copy for a while, and that distinction is the point. Answers are deterministic within one behaviour_version, but a deploy changes that version. Revalidating leaves the server as the one that decides whether the old bytes are still the right bytes; a plain time-to-live would serve last week's answer with no way to know it was stale.

POST /inspect is never cached. A POST response is not addressable, so there is nothing to key on.


Two things that are easy to get wrong

The panel may not exist when a command fires. Revealing the view and the view announcing itself are separate events, and the command that caused the reveal returns between them. A request landing in that gap has nowhere to render. NerdulatorViewProvider holds it in pending and the webview's ready handshake flushes it. This is the same race the Chrome version solved with a session-storage write and a timestamp; the shape differs, the problem does not.

The reveal is executeCommand('nerdulator.panel.focus') rather than view.show(), because show() needs a resolved view and the first call happens when the container has never been opened. VS Code generates <viewId>.focus for every contributed view and it works in both cases.

Two quick clicks can come back in either order. Relation chips stay clickable while a request is in flight, and nothing guarantees the answers arrive in the order the questions went out. Without a guard the slower one wins and the panel shows something nobody asked for. Every request carries a generation number and a stale answer is dropped. Stepping back bumps it too, so an in-flight request cannot land on top of the page the reader just returned to.


Permissions and privacy

VS Code extensions are not permission-scoped the way a Chrome extension is, so the honest statement is what this one touches:

  • It reads the current selection, or the word under the cursor, and only when you invoke a command.
  • It makes one HTTPS request to nerdulator.com per inspection. The API takes no credentials and sets no cookies.
  • It declares untrustedWorkspaces: supported and virtualWorkspaces: true, because it never executes workspace code and never touches the file system.
  • No telemetry, no settings, no storage. The ETag cache is in memory and goes when the window closes.
  • The API's own usage logging records the shape of a request, never the input: the recognised reading ids and the input length. Requests from here carry User-Agent: nerdulator-vscode/0.1.0 and nothing else identifying.

What you inspect leaves your machine. That is the whole mechanism, and it is worth stating plainly in an editor, where a selection is more likely to be somebody's own source than a word on a web page.


Security

The selected text came from a file, and the values came back through an API. Neither is trusted.

  • Every node in the webview is built with createElement and filled with textContent. There is no innerHTML in this codebase, and adding one would turn a selected word into script.
  • The webview runs under default-src 'none' with a per-load nonce, and localResourceRoots limited to media/.
  • No connect-src, so the webview cannot reach the network.
  • HTTPS only, no remote code, no eval, no dynamic execution.

Files

package.json           commands, menus, the view container, keybinding
src/
  extension.js         activation; works out what text the reader meant
  api/nerdulator.js    the only file that talks to the API, plus the ETag cache
  panel/view.js        the webview view: state, messaging, the HTML shell
media/
  main.js              renders the shape; no per-reading branches, no fetching
  main.css             every colour is a --vscode-* variable
icons/
  activity.svg         monochrome, because VS Code masks it
  icon128.png          the marketplace icon
test/
  panel.test.js        the panel and the renderer, no editor involved
  stubs.js             just enough `vscode` to run view.js outside VS Code
  integration/         the same extension inside a real VS Code

No framework and no build step. It renders a few dozen deterministic facts and does not need one. npm run typecheck runs tsc over the JSDoc with strict and noUncheckedIndexedAccess, matching the site's own settings, without introducing a compile.


Testing

The Chrome extension has no tests. These exist because the port is not the rendering, which was a transcription, but the ordering rules around it, and those were wrong on the first attempt.

npm test                                    # the panel and the renderer
npm run test:vscode                         # inside a real VS Code
xvfb-run -a npm run test:vscode             # the same, headless
VSCODE_EXECUTABLE=/usr/share/code/code npm run test:vscode   # reuse an install

npm test runs against the live API, not a fixture. The extension's whole contract is "render whatever the API returns", so a fixture would only prove that it renders a copy of what the API said on the day somebody captured it. A test that goes over the wire fails when the envelope moves, which is the news worth having.

The two suites answer different questions, and neither can answer the other's. test/stubs.js fakes enough of the vscode module to run src/panel/view.js in plain Node, which makes the ordering rules cheap to test: a request arriving before the panel exists, two answers racing, back with nothing to go back to. But a stub will happily agree with a manifest VS Code would reject, so test/integration launches a real editor and checks that the view container really generated nerdulator.panel.focus, that opening the view activates the extension, and that the commands exist.

One thing neither proves on its own: whether VS Code loads media/main.js under that content security policy. NERDULATOR_SCREENSHOT=1 dumps the running window so it can be looked at, because if the nonce or the policy were wrong the script would be blocked, ready would never arrive, and the panel would sit on its hint text with the request still pending.

What they caught

A superseded answer landed on top of the page the reader had gone back to. The generation guard claimed its number inside execute(), which runs after the await on revealing the view. A request that had been revealed but not yet started therefore took a fresh generation after back() had already tried to invalidate it. The guard looked right and did nothing in the one case it was written for. It now claims the number in run(), synchronously, when the request is asked for. Lesson: a sequence number that is claimed after an await is not a sequence number.


Known limits

  • Selections over 10 KB are refused before a request is made; the API's own limit is the same.
  • Rate limits are 5 requests a second, 20 burst, 1,000 an hour per caller, shared with anything else calling the API from your address.
  • A request that has not answered in 10 seconds is abandoned. Node, unlike a browser, will wait indefinitely otherwise.
  • Behind a corporate proxy, the host's fetch follows VS Code's own proxy settings (http.proxy, http.proxySupport). If those are wrong, every inspection reports "Nerdulator could not be reached".
  • The panel is a side bar view, so it is narrow. Long fact values wrap; long relation targets are ellipsised with the full text on hover.

Not in 0.1.0

A hover provider, a CodeLens, diagnostics, "find the 7 nerdy things in this file", a status bar entry, history, settings, an API base override, accounts or keys. Every one of those is a decision about when to interrupt somebody's editing, and none of them is answerable before anyone has used the thing. The job is: select something, nerdulate it.

Publishing

MIT, and the publisher id nerdulator on both the VS Code Marketplace and Open VSX. The full sequence, including the two logins that have to be done by a person, is in PUBLISHING.md.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft