Skip to content

fix: detect polarity changes in programmatic value updates#121

Open
paperboyo wants to merge 1 commit intoguardian:mainfrom
paperboyo:fix/polarity-attr-diff
Open

fix: detect polarity changes in programmatic value updates#121
paperboyo wants to merge 1 commit intoguardian:mainfrom
paperboyo:fix/polarity-attr-diff

Conversation

@paperboyo
Copy link
Copy Markdown

@paperboyo paperboyo commented Mar 27, 2026

This line is the only human thing about it (as you can tell from extensive comments in the code). Apologies if this makes no sense. I tried my best to make sure it does and that it's actually sensible. But who knows. Not moi. I just thought PRs better tha issues. Tests ran and good.

PR opened and reasoned for by Claude Opus 4.6

Problem

When a host application calls setAttribute("value", ...) on <cql-input> and only the polarity of a chip changes (e.g. +tag:foo-tag:foo), the editor silently ignores the update. The rendered query and emitted queryChange event still reflect the old polarity.

Cause

The vendored ProseMirror diff functions (findDiffStartForContent / findDiffEndForContent) compare child nodes by content only — they do not check node attributes. Two chip nodes with the same text but different POLARITY attributes are treated as identical, so mergeDocs finds no diff and returns early.

The original ProseMirror diff.ts includes a sameMarkup check that would catch this, but it was intentionally removed in #52 to prevent transient attributes (IS_SELECTED, IS_READ_ONLY) from causing unnecessary document replacements that destroy cursor position.

Fix

Introduce sameContentMarkup — a targeted comparison that checks node type, marks, and attributes except the transient ones (IS_SELECTED, IS_READ_ONLY). This restores sensitivity to semantic attribute changes like POLARITY while preserving the #52 fix.

The set of skipped attributes is explicit rather than inferred, so any future semantic attribute will be compared by default.

Why this does not regress #52

The #52 bug occurred because IS_SELECTED differs between the live document (set by the CQL plugin based on cursor position) and the incoming document (always default). sameContentMarkup explicitly skips IS_SELECTED and IS_READ_ONLY, so these transient differences never trigger a replacement.

All five existing selection-preservation tests continue to pass.

Tests

  • editor.spec.ts: verifies updateEditorView applies a polarity-only change
  • CqlInput.spec.ts: verifies the full setAttributequeryChange path emits the updated query

Context

Discovered while building kupua, which toggles chip polarity programmatically via setAttribute. Current workaround: force-remount the entire <cql-input> web component on polarity change.

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