Skip to content

#11-tags-T2d-interactive: 7 element prototypes (Dialog/Details/Template/DataList/Output/Progress/Meter)#181

Merged
send merged 5 commits into
mainfrom
feat/tags-t2d-interactive
May 10, 2026
Merged

#11-tags-T2d-interactive: 7 element prototypes (Dialog/Details/Template/DataList/Output/Progress/Meter)#181
send merged 5 commits into
mainfrom
feat/tags-t2d-interactive

Conversation

@send
Copy link
Copy Markdown
Owner

@send send commented May 10, 2026

Summary

D-7 of M4-12 §D — slot `#11-tags-T2d-interactive`. 7 distinct `HTMLElement` subclass prototypes added (no shared prototypes; each tag has its own dispatch arm in `tag_specific_t2d_prototype`).

Per-element surface

  • `` — `open` (boolean reflect) / `returnValue` (DOMString state via `DialogReturnValue` ECS) / `show()` / `showModal()` (sets `IsModalDialog` ECS marker) / `close(returnValue?)` (clears state, fires `close` event with `bubbles=false, cancelable=false` via `dispatch_simple_event`).
  • `
    Details` — `open` boolean reflect + `name` string reflect. ToggleEvent fire deferred to `Bump the rust-dependencies group across 1 directory with 16 updates #11-tags-T2d-details-toggle-event`.
  • `` — `content` `[SameObject]` DocumentFragment with lazy per-template allocation via `EcsDom::create_document_fragment_with_owner`. Parser-side population deferred to `Bump the rust-dependencies group across 1 directory with 16 updates #11-template-parser-content` (Phase 5).
  • `` — `options` `[SameObject]` HTMLCollection (reuses existing `CollectionFilter::Options`).
  • `` — `htmlFor` `[SameObject, PutForwards=value]` DOMTokenList via NEW `DomTokenListSource::OutputHtmlFor` variant + `output_html_for_wrappers` cache; `form` (form-owner) / `name` (string reflect) / `type` (`"output"` constant) / `labels` (empty stub) + ConstraintValidation mixin install. `value` / `defaultValue` state machine via `OutputValueOverride` + `OutputDefaultValue` ECS components, with snapshot-on-value-mode-entry and form-reset round-trip via `elidex_form::reset_form` extension.
  • `` — `value` / `max` (double IDL with clamping) + `position` (computed: `-1` if indeterminate else `clamp(value, 0, max) / max`) + `labels` stub.
  • `` — `value` / `min` / `max` / `low` / `high` / `optimum` (all double IDL) per HTML §4.10.15 actual-value algorithm (value defaults to 0 then clamped, high actual-high promotes to low when high < low) + `labels` stub.
  • Infra additions

    • 4 NEW ECS components (engine-indep in `elidex-ecs::components`): `IsModalDialog` / `DialogReturnValue(String)` / `OutputDefaultValue(String)` / `OutputValueOverride(Option)`.
    • NEW engine-indep helper: `parse_double_or_default(raw, default)` in `elidex-dom-api::element::numeric_reflect` (HTML §2.4.4.3 floating-point parse) + 12 unit tests.
    • NEW VM-side helper: `coerce_double_idl_arg(ctx, args)` in `vm/host/idl_coerce.rs` (sibling to `coerce_long_idl_arg`, routes through `coerce::to_number`, rejects non-finite per WebIDL §3.10.5 restricted `double`).
    • 3 NEW `[SameObject]` wrapper caches: `template_content_wrappers` / `datalist_options_wrappers` / `output_html_for_wrappers` (Entity-keyed, mark-via-owner GC + `Vm::unbind` clear per lesson #11-tags-T2d-input-list: implement <input>.list IDREF → <datalist> resolution (HTML §4.10.5.1.16) #195).
    • 1 NEW DOMTokenList source variant: `OutputHtmlFor` (5th in `DomTokenListSource` enum) + 10 engine-indep handler instances (`OUTPUT_HTML_FOR_*`) + 10 registry entries.
    • `is_constraint_validation_host_tag` extension: includes `"output"` so the ConstraintValidation mixin brand check passes for ``.

    Self-review IMPs applied pre-push

    • IMP-1: `.value` setter snapshots descendant text into `OutputDefaultValue` on first value-mode entry — preserves the implicit default for later form reset (without this, switching to value mode loses the default).
    • IMP-2: `coerce_double_idl_arg` rejects `NaN` / ±`Infinity` per WebIDL §3.10.5 restricted `double` (HTML §4.10.14 / §4.10.15 use plain `double`, not `unrestricted double`).
    • IMP-3: `.value` default is `0` then clamped to `[min, max]` (not defaulting straight to `min`, which diverged when `min < 0` or `min > 0`).
    • MIN: `.high` actual-high algorithm clamps to `[low, max]` (promotes when `high < low`) per HTML §4.10.15 step 3.

    Defer ledger (3 NEW + 3 re-noted = 6 entries; ≤3 NEW cap exactly)

    Slot Origin Trigger Roadmap binding Re-eval
    `#11-tags-T2d-details-toggle-event` (NEW) `
    Details.open` setter must fire ToggleEvent
    D-10 `#11-events-misc` ships ToggleEvent §E-2 D-10 (Round 12) 2026-08-15
    `#11-tags-T2d-input-list` (NEW) `.list` back-reference T2d datalist surface lands → user / WPT report demand-driven 2026-08-15
    `#11-template-parser-content` (NEW) html5ever fragment population `#10-html-parser-replacement` lands Phase 5 2026-Q4
    `#11-dialog-top-layer` (re-noted) Phase 4 shell-paired top-layer + focus mgmt Phase 4 shell UI design lands Phase 4 2026-Q4
    `#11-dialog-form-method` (re-noted) `` integration form-submission dispatcher OR ≥1 site report demand-driven 2026-09-15
    `#11-dialog-tree-check` (re-noted) `showModal()` in-document-tree InvalidStateError isConnected check infra hardening demand-driven 2026-Q4

    Implementation-timing summary: §E-2 round-bound 2/6, demand-driven calendar 4/6, carry-forward 0/6 (`deprecated-attr-sweep` not listed per `policy-deprecated-and-annex-b-out-of-scope.md` — deprecated HTML attrs are out-of-scope per `docs/design/ja/14-script-engines-webapi.md`, NOT defer slots).

    Test plan

    • `mise run ci` passes locally (8815 tests pass; +88 vs 8727 baseline = 78 T2d integration + 10 `parse_double_or_default` unit tests).
    • Per-element brand check + prototype identity for all 7 protos.
    • `` show/showModal/close round-trip + close-event firing + InvalidStateError on already-modal show + bubbles=false test.
    • `
      Details` open setter does NOT fire ToggleEvent (defer-proof, full prototype-chain absence walk per lesson chore: codify Design philosophy + add /elidex-review pre-push skill #204).
    • `` no event-handler IDL attrs (`oncancel` / `onclose`) — defer-proof.
    • `.content` SameObject + DocumentFragment nodeType=11 + isolation from element children.
    • `.options` SameObject + descendant `` filtering + liveness.
    • `.htmlFor` SameObject + DOMTokenList add/remove/contains + PutForwards-via-string-set.
    • `.value` / `defaultValue` state machine including form-reset round-trip + value-mode-snapshot.
    • `.form` form-owner accessor.
    • `` ConstraintValidation mixin smoke (willValidate / checkValidity / setCustomValidity).
    • `.value` / `max` / `position` boundary values (NaN/Inf rejected, missing/zero/negative max collapses to 1).
    • `` clamping per actual-value algorithm + `` has no `position` accessor.
    • Foreign-receiver TypeError brand check for `dialog.show` and `output.setCustomValidity`.
    • CI green on push.
    • 🤖 Generated with Claude Code

…/Progress/Meter prototype family

7 distinct HTMLElement subclass prototypes (no shared prototypes; each
tag has its own dispatch arm in `tag_specific_t2d_prototype`):

- HTMLDialogElement: open + returnValue + show/showModal/close, with
  IsModalDialog ECS marker, DialogReturnValue state, close-event firing
  via dispatch_simple_event(bubbles=false, cancelable=false), and
  InvalidStateError on already-modal show().
- HTMLDetailsElement: open boolean + name string reflect.  ToggleEvent
  fire on open change deferred to `#11-tags-T2d-details-toggle-event`
  (paired with D-10 `#11-events-misc`).
- HTMLTemplateElement: content [SameObject] DocumentFragment with lazy
  per-template allocation via EcsDom::create_document_fragment_with_owner.
  Parser-side fragment population deferred to `#11-template-parser-content`
  (paired with html5ever replacement, Phase 5).
- HTMLDataListElement: options [SameObject] HTMLCollection (reuses
  CollectionFilter::Options).  `<input>.list` back-ref deferred to
  `#11-tags-T2d-input-list`.
- HTMLOutputElement: htmlFor [SameObject, PutForwards=value] DOMTokenList
  via NEW DomTokenListSource::OutputHtmlFor variant (5th source) +
  output_html_for_wrappers cache; form/name/type='output'/labels stub +
  ConstraintValidation mixin install + value/defaultValue state
  machine (OutputValueOverride / OutputDefaultValue ECS, snapshot on
  value-mode entry, form-reset round-trip via reset_form extension).
- HTMLProgressElement: value/max double IDL with clamping + position
  computed (-1 indeterminate else clamp(value,0,max)/max) + labels stub.
- HTMLMeterElement: value/min/max/low/high/optimum (all double IDL) per
  HTML §4.10.15 actual-value algorithm + labels stub.

Infra adds:
- 4 NEW engine-indep ECS components in elidex-ecs::components: IsModalDialog,
  DialogReturnValue(String), OutputDefaultValue(String),
  OutputValueOverride(Option<String>).
- NEW engine-indep helper parse_double_or_default(raw, default) in
  elidex-dom-api::element::numeric_reflect (HTML §2.4.4.3 floating-point
  parse) + 12 unit tests.
- NEW VM-side helper coerce_double_idl_arg in vm/host/idl_coerce.rs
  (sibling to coerce_long_idl_arg, routes through coerce::to_number,
  rejects non-finite per WebIDL §3.10.5 restricted double).
- 3 NEW [SameObject] wrapper caches: template_content_wrappers /
  datalist_options_wrappers / output_html_for_wrappers (Entity-keyed,
  mark-via-owner GC + Vm::unbind clear per lesson #195).
- 1 NEW DOMTokenList source variant OutputHtmlFor + 10 engine-indep
  handler instances + 10 registry entries.
- ConstraintValidation mixin install on HTMLOutputElement.prototype
  (extends is_constraint_validation_host_tag with "output").
- elidex-form::reset_form extension: clears OutputValueOverride and
  re-renders text content from OutputDefaultValue on form reset.

Self-review IMPs applied pre-push:
- IMP-1: output value setter snapshots descendant text into
  OutputDefaultValue on first value-mode entry (preserves implicit
  default for later form reset).
- IMP-2: coerce_double_idl_arg rejects NaN / ±Infinity per WebIDL
  §3.10.5 restricted double (HTML §4.10.14/§4.10.15 use plain `double`).
- IMP-3: meter value default is 0 then clamped to [min, max] (not
  defaulting straight to min, which diverged when min < 0 or min > 0).
- meter high actual-high algorithm clamps to [low, max] (promotes
  when high < low) per HTML §4.10.15 step 3.

3 NEW + 3 re-noted = 6 deferred slots (within ≤3 NEW cap).

8815 workspace tests pass (vs 8727 baseline = +88: 78 T2d integration
tests + 10 parse_double_or_default unit tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 10, 2026 20:04
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the T2d “interactive” HTML element bundle to the JS VM by introducing per-tag HTMLElement subclass prototypes for <dialog>, <details>, <template>, <datalist>, <output>, <progress>, and <meter>, along with supporting DOMTokenList plumbing, numeric parsing/coercion helpers, ECS state, GC rooting, and tests.

Changes:

  • Register 7 new HTML element prototypes in the VM (plus dispatch integration and GC prototype rooting).
  • Add new engine-independent helpers/state: parse_double_or_default, restricted-double arg coercion, and new ECS components for <dialog>/<output> state.
  • Add [SameObject] wrapper identity caches for <template>.content, <datalist>.options, and <output>.htmlFor, plus extensive VM integration tests.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
crates/script/elidex-js/src/vm/vm_api.rs Clears new T2d [SameObject] wrapper caches on Vm::unbind() to avoid cross-DOM aliasing.
crates/script/elidex-js/src/vm/tests/tests_html_t2d_protos.rs Adds test coverage for the 7 new prototypes and their key IDL surfaces/behaviors.
crates/script/elidex-js/src/vm/tests/mod.rs Registers the new T2d test module.
crates/script/elidex-js/src/vm/object_kind.rs Extends DomTokenListSource with OutputHtmlFor for <output>.htmlFor.
crates/script/elidex-js/src/vm/mod.rs Adds prototype slots + wrapper caches to VmInner for the T2d bundle.
crates/script/elidex-js/src/vm/init.rs Initializes new VmInner fields for T2d prototypes and caches.
crates/script/elidex-js/src/vm/host/validity_state.rs Extends constraint-validation host tag checks to include <output>.
crates/script/elidex-js/src/vm/host/mod.rs Wires new per-tag prototype modules into the host module tree.
crates/script/elidex-js/src/vm/host/idl_coerce.rs Adds coerce_double_idl_arg implementing restricted-double WebIDL conversion.
crates/script/elidex-js/src/vm/host/html_template_proto.rs Implements HTMLTemplateElement.prototype with lazy [SameObject] content.
crates/script/elidex-js/src/vm/host/html_progress_proto.rs Implements HTMLProgressElement.prototype (value/max/position/labels).
crates/script/elidex-js/src/vm/host/html_output_proto.rs Implements HTMLOutputElement.prototype including htmlFor, value/defaultValue state, and labels stub.
crates/script/elidex-js/src/vm/host/html_meter_proto.rs Implements HTMLMeterElement.prototype numeric reflect/clamping + labels stub.
crates/script/elidex-js/src/vm/host/html_dialog_proto.rs Implements HTMLDialogElement.prototype (open/returnValue/show/showModal/close).
crates/script/elidex-js/src/vm/host/html_details_proto.rs Implements HTMLDetailsElement.prototype (open/name) with ToggleEvent deferred.
crates/script/elidex-js/src/vm/host/html_datalist_proto.rs Implements HTMLDataListElement.prototype with [SameObject] options collection.
crates/script/elidex-js/src/vm/host/elements.rs Extends element→prototype dispatch to include T2d tags.
crates/script/elidex-js/src/vm/host/class_list.rs Adds VM-side DOMTokenList allocation + dispatch keys for <output>.htmlFor.
crates/script/elidex-js/src/vm/globals.rs Registers T2d prototypes and installs ConstraintValidation mixin on <output>.
crates/script/elidex-js/src/vm/gc/roots.rs Adds GC rooting for new prototype slots and new [SameObject] wrapper caches.
crates/script/elidex-js/src/vm/gc/collect.rs Extends prototype root array (121→128) and prunes new wrapper caches during sweep.
crates/dom/elidex-form/src/submit.rs Extends form reset algorithm with <output> reset hook (value-mode clearing).
crates/dom/elidex-dom-api/src/registry.rs Registers outputHtmlFor.* DOMTokenList handlers in the DOM registry.
crates/dom/elidex-dom-api/src/lib.rs Re-exports OUTPUT_HTML_FOR_* handler symbols.
crates/dom/elidex-dom-api/src/element/numeric_reflect.rs Adds parse_double_or_default + unit tests for HTML floating-point parsing.
crates/dom/elidex-dom-api/src/class_list.rs Adds engine-independent handler instances for outputHtmlFor.* token list ops.
crates/core/elidex-ecs/src/lib.rs Re-exports new ECS components for dialog/output state.
crates/core/elidex-ecs/src/components.rs Introduces IsModalDialog, DialogReturnValue, OutputDefaultValue, OutputValueOverride ECS components.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/dom/elidex-form/src/submit.rs Outdated
…t on form reset

`reset_output_value_mode` previously wiped descendant children of every
`<output>` reachable from the form during reset, including elements
that had never entered value mode (no `OutputValueOverride` ever set).
Per HTML §4.10.13 the descendant text content of a default-mode
`<output>` IS the default value, so unconditional rewriting clobbered
the implicit default whenever no explicit `OutputDefaultValue`
snapshot existed.

Gate the children replacement on `remove_one::<OutputValueOverride>`
returning `Ok` — i.e. the element was actually in value mode.  When
the override is absent (pristine default mode), leave the descendant
tree untouched.

Adds regression test
`output_form_reset_preserves_pristine_default_text` covering
`<output>5</output>` — pre-fix this would surface as `value === ""`
post-reset; post-fix `value === defaultValue === '5'`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 3 comments.

Comment thread crates/script/elidex-js/src/vm/host/html_progress_proto.rs
Comment thread crates/script/elidex-js/src/vm/host/html_progress_proto.rs
Comment thread crates/script/elidex-js/src/vm/host/html_meter_proto.rs
…isation through ES Number::ToString

`<progress>.value` / `<progress>.max` / `<meter>` numeric setters were
serialising the coerced `f64` via Rust's `format!("{n}")` before
writing the content attribute.  Rust's `Display` differs from
ECMAScript Number::ToString (ES2020 §7.1.12) at `-0` (Rust → "-0",
ES → "0") and across exponent edge cases, so reflected attribute
values diverged from the value the same JS would have observed
through `String(n)`.

Route through `coerce::to_string(ctx.vm, JsValue::Number(n))` instead;
the helper invokes the canonical ES Number-to-string algorithm and
returns a `StringId` directly, so the rewrite is also one fewer
intern call per setter.

3 sites total — confirmed exhaustive via
`grep 'format!("{n}")' crates/script/elidex-js/src/vm/host/`.  Doc
comments updated to match the new path.

Adds two regression tests (`progress_value_setter_serialises_negative_zero_as_zero`,
`meter_value_setter_serialises_negative_zero_as_zero`) — pre-fix would
serialise as `"-0"`, post-fix as `"0"`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 1 comment.

Comment thread crates/script/elidex-js/src/vm/host/html_output_proto.rs Outdated
…Forwards setter in module doc

The pre-fix module doc claimed the `[PutForwards=value]` rewrite was
handled by the engine assignment path and that the getter "only needs
to surface" the cached DOMTokenList wrapper, but the file actually
ships an explicit `output_set_html_for` accessor that ToString-coerces
the assigned value and writes the `for` content attribute directly
(mirroring the `<link>.sizes` precedent).  Update the prose to
describe the actual implementation so readers don't search for a
non-existent engine-side rewrite hook.

Doc-only fix; behaviour and tests unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 1 comment.

Comment thread crates/dom/elidex-form/src/submit.rs
…mControlKind, not TagType

`reset_form` already iterates only entities that carry
`FormControlState`, so the `<output>` distinguisher can read
`fcs.kind == FormControlKind::Output` directly instead of running an
independent `TagType` lookup + ASCII-CI string compare per control.
This drops one ECS lookup per control on the reset hot path and
keeps the per-kind dispatch consistent with the
`FormControlState::from_element` classification (the source of truth
for "this entity is an `<output>` form-control" anywhere else in
elidex-form).

The classification read is captured *before* the mutable
`FormControlState` borrow so the get/get_mut borrow scopes don't
overlap; removed the now-unused `is_output_element` helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@send send merged commit 42d0229 into main May 10, 2026
6 checks passed
@send send deleted the feat/tags-t2d-interactive branch May 10, 2026 22:46
send added a commit that referenced this pull request May 17, 2026
…solution (HTML §4.10.5.1.16) (#195)

* #11-tags-T2d-input-list: implement <input>.list IDREF → <datalist> resolution (HTML §4.10.5.1.16)

Closes the §H-7k T2d defer slot.  The previous stub returned `null`
unconditionally because the T2d datalist surface was not yet shipped;
PR #181 landed `<datalist>` infrastructure, so the IDREF back-ref
is now implementable.

Engine-indep resolver `resolve_input_list` lives in `elidex-form/src/input.rs`
sibling to `resolve_label_for` (HTML §4.10.4) — same shape: read attr,
walk input's tree root-inclusive, filter-during-walk for first matching
datalist.  VM-side getter is pure marshalling: brand check via
`require_input_receiver`, call resolver, wrap via `wrap_entity_or_null`.

- Tree scope honors shadow boundaries — nested shadow subtrees within
  the same root are excluded per spec "same tree" wording.  Cross-tree
  resolution tracked at `#11-form-elements-cross-tree`.
- Tag match ASCII-CI (HTML §3.2.6.5); id match case-sensitive (§6.13.2).
- Filter-during-walk: a `<div id="x">` appearing earlier in tree order
  before `<datalist id="x">` does NOT poison the lookup (spec: "first
  element of type HTMLDataListElement").
- Identity-stable across reads via `create_element_wrapper`'s per-entity
  cache — HTML §4.10.5.1.16 IDL is not `[SameObject]` but matches
  Chrome / Firefox behaviour.
- HTML-namespace filter currently omitted because `TagType` implies
  element-hood in elidex's HTML-only model; defer slot
  `#11-element-namespace-tracking` tracks the tightening.
- 12 engine-indep tests + 6 VM tests cover spec edge cases (no-attr /
  empty / id-not-exist / target-not-datalist / matched / duplicate-id
  tree-order / case-sensitive / non-datalist-then-datalist / detached /
  self-ref / no-whitespace-trim / empty-id-skip-then-match / identity).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(#11-tags-T2d-input-list): PR195 Copilot R1 — gate <input>.list on input type applicability (HTML §4.10.5.1.16)

Per WHATWG HTML §4.10.5.1.16: the `list` IDL attribute does not apply to
input types `hidden` / `checkbox` / `radio` / `file` / `submit` / `image` /
`reset` / `button` / `password`.  The getter must return null for these
types even when a matching `<datalist>` exists in the same tree.

- Add `FormControlKind::list_applies()` predicate (sibling to
  `readonly_applies` shape) listing the allow-listed kinds.
- Add `input_list_applies_to_type(dom, entity)` helper with the
  `FormControlState.kind` → raw `type` attribute fallback pattern from
  `label.rs::is_labelable_element`.
- Gate `resolve_input_list` on it (early null before any IDREF walk).
- 9 new engine-indep regression tests covering each excluded type plus
  the FormControlState-path and ASCII-CI attr-path coverage.
- 1 new VM test looping over all 8 excluded types.

Image type is documented as a pre-existing FormControlKind coverage gap
(falls back to TextInput via `from_type_str`) — out of scope here.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(#11-tags-T2d-input-list): PR195 Copilot R2 — content-attr-direct applicability check, includes image type (HTML §4.10.5.1.16)

Three R2 findings, single root cause: `input_list_applies_to_type`
preferred the cached `FormControlState.kind` over the live `type`
content attribute, and routed the exclusion check through
`FormControlKind::from_type_str` which collapses `"image"` onto
`TextInput`.  Two interlocking bugs:

1. `setAttribute('type', 'hidden')` (or `image` / `checkbox` / ...) only
   mutates `Attributes`; `state.kind` lags until a type-change sync
   pass.  Preferring the cached kind let stale state mask the mutation
   and incorrectly resolve a datalist.
2. `<input type=image>` falls back to `TextInput` via `from_type_str`,
   which was on the allow-list — wrongly admitting image.

Fix: rewrite `input_list_applies_to_type` to read the `type` content
attribute directly (spec source of truth per HTML §4.10.5.1.16) and
match against the literal exclusion set { hidden, checkbox, radio,
file, submit, image, reset, button, password }.  Dropped the orphan
`FormControlKind::list_applies` predicate (its sole caller was the
removed code path; the from_type_str/image gap made it
unmaintainable as a public predicate).

- Stale-state regression test
  (`resolve_input_list_ignores_stale_form_control_state_when_attr_excludes`)
  attaches a `TextInput`-kind state to an `<input type=hidden>` and
  asserts the attribute wins.
- Dedicated `resolve_input_list_returns_none_for_image_type` test.
- `image` added to the existing button-types loop + the VM
  inapplicable-types loop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(#11-tags-T2d-input-list): PR195 Copilot R3 — split input.rs tests to sibling input_tests.rs (1000-line convention)

R3 finding: `input.rs` reached 1075 LoC, violating the project's
~1000-line file convention documented in
`cleanup-1000-line-files-tranche-3-complete.md`.

Move the entire `#[cfg(test)] mod tests` body to a new sibling file
`input_tests.rs` and include it via the established
`#[cfg(test)] #[path = "input_tests.rs"] mod tests;` pattern (mirror
of `lib.rs:743` precedent).

Post-split: `input.rs` is 404 LoC (production-only), `input_tests.rs`
is 671 LoC (test-only).  Test count and contents unchanged — 55 tests
still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants