Skip to content

Conversation

@palmer-cl
Copy link
Collaborator

  • Refactored track-change mark snapshot logic into shared helpers
  • Fixed format suggestion reversibility using exact mark attr snapshot matching and consistent before/after upsert behavior, so rejecting a color suggestion restores original styling correctly.
  • Added regression coverage: an interaction test for “suggest color -> reject -> no inline color left” and new unit tests for markSnapshotHelpers (exact match, type fallback, upsert behavior, and range lookup).

@linear
Copy link

linear bot commented Feb 8, 2026

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d402911ed

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Contributor

@caio-pizzol caio-pizzol left a comment

Choose a reason for hiding this comment

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

Very clean implementation!

Unfortunately, it is not fully fixing it

Font color is "rejected" but the font family is not returning to the original one "Times New Roman" but to "Arial"

Original:
Image

After reject:
Image

@palmer-cl
Copy link
Collaborator Author

Very clean implementation!

Unfortunately, it is not fully fixing it

Font color is "rejected" but the font family is not returning to the original one "Times New Roman" but to "Arial"

Original: Image

After reject: Image

@caio-pizzol this should be fixed now. Refactored to make it more robust when tracking formatting

Copy link
Contributor

@caio-pizzol caio-pizzol left a comment

Choose a reason for hiding this comment

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

Feel free to merge it once relevant comments are addressed.

One thing to note: the interaction tests cover PM state correctly, but there's no interaction regression test for the complete scenario. Since rejecting format suggestions affects what the user sees, a visual test (import doc, suggest color, reject, screenshot-compare) would catch divergence.

Just added a story - let's sync if you have any question on adding this to other PRs.

return objectIncludes(normalizedLeft, normalizedRight) && objectIncludes(normalizedRight, normalizedLeft);
};

export const markSnapshotMatchesStepMark = (snapshot, stepMark, exact = true) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

three matching functions that are the same logic with different parameter types markSnapshotMatchesStepMark(snapshot, stepMark), markMatchesSnapshot(mark, snapshot), and hasMatchingMark(marks, stepMark) all do "does type match + do attrs match?"

but each handles different input shapes (snapshot.type vs mark.type.name vs mark.type). A single function with a type-name extractor would eliminate the duplication:

  const getTypeName = (m) => m.type?.name ?? m.type;
  const marksMatch = (a, b, exact = true) => {
    if (getTypeName(a) !== getTypeName(b)) return false;
    return !exact || attrsExactlyMatch(a.attrs || {}, b.attrs || {});
  };

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

good idea. updated.

const marks = [];
const seen = new Set();

doc.nodesBetween(from, to, (node) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Both iterate doc.nodesBetween, check node.isInline, then deduplicate marks with mark.type.name + separator + JSON.stringify(mark.attrs) into a Set.

The unsetAllMarks.js version uses \0 separator, this one uses :.

Not blocking since they're in different domains, but if more consumers appear this should be a shared utility in core/helpers/.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok makes sense will keep an eye out for this pattern

type: mark.type.name,
attrs: { ...mark.attrs },
}));
before = liveMarks
Copy link
Contributor

Choose a reason for hiding this comment

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

liveMarks is fetched from newTr.doc (which already includes the addMark from line 42), so the new mark attrs are captured in the snapshot.

Then track-change marks are filtered out. This means the before snapshot already reflects the incoming style change, not the state before it. Was this tested for the "add formatting on top of existing tracked formatting" scenario?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Was this tested for the "add formatting on top of existing tracked formatting" scenario?

Can you elaborate on this one? do we have a VRT for that scenario that would fail in the test suite?

let typeOnlyMatch = null;
const shouldFallbackToTypeOnly = !snapshot?.attrs || Object.keys(snapshot.attrs).length === 0;

doc.nodesBetween(from, to, (node) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

return false in nodesBetween only stops descent, not iteration. After exactMatch is found, sibling nodes are still visited needlessly.

Should we add if (exactMatch) return at the top of the callback?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes good idea. Added a short circuit return in the function.

…lor-change-suggestion-not-revertible-on' into colep/sd-1770-tracked-changes-color-change-suggestion-not-revertible-on
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants