Skip to content

feat(live_ui): declared phx-click + arbitrary data-* attribute passthrough for IUR nodes#159

Merged
ty13r merged 2 commits into
mainfrom
claude/liveui-attr-passthrough
Jun 13, 2026
Merged

feat(live_ui): declared phx-click + arbitrary data-* attribute passthrough for IUR nodes#159
ty13r merged 2 commits into
mainfrom
claude/liveui-attr-passthrough

Conversation

@ty13r

@ty13r ty13r commented Jun 13, 2026

Copy link
Copy Markdown
Member

Additive attribute passthrough for IUR nodes

AshUI.Rendering.LiveUIAdapter couldn't emit a custom phx-click event name (only fixed/derived names) or arbitrary data-* attributes from an IUR node, so host apps resorted to regex-injecting them into rendered HTML. This adds a clean, additive passthrough.

API (opt-in node props, read via the existing prop/2):

  • "on_click" => %{"event" => "select_doc", "values" => %{"doc_id" => "d1"}} → emits phx-click="select_doc" + one phx-value-<k>="<v>" per value. Bare string "on_click" => "select_doc" also accepted.
  • "data" => %{"block_id" => "b1"} → emits one data-<k>="<v>" per pair.

Implemented via a shared passthrough_attrs/1 (in the attr/2 helper family), wired append-only into the artifact_row clause + the generic/custom fallback. Mirrors the form widgets' existing phx-click/phx-value-* pairing, so the emitted HTML is idiomatic.

Security: every emitted value goes through html_attr/1 (escapes & < > " '); attribute-name keys are restricted to ~r/\A[A-Za-z0-9_-]+\z/ so a hostile key can't inject an extra attribute/handler. Cross-model review (Codex) ran a live injection probe — event = x" onmouseover="evil() renders as phx-click="x&quot; onmouseover=&quot;evil()" (escaped, no injected attribute); hostile keys dropped. A regression test codifies the event-name escaping.

Backward-compat: passthrough_attrs/1 returns "" when neither prop is present → nodes without these props render byte-identically. Both call sites are append-only; no existing clause's output changed. Full suite green: 1085 tests, 0 failures. 55 tests in the adapter test file (8 new for this feature, incl. value + event escaping + hostile-key rejection + unchanged-node).

Consumed by ariston-ui (operator Screens) to replace an HTML-regex-injection workaround (#829). Co-maintained framework change; reviewed Opus-impl → Codex-review (writer≠reviewer).

ty13r and others added 2 commits June 13, 2026 06:13
… LiveUIAdapter

Add an additive, opt-in passthrough so an IUR node can declare interaction
and identity attributes via its props and have LiveUIAdapter emit them:

  * "on_click" => %{"event" => "select_doc", "values" => %{"doc_id" => "d1"}}
    (or a bare "select_doc" string) -> phx-click + phx-value-<key> pairs
  * "data" => %{"block_id" => "b1"} -> data-<key> attributes

WHY: ariston-ui operator Screens need swimlane artifact rows to carry a
custom phx-click event + phx-value-* (e.g. select_doc / doc_id) and doc-block
widgets to carry data-block-id. Today those are regex-injected into rendered
HTML as a post-processing hack (the form-widget clauses only emit a fixed
event_name/phx-value-binding_id shape, and the generic fallback emits only
class/style/data-widget-id). This gives the renderer a first-class capability
and lets ariston-ui drop the regex workaround for #829.

A new shared helper passthrough_attrs/1 turns those props into attribute
strings via the existing attr/2 helper, wired into the artifact_row clause and
the generic/custom widget fallback. Values are HTML-escaped via html_attr/1 and
attribute-name keys are restricted to [A-Za-z0-9_-] so a hostile key cannot
inject extra attributes. Pairs emit in sorted key order for deterministic output.

Backward-compatible + additive only: a node WITHOUT the new props renders
byte-identically (passthrough_attrs/1 returns ""). The full existing ash_ui
suite stays green (1085 tests, 0 failures). New tests cover on_click ->
phx-click+phx-value, data -> data-*, the unchanged-node baseline, value
escaping, and hostile-key rejection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…onest backward-compat comment (closes Codex P3)

WHY: Codex P3 — on_click.event NAME had no explicit escaping test (existing
test covered only phx-value-* + data-* VALUES). lib/ untouched; test-only.

* Add test: renders artifact_row with event = x" onmouseover="evil(), asserts
  phx-click value is escaped (x&quot; onmouseover=&quot;evil()) and refutes raw
  injection string appearing as a live attribute.
* Replace tautological assert without == also_without in the byte-identically
  test with a comment noting drift-from-main is caught by the full suite
  (1085/0); concrete refutes on phx-click/phx-value-/data-* remain.
* Suite: 55 tests, 0 failures.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ty13r ty13r merged commit 99d498a into main Jun 13, 2026
8 checks passed
@ty13r ty13r deleted the claude/liveui-attr-passthrough branch June 13, 2026 11:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant