# Changelog
All notable changes to Neura are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/); versions follow
[Semantic Versioning](https://semver.org/).
## [0.2.0] — unreleased
### Security
- **Security review across the library, shim, docs site, and delivery
pipeline.** No exploitable defect was found in the components'
default rendering paths - every text option was already inserted as
text. The review hardened the edges around them:
- `Neura.escapeHtml` is now part of the public API, so consumers have
a supported escaper for the options that deliberately take raw HTML
(`bodyHtml`, select `format*`, restful-table `readView`). It escapes
quotes as well as angle brackets, making it safe in attribute
contexts - the widespread `textContent`/`innerHTML` round-trip is
not.
- Date-picker labels drawn from `Neura.i18n` are escaped before being
written into the calendar markup, so a translation pack from an
untrusted source cannot inject markup or break out of an attribute.
- Element ids are escaped with `CSS.escape` before being used in
attribute selectors (dropdown, inline dialog, expander), and
restful-table encodes row ids into request URLs.
- Keyboard shortcuts refuse to navigate to non-fetchable URL schemes,
and `AJS.whenIType.fromJSON` only invokes operations from a fixed
allowlist.
- `AJS.Cookie` validates cookie names and escapes them before use,
restoring protection AUI's design provided implicitly; cookies are
written with `SameSite=Lax`.
- Docs examples no longer model unescaped interpolation of fetched
data - the dropdown and inline-dialog lazy-load samples build nodes
and set `textContent`.
- **Docs site headers.** The site's security headers were defined but
never sent: nginx drops inherited `add_header` directives in any
location that sets one of its own, and every location did. They are
now included in each location and verified in a test. The site also
sends a Content-Security-Policy, and `Cross-Origin-Opener-Policy` was
added.
- **Published integrity hashes now work.** The SRI manifest was written
to the site root while the CDN page fetched it from `/cdn/`, so the
integrity table was empty in every built image. The hashes are
verified to match the bytes the CDN actually serves.
- **Zero known vulnerabilities in the dependency tree** (the library
itself has no runtime dependencies). Build tooling was updated to
clear three advisories in dev-only packages.
- A [security policy](SECURITY.md) documents the escaping model, which
options take raw HTML, and how to report a vulnerability privately.
### Added
- **Responsive header fixed** (three bugs found by a docs-vs-source
audit): the primary-nav lookup only matched a wrapper-div markup form,
so the overflow-into-More behavior silently never activated on the
canonical `
` markup; `refresh()` wiped the
overflow menu with `innerHTML`, losing items that should return to
the bar on a partial resize; and `destroy()` removed the menu with
overflowed items still inside, deleting them from the page. The
available-width math now measures the secondary nav's width against
the container edge (its left edge is meaningless mid-measurement when
the float wraps). New Playwright spec covers the full
collapse/restore/destroy cycle.
- **Editor metadata + playground + SRI** — the package now ships
`web-types.json` (IntelliJ-family IDEs autocomplete every `neura-*`
class with doc links, straight from node_modules) and a VS Code
snippets file with the common skeletons. The docs site gained a
**Playground** (edit markup, preview against the real bundles with
auto-init live, theme switcher) and the CDN page publishes
**Subresource Integrity hashes** for the version-pinned files. The
npm package metadata is publish-ready: proper `exports` with the
`types` condition, complete `files` list, side-effect marking.
- **Recipes** — a new guide page of complete, tested patterns for
server-rendered apps: confirming a destructive action (declarative
`data-confirm` triggers, one delegated listener, text-safe content,
POST semantics), loading a form into a dialog (wired via the
bubbling `neura-dialog-show` event), and mapping server validation
errors onto form fields. Every recipe runs live with exactly the
code shown and is covered by its own browser spec.
- **Migration scanner / codemod** — `npm run migrate -- ` scans
any AUI 5.4 codebase and reports every `aui-*` class and `AJS.*`
call with a verdict: mechanical rename (applied by `--write`),
covered by the shim, dead even in AUI, deliberately unported, or
unknown (`--strict` turns unknowns into a CI failure). The mapping
is parsed from the shim source itself, so the report cannot drift
from the library. `AJS.*` calls are never rewritten automatically;
the report names the Neura target for each.
- **TypeScript definitions** — `neura.d.ts` ships in the flatpack, the
zip, and the CDN: the full `Neura.*` API typed (instance methods,
option shapes, event names) plus the `window.Neura` global, giving
autocomplete and checking in TypeScript and plain-JS editors alike.
Compile-checked in CI against a usage fixture. The legacy `AJS`
facade stays deliberately untyped.
- **`Neura.debug` - dev-mode diagnostics** — opt-in markup-contract
warnings (`` or `Neura.debug(true)`;
`Neura.debug.scan(el)` for injected content). Neura now tells you,
with the fix in the message, when a trigger's `aria-controls` points
nowhere, tooltip text sits in the wrong attribute, a tabs link has
no pane, a dialog has no id, or a closeable message has no close
control. Zero cost when disabled.
- **Docs example validation** — a new repeatable harness
(`npm run validate:docs`) verifies every code panel on every docs
page: JavaScript snippets must parse, `Neura.*`/`AJS.*` calls must
resolve against the real runtime surface, HTML snippets may only
reference classes that exist in the built stylesheet (or documented
semantic markers), and CSS snippets are checked for balance and
real token names. A companion crawl clicks every demo trigger on
every page watching for console errors, and a mirror check compares
HTML panels against the rendered demos. The full pass found and
fixed: a wrong checkbox wrapper class in the kitchen sink, a JSON
exchange example mislabeled as JavaScript, and the sortable-table
demo not exercising the unsortable-column modifier its snippet
documents (it now has an Actions column).
- **Brand: derived color system** — the accent palette is now
generated, not hand-picked: every brand stop derives from the single
anchor `#3f3d9c` via Neura's OKLCH ramp formula
(`scripts/derive-palette.mjs`), with lightness solved against WCAG
contrast targets, chroma clamped to gamut, and the visited-link hue
fixed at an owned purple; the built-in themes are the same formula
at other hues. Contrast improves along the way (white on primary
buttons: 3.9:1 → 4.6:1 top stop, 6.0:1 bottom). The docs site gained
Neura's mark — the "nerve thread" N (one continuous stroke, a node
at each synapse end) as favicon and header brand. Two new guide
pages articulate the identity: **Design principles** (the two-era
strategy: parity now on purpose, the Neura visual language at 2.0,
plus the six principles and the color-system contract) and **How
it's built** (the verification culture: pixel-diffed parity,
source-executed behavioral contracts, three test layers).
- **AJS window event bus made faithful — the portal's dialog lifecycle
works under the drop-in.** A source-level audit of the Nsys portal
found its single most load-bearing integration: AUI's
`AJS.bind/trigger` are jQuery(window) events, legacy `AJS.Dialog`
broadcasts `show.dialog`/`hide.dialog`/`remove.dialog` there, and six
portal plugin files hook `show.dialog` (keyed on `data.dialog.id`) to
wire the forms inside ajax-loaded dialogs. The shim's bus was a
private EventTarget and the events were never fired — every
dialog-hosted validation flow and the dashboard gadget editor would
have gone silently dead. `AJS.bind/unbind/trigger` now route through
`jQuery(window)` whenever jQuery is present (CustomEvent fallback
otherwise), and the legacy Dialog fires all three lifecycle events
with `{dialog}` payloads, in AUI's order.
- **Migration file map corrected** — the portal decorator loads SEVEN
files from the AUI block, and the docs said `aui-dependencies.js`
"can be blanked". Wrong: it bundles jQuery UI 1.8.24, which dashboard
gadget drag-reordering depends on — blanking it breaks that silently.
The drop-in path (CLAUDE.md, compatibility-page checklist, llms.txt)
now lists all seven files with the correct action per file: swap
`aui.css`/`aui.js`, blank `aui-experimental.{css,js}` + `aui-soy.js`
(their load-bearing parts — table sorting, the icon font — are
covered), keep `aui-dependencies.js` as-is.
- **`AJS.debounce`** — an audit against AUI's helper-functions doc found
it missing from the shim (it's real in AUI 5.4 core); ported with
AUI's exact semantics (trailing-edge, preserves `this`/args, wrapper
returns the last completed result). The page's other helper,
`$.fn.isDirty`, stays unported by design: it was a jQuery plugin the
portal's flatpack never bundled, so no Nsys-era code can call it.
- **`AJS.template` shim made faithful to AUI's contract** — the previous
minimal fill-in interpolated values RAW; AUI's `.fill()` HTML-escapes
every value (that's its safety contract — legacy code passes untrusted
data through `{key}` tokens assuming escaping), so the drop-in could
reintroduce XSS in un-audited plugin code. Now verified against the
original template.js: escaping by default, per-value raw opt-in via a
`"key:html"` data key, path (`{a.b}`, `{a["x y"]}`) and call
(`{fn()}`) tokens, chainable partial fills (unresolved tokens
survive), `fillHtml()`, callable/stringifiable result, and
`AJS.template.load()` for `