Fix ReplaceXM (小明 → 高老师) on the /web contest pages
What does this PR aim to accomplish?:
With "将网站中所有"小明"和"我"关键字替换为"高老师"…" (ReplaceXM) turned on, nothing was replaced on the /web contest pages (e.g. https://www.xmoj.tech/web/contest).
Cause: the /web app returns early into InitializeContestWebApp() and never reaches the body.innerHTML rewrite in main(). That is intentional, since rewriting the body would break Vue, but it left ReplaceXM with no /web implementation.
How does this PR accomplish the above?:
- New
ReplaceXMText()applies the same substitutions, in the same order, asmain()(including the 我们 / 自我 fix-ups). InitializeContestWebApp()gets aReplaceXM()that walks the text nodes of#appand updatesdocument.title. It runs at the end of everyEnhance()pass, so whatever Vue re-renders is replaced again through the existing MutationObserver. It only writes a node when its text actually changes, so it does not loop.pre,code,textarea,script,styleand the Monaco editor are skipped, so samples and code that users copy stay unchanged. (The legacy innerHTML rewrite does touch those.)- The legacy
main()path is unchanged.tests/account-settings.test.cjsslicesmain()out on its own, so it can't call the helper. - Added a unit test in
tests/contest-web.test.cjs: 我们 / 自我 are preserved and re-applying the replacement changes nothing.
Does this PR need a documentation update?:
No
By submitting this pull request, I confirm the following:
- I have read and understood the contributor's guide, as well as this entire template. I understand which branch to base my commits and Pull Requests against. (
devfor XMOJ-Script,masterfor everything else). - I have commented on my proposed changes within the code.
- I have tested my changes.
- I am willing to help maintain this change if there are issues with it later.
- It is compatible with the GNU General Public License v3.0
- I have squashed any insignificant commits. (
git rebase) - I have checked that another pull request for this purpose does not exist.
- I have considered and confirmed that this submission will be valuable to others.
- I accept that this submission may not be used, and the pull request can be closed at the will of the maintainer.
- I give this submission freely and claim no ownership to its content.
- I have verified that my changes work correctly in all four UI combinations:
- the script's monochrome UI on XMOJ's new UI (
/webpages) - the script's monochrome UI on XMOJ's old/classic pages
- the script's classic UI on XMOJ's new UI (
/webpages) - the script's classic UI on XMOJ's old/classic pages
- the script's monochrome UI on XMOJ's new UI (
[!WARNING]
Item 11 is not verified yet. node --test tests/*.test.cjs passes (100/100). I ran tests/userscript-harness.cjs with {"ReplaceXM":true,"MonochromeUI":false} and with {"ReplaceXM":true,"MonochromeUI":true}. Both runs had no script errors, but they were logged out: every page, /web included, redirected to /loginpage.php, so the new code never ran on a /web page. A maintainer needs to check /web/contest, a contest, a problem and the rank page with ReplaceXM on, in both MonochromeUI modes, while logged in.
- I have read the above and my PR is ready for review. Check this box to confirm
🤖 Generated with Claude Code
Summary by Sourcery
Extend ReplaceXM support to /web pages without disrupting Vue rendering.
Bug Fixes:
- Fix ReplaceXM substitutions on Vue-based /web pages, including contest pages, while preserving the intended handling of 我们 and 自我.
Enhancements:
- Apply replacements to rendered application text and document titles after each enhancement pass without altering code, samples, editor content, or other excluded elements.
Build:
- Bump the project and userscript versions to 4.0.6.
Tests:
- Add coverage verifying ReplaceXM substitutions remain stable when reapplied and preserve 我们 and 自我.
Chores:
- Record the 4.0.6 update metadata.
Summary by cubic
Fixes ReplaceXM on /web contest pages, where the Vue app bypassed the legacy page-wide rewrite. Text in #app and the document title now uses the same substitutions without disrupting Vue rendering.
- Reapplies replacements after Vue updates through the existing enhancement observer.
- Skips code, samples, textareas, scripts, styles, and Monaco editor content.
- Preserves
我们and自我and verifies repeated replacement is stable.