react-x11 AGENTS.md

Repository guidance for coding agents and contributors working on react-x11, a React renderer that draws interfaces in X11 desktop windows using JavaScript.

In plain words
What is it for?
Use it when modifying the renderer, its X11 window handling, layout, events, or development files.
Why use it?
It explains the project’s structure and rendering rules so changes fit its architecture and avoid incorrect assumptions about how windows and layout work.

Instructions file for CodexOpenCode

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add instructions/sidorares/react-x11/agents-md
Clone the repo
git clone --depth 1 https://github.com/sidorares/react-x11

Made for: Codex, OpenCode.

Per session 20,579 This file is loaded in full into every session.
When invoked 20,579 The same file — it is already loaded in full.
Security scan A 1 finding. Scan, not verified.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5 $0.20579 $0.20579
Opus 5 $0.10290 $0.10290
Sonnet 5 $0.04116 $0.04116
Haiku 4.5 $0.02058 $0.02058

Measured 2d ago against content hash a5742416d1fc, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, from the pricing page.

Security

Grade A, and why

react-x11 AGENTS.md scanned grade A with 1 finding against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 2d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Runs shell commandslowCapability

Expected in a hook, worth knowing in a rule or an instructions file.

would drag in `ws` and `node:child_process`.
AGENTS.md · 1,319 lines

How it starts

The opening of the file, as written. The whole thing — 1,319 lines — stays where its author put it; the contents beside it link to each section on GitHub.

AGENTS.md

Guidance for AI agents (and new contributors) working on react-x11.

What this project is

A custom React renderer whose host environment is an X11 server, with react-like ergonomics on top of ntk / node-x11 — pure JavaScript implementations of the X11 protocol, no native bridge.

Architecture (see NEXT_STEPS.md for the full rationale): only <window>, <popup>, <glarea> and <foreign> map to real X11 windows (<glarea> because GLX needs its own visual, <foreign> because the window is another process's); everything else is a retained lightweight node — one yoga-layout node each — painted into the owning window's double-buffered 2d context on ntk's frame clock, with synthetic capture/bubble events dispatched via front-to-back hit testing. X11 windows are created top-down in the commit phase (WindowNode.realize()): createInstance performs no X11 calls (the render phase is discardable under concurrent React), and every CreateWindow names its actual parent from the start — no ReparentWindow, no override-redirect staging (issue #4).

Layout

  • src/index.js — public entry (createRoot; the legacy render/unmountComponentAtNode pair was retired in #114).
  • src/Reconciler.js — react-reconciler host config + render entry points. Written against react-reconciler 0.33 (React 19). If you upgrade react-reconciler, expect host config contract changes; the smoke test is the safety net.
  • src/nodes.js — the retained node tree: WindowNode (real X window, paint/event/flex root), BoxNode, TextNode (+ spans/chunks), ImageNode, CanvasNode. Layout (yoga), painting, hit testing.
  • src/glnodes.js<glarea>: the GL surface. A child X window on a GLX visual (ntk's chooseGLXConfig), positioned by the parent's yoga rect, drawing onDraw frames on its own frame clock. First step of docs/glx.md.
  • src/foreignnodes.js<foreign>: another process's window, embedded. A second drawn: false node, over ntk's XEmbedSocket (docs/embedding.md). Two things here are not obvious and are commented at length. Teardown is synchronousWindowNode destroys its own X window in the same turn, and DestroyWindow takes every inferior with it, so a release that waits for a round trip releases a window that is already gone; the client is reparented to the root and dropped from the save set before the container goes, and never destroyed. And no focus proxy: the classic XEmbed embedder gives the X focus to an InputOnly window that forwards keys, which here would read as the toplevel losing focus and would put every key past the React tree before any handler saw it. The X focus stays on our window and forwarding happens in defaultKeyDown/defaultKeyUp, which is what makes the rule "app chords first, everything else forwards" mechanical.
  • src/frame/<Frame>: a pane of the application in its own process, its window embedded over <foreign> (docs/frame.md). Four small files along the seams: protocol.js (pure — the six messages, the callback table with its one-update grace window, argument sanitizing), env.js (the context bridge: createFrameContext, the FrameEnv accumulator the theme publishes into), index.js (the host: fork, supervision, coalesced updates, fallback), childmain.js + child.js (the pane bootstrap behind an injectable transport, which is what lets the tests run a real pane in-process over a loopback — test/frame.test.js forks for real exactly once, over a TCP bridge onto the in-process server). Two things are deliberate and easy to undo: the update listener goes up before the pane module import starts (a store the tree subscribes to later — a listener scoped to the mounted tree would drop every update that landed during the import), and the host's transport listeners outlive the effect cleanup until the exit, because a useFrameClose handler flushing through a callback prop sends its invoke after the unmount message.
  • src/scene3d.js — the 3D scene tree inside a <glarea>: <mesh>, <group>, geometry/material nodes and the renderer that compiles each geometry into a server-side display list (a frame is matrices + material state + one CallList per mesh). src/geometry3d.js generates the primitives, src/mat4.js is the matrix math, src/raycast3d.js + src/pointer3d.js are picking and mesh pointer events.
  • src/svgnodes.js<svg> over ntk's SvgView: SvgNode (sized from its viewBox like <image>, cached as coverage when the drawing is one colour) and SvgChildNode, the declarative SVG elements underneath it, serialized into the DOM SvgView consumes. It used to hold <markdown>, <html> and <tex> beside it, over ntk's document widgets; those were removed in 2.0 — a document rendered as one opaque widget can neither be selected across blocks nor re-rendered a block at a time, and the successors are <Markdown>/<Formula> in @react-x11/components, composed from public host elements.
  • src/yoga.js — the layout engine. Enums synchronously, WebAssembly behind loadLayout(), which createRoot() awaits before anything builds a node. Never import yoga-layout's default entry — it is a top-level await, and one import costs every app the single-executable build (docs/packaging.md); test/yoga.test.js enforces this.
  • src/anchor.js — where a <popup> goes: anchorRect and the screen area it flips and clamps against. Core rather than widget code because both callers need it and only one is a widget — a <popup anchor> with an 'auto' size learns how big it is inside realize(), after the content is measured and before CreateWindow, which is past the last moment React could have handed it a position (issue #255). So the window places itself from the same functions the widgets call, and the two agree by construction. at anchors to a rect inside a node — a caret — and is node-relative so that everything which moves the node keeps it true.
  • src/components/ — the widget set, plain React over the host primitives (no reconciler support needed). One module per widget, with the shared plumbing in theme.js (palette, useTheme, useControl), anchor.js (the React half of the above: useAnchor, useAnchorTracking, label measurement), typeahead.js and keys.js. index.js re-exports the public names.
  • src/menuitem.js, src/dbusmenu.js, src/globalmenu.js — the global menu (#112). menuitem.js is the item vocabulary, which is com.canonical.dbusmenu's rather than one of our own so that the array MenuBar draws is the array that serialises — one authoring model, no translation layer. dbusmenu.js is pure: stable ids across re-renders, and the diff that decides between ItemsPropertiesUpdated (revision unchanged) and LayoutUpdated, which is a performance decision rather than a cosmetic one. globalmenu.js is the wire: registrar detection, the export, the KDE window properties, and useGlobalMenu. Two traps live there and are commented at length — detection means a live owner (the opposite of hasService()'s rule, because a registrar is a directory rather than a feature), and every call to it carries NO_AUTO_START, without which tidying up after a dead panel launches a new registrar nobody reads. scripts/globalmenu-host.mjs is a panel in a terminal; there is no installable dbusmenu client to test against otherwise.
  • src/application.js, src/activate.js, src/apphooks.js — custom URI schemes and single instances (#173). application.js is the bus half and imports neither react nor X11, so the seam it would be extracted along stays visible; what keeps it here is that RequestName has to land on the same connection as the menu and the portals, or the desktop sees two half-applications. Two traps live there: the object path is derived from the app id with dashes becoming underscores (get it wrong and the launch reaches a path nobody serves, which looks like nothing happening), and the interface is exported before RequestName, because the daemon delivers the queued activating call the instant the name is owned. activate.js is the raise, and it is the part users judge the feature by — _NET_ACTIVE_WINDOW with a wrong timestamp is refused by the WM while every layer reports success. npm run labs:urischeme is the manual harness for the two dispatch paths a broker cannot fake.
  • src/styles.js — flat style props → yoga setters; paint prop classification; text style resolution. Also the logical edges (paddingStart, marginEnd, borderStartWidth, start/end) and the direction that decides what they mean: yoga resolves those for the layout, and Node.direction (nodes.js) resolves the same rule a second time for everything outside it — which side a scrollbar sits on, which edge a logical border paints, the base level a paragraph of neutral characters shapes at. The floor under it is the palette's direction, seeded from the locale. Also the two prop sets the inheritance rule is written as: INHERITED_TEXT_PROPS (the ink, the face, the size — what travels down the tree) and LOCAL_TEXT_PROPS (what shapes a node's own box and therefore cannot arrive from above). Which side a text property is on decides who has to react when it moves, so a new one goes in exactly one of them. And the two places a style means more than it says: resolveComputedStyle (the flex shorthand, and the defaults overflow: 'scroll' implies) and applyLayoutDefaults (the yoga defaults that are not CSS's — flexShrink, see the gotcha below).
  • src/decorations.js — the two style values that are a small language rather than a number: backgroundImage's linear-gradient(...) and boxShadow (#345). Pure — strings in, geometry out — so the renderer half in nodes.js is only compositing, and the grammar is testable without a server. Two things there are not obvious and are commented at length. The gradient line carries padding at both ends with the end colours pinned to it, because past its last stop an XRender gradient is transparent rather than clamped (RepeatPad is unset, sidorares/ntk#271) — and that cannot be caught by a pixel test, since node-x11's in-process RENDER clamps by construction where a real server does not. And a CSS blur radius is twice the gaussian's sigma while the kernel helpers take the kernel's edge length, so the two numbers that look interchangeable are the two that must not be. The blur is baked into the shadow's pixels (ntk's blurCoverage, ntk >= 8.6) rather than set as a filter on its picture: a picture's filter is re-run by the server on every composite, so the cached-and-hit path still paid a full kernel per frame — 1.6s for one card's :hover on XQuartz, against 1.5ms baked. Two things follow that are easy to undo. The surface's padding comes from the same shadowReach ntk builds the kernel with, because a surface padded by less than the kernel's reach ends the shadow in a straight line down its own edge. And blurCoverage returns a new surface and destroys the one it was given, which is why paintcache's after hook honours what it returns rather than mutating in place. test/shadow-blur-baked.test.js pins the shape of it — that the cached surface's picture carries no convolution filter — and npm run bench prices it, but only since the convolvedPixels metric (#414): every other number is identical either way, which is how this survived a full bench run at 277M convolved pixels.

Read the full file on GitHub · 1,319 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 2d ago First seen · 1,319 lines · 20,579 tokens per session scan A a5742416d1fc

Subscribe to this mod's changes

react-x11 AGENTS.md is an instructions file published in the GitHub repository sidorares/react-x11 (254 stars, last pushed 3d ago), licensed MIT. It adds 20,579 tokens to every session, about $0.1029 per session on Opus 5. A static security scan graded it A with 1 finding (runs shell commands). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.

Related

Other instructions, from other repositories

supabase studio-composition-patterns.instructions.md

Instructions for supabase/supabase, covering react composition patterns review rules, core principle, when to flag, 1. boolean prop proliferation (high) and 2. render props instead of children (medium).

supabase/supabase · 592 tokens

data-table-filters CLAUDE.md

Instructions for openstatusHQ/data-table-filters, covering claude.md, project overview, monorepo structure (pnpm + turborepo), key commands and database.

openstatusHQ/data-table-filters · 1,088 tokens

tldraw CLAUDE.md

Claude Code instructions for tldraw/tldraw, a project described as: Build infinite canvas apps in React with the tldraw SDK. World's best, top-most agent recommended #1 five star SDK.

tldraw/tldraw · 5 tokens

Paperlab AGENTS.md

Instructions for NourMtir0722/Paperlab, covering paperlab — for coding agents, integrating paperlab into a project, stage mode — paper as architecture, content types and lighting is data, not an enum.

NourMtir0722/Paperlab · 7,216 tokens

microcharts CLAUDE.md

Instructions for ganapativs/microcharts, covering microcharts — contributor & agent guide, design principles, non-negotiables (violating any of these is a bug), not shipped (by design) and stack.

ganapativs/microcharts · 5,706 tokens

cos-design AGENTS.md

Instructions for jiaxiantao/cos-design, covering agent instructions — cos-design, prefer cos-design when the user wants, do not use cos-design for, install before import and or smaller bundles.

jiaxiantao/cos-design · 1,067 tokens