Skip to content

Sync Dev#999

Merged
boomzero merged 34 commits into
masterfrom
dev
Jul 27, 2026
Merged

Sync Dev#999
boomzero merged 34 commits into
masterfrom
dev

Conversation

@boomzero

@boomzero boomzero commented Jul 27, 2026

Copy link
Copy Markdown
Member

What does this PR aim to accomplish?:

Promotes dev to master, releasing 3.5.4 as a production release (master is currently on 3.5.0).

This rolls up four prereleases:

Version PR Change
3.5.1 #986 Fix Violentmonkey SyntaxError — wrap async init block in an IIFE
3.5.2 #989 Use Monaco Editor in submitpage.php, with code saving on edit
3.5.3 #992 Eliminate dark-mode flash (FOUC/CLS) in MonochromeUI / NewBootstrap
3.5.4 #997 Pin Monaco to 0.52.2 to stop intermittent editor load failures

How does this PR accomplish the above?:

A straight merge of dev into master. Merging triggers UpdateToRelease.js, which flips "Prerelease": false on the 3.5.4 entry in Update.json and cuts the production release.

Verification done before opening:

  • Merge is conflict-free — confirmed with git merge-tree --write-tree origin/master origin/dev (exit 0). 31 commits ahead.
  • All checks green on dev HEAD (0da6227): CodeQL, Prerelease, Sync.
  • Versions are in sync at 3.5.4 across package.json, XMOJ.user.js @version, and the Update.json key.
  • The master-only changes to ads.txt and index.html are preserved — dev has not touched either file since the merge base (ec4cadc), so the merge takes master's side and does not resurrect the removed Google Ads script.

Not included in this release (both target dev, CI-only, no userscript impact): #995 actions/setup-node 4→7, #996 actions/labeler 4→7.


By submitting this pull request, I confirm the following:

  1. 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.
  2. I have commented on my proposed changes within the code.
  3. I have tested my changes.
  4. I am willing to help maintain this change if there are issues with it later.
  5. It is compatible with the GNU General Public License v3.0
  6. I have squashed any insignificant commits. (git rebase)
  7. I have checked that another pull request for this purpose does not exist.
  8. I have considered and confirmed that this submission will be valuable to others.
  9. I accept that this submission may not be used, and the pull request can be closed at the will of the maintainer.
  10. I give this submission freely and claim no ownership to its content.
  11. I have verified that my changes work correctly in both the new UI and the old/classic UI.

Note on items 3 and 11: these were verified manually by the maintainer, not by automation — this repo has no automated test suite. The browser checks (Monaco loading on submitpage.php, dark-mode behaviour) were done by hand.


  • I have read the above and my PR is ready for review. Check this box to confirm

🤖 Generated with Claude Code


Summary by cubic

Release 3.5.4 by merging the latest dev into master. Ships the Monaco-based submit editor, removes dark-mode flash on the new UI, and fixes intermittent editor load errors.

  • New Features

    • Replace the submit editor on submitpage.php with monaco-editor, including auto-save while editing.
  • Bug Fixes

    • Fix Violentmonkey top-level await error by wrapping async init in an IIFE.
    • Remove dark-mode FOUC/CLS in MonochromeUI/NewBootstrap with early theme/CSS injection.
    • Pin monaco-editor to 0.52.2 and make the loader single-flight to prevent random load failures.

Written for commit 0da6227. Summary will update on new commits.

Review in cubic

Copilot AI and others added 30 commits May 30, 2026 03:37
Signed-off-by: Shan Wenxiao <seanoj_noreply@yeah.net>
…-in-xmoj-script

Fix Violentmonkey SyntaxError: wrap async init block in IIFE
Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4 to 5.
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](actions/dependency-review-action@v4...v5)

---
updated-dependencies:
- dependency-name: actions/dependency-review-action
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
…s/dev/actions/dependency-review-action-5

Bump actions/dependency-review-action from 4 to 5
…nt (#989)

* Use Monaco Editor and save code in submitpage.php when edit the content.

* 3.5.2

* Update version info to 3.5.2

* Fix AI-detected bugs: Monaco timeout, MergeView language, XSS, fallback editor, package.json version

* Fix code review issues: MergeView language expression, em space comment, fallback interface stubs

* Bump version from 3.5.1 to 3.5.2

Signed-off-by: Shan Wenxiao <seanoj_noreply@yeah.net>

* Update time and description of 3.5.2

* Fix constant condition: replace dead typeof check with fallback '80px' in setSize

* Update time and description of 3.5.2

* Update XMOJ.user.js

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Signed-off-by: Shan Wenxiao <seanoj_noreply@yeah.net>

* Update time and description of 3.5.2

* Fix 3 AI review bugs: ignoreWhitespace newlines, setSize replay, fallback persistence

* Update time and description of 3.5.2

* Remove rounded corners style from Monaco editor

Removed custom style for rounded corners in the Monaco editor.

Signed-off-by: zsTree <233546082+def-WA2025@users.noreply.github.com>

* Update time and description of 3.5.2

* Update time and description of 3.5.2

* Update XMOJ.user.js

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Signed-off-by: zsTree <233546082+def-WA2025@users.noreply.github.com>

* Update time and description of 3.5.2

* Increase Monaco editor and fallback textarea height

Updated the size of the Monaco editor and fallback textarea to 550px.

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Fix typo in FileSaver.js URL

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Enhance localStorage handling and editor dimensions

Added fallback key logic for localStorage retrieval and adjusted Monaco editor height.

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Fix condition for checking saved localStorage value

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Implement auto-fit for Monaco editor and loading UI

Enhance Monaco editor integration with auto-fit feature and loading placeholder

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Refactor options handling for improved clarity

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Update XMOJ.user.js

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Define options in createMonacoEditor function

Ensure options is always defined in createMonacoEditor.

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Refactor options handling in Monaco editor functions

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Enhance dispose function for better resource management

Refactor dispose function to include editor cleanup and model disposal.

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Refactor Monaco editor initialization and disposal

Removed the creation of a custom style element for Monaco editor. Added error handling for disposing transient Monaco editors in various functions.

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Simplify error message editor disposal logic

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Modify applyAutoFit function to accept editor parameter

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Update theme from 'darcula' to 'vs-dark' & Use Cloudflare cdnjs

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Simplify submission key retrieval logic

Removed unused localStorage fallback logic for submission key.

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

* Improve error handling and user feedback

Enhanced error handling by adding console error logging and user alerts in DebugMode for various operations.

Signed-off-by: zsTree <wa2025666@gmail.com>

* Update time and description of 3.5.2

---------

Signed-off-by: Shan Wenxiao <seanoj_noreply@yeah.net>
Signed-off-by: zsTree <233546082+def-WA2025@users.noreply.github.com>
Signed-off-by: zsTree <wa2025666@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Shan Wenxiao <seanoj_noreply@yeah.net>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Bootstrap 5.3.3 CSS was previously fetched from CDN at document-idle,
causing a flash of the old light-themed page before dark mode took effect.

Changes:
- Add @run-at document-start so an early shim can run before first paint
- Add @resource BootstrapCSS + @require Bootstrap JS bundle so both are
  cached by the userscript manager (no CDN round-trip on page load)
- Add @grant GM_getResourceText
- Early synchronous IIFE: reads saved theme/settings from localStorage,
  sets data-bs-theme on <html> immediately, injects Bootstrap CSS and the
  MonochromeUI or default skin CSS before the browser paints anything, and
  uses a MutationObserver to drop the page's own old Bootstrap/theme link
  tags before they are ever fetched
- Extract MonochromeSkinCSS and NewBootstrapSkinCSS to top-level consts so
  they are available both to the early block and to the late Style element
  (which still applies them at document-end for any dynamic page additions)
- Remove Bootstrap CSS + JS from the runtime CDN resources array
- Remove the now-redundant post-load link-removal loop, data-bs-theme
  assignment, and earlyStyle CSS-variable injection from the NewBootstrap block
- Add DOMContentLoaded wait at the start of the main async IIFE so all
  existing DOM-dependent code continues to work correctly under document-start

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
The early block now owns the MonochromeSkinCSS / NewBootstrapSkinCSS
injection. The Style element created at document-end is kept (other
code appends to it further down the IIFE), but it no longer re-sets
the skin CSS that was already applied synchronously at document-start.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
If GM_getResourceText("BootstrapCSS") returns null (the @resource has
not been downloaded yet on first install or update), the early block was
still setting up the MutationObserver that blocked the page's original
Bootstrap CSS — leaving the page with no Bootstrap CSS at all.

Fix: bail out of the early block before the observer is set up when the
resource is unavailable, so the page's original stylesheets load as a
fallback (flash still happens, but the page renders correctly).

Also add a CDN fallback entry to the runtime resources array: on the
rare load where the @resource wasn't ready, Bootstrap 5.3.3 CSS is
fetched from CDN instead, maintaining the same behaviour as before this
PR. Subsequent page loads use the cached @resource and no CDN request.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
The browser preload scanner starts fetching old Bootstrap/theme
stylesheets before the document-start MutationObserver can remove
the link elements. Once fetched, those stylesheets are applied and
override our early-injected Bootstrap 5 + skin CSS — causing the new
UI to render for a moment and then immediately revert.

Restoring the IIFE-time link removal loop fixes this: removing a
<link> from the DOM un-applies its stylesheet from the CSSOM
immediately, regardless of whether the preload scanner had already
fetched it. The two layers now work together:

  1. MutationObserver (document-start): best-effort early block that
     prevents old CSS from ever loading when the timing works out.
  2. Link removal loop (document-idle): guaranteed cleanup that
     removes any preload-scanner-fetched stylesheets after the DOM
     is ready, eliminating the revert.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
The visible flash was caused by old Bootstrap 3 CSS sitting in the CSSOM
from the moment DOMContentLoaded fired until the IIFE's link removal loop
ran — a window that includes a full loginpage.php network round-trip.

Move the definitive link removal into the early block's DOMContentLoaded
handler so it fires at the earliest possible moment after the DOM is
ready, before any async work in the main IIFE. The observer is also
disconnected at the same time.

The IIFE-level link removal loop is kept as a belt-and-suspenders
fallback for edge cases (e.g. early block disabled due to cache miss).

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
No JavaScript approach can reliably prevent the browser preload scanner
from fetching and visibly applying old Bootstrap CSS before DOMContentLoaded.
Attempting to evict stylesheets at DOMContentLoaded still leaves a window
where old CSS is visible, causing CLS.

Instead, hide the page immediately at document-start with
  html { opacity: 0 !important; }
so the user never sees the intermediate broken state. The page is revealed
in the DOMContentLoaded handler, after old stylesheet links have been
removed and the correct Bootstrap 5 + skin CSS is already in place.
A 3-second safety-net timeout ensures the page is always revealed even
if something unexpected prevents DOMContentLoaded from firing.

This is the standard FOUC prevention pattern. The hidden window is just
HTML parse time (~50-150 ms for a server-rendered page), after which the
user sees the correct final state with zero CLS.

The hide style is only injected when the @resource is available
(_earlyBootstrapInjected = true), so the fallback path (cache miss on
first install) continues to show the page immediately.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
The DOMContentLoaded listener registered inside the early IIFE's
sandbox context was not firing reliably, causing the opacity:0 FOUC
prevention style to only be removed by the 3-second safety-net
timeout — resulting in a mandatory 3 s blank page on every load.

Fix: expose the FOUC style element and MutationObserver via top-level
variables (_foucStyle, _earlyObs) and perform the reveal + link
cleanup at the top of the main async IIFE, immediately after its
DOMContentLoaded wait. The IIFE's own DOMContentLoaded mechanism is
proven to work, so the reveal is now reliable.

The early block still hides the page at document-start and arms the
MutationObserver for best-effort link interception; the IIFE takes
care of teardown and the final CSSOM cleanup at DOMContentLoaded time,
before the loginpage.php fetch begins.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
When GM_getResourceText("BootstrapCSS") returns null (first install or
@resource not yet cached), the early block exits before injecting
MonochromeSkinCSS/NewBootstrapSkinCSS. The Style element in the IIFE
now injects the skin CSS (plus AddAnimation/AddColorText overrides)
whenever _earlyBootstrapInjected is false, so the skin is always
applied regardless of cache state.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
fix: eliminate dark-mode flash in MonochromeUI/NewBootstrap
Monaco 0.53.0 declares its minified helper variables with `var` at the top
level of every chunk file. AMD chunks are plain scripts rather than modules,
so those helpers land on `window`: a live load leaks h, m, r, o, p, s, i, f,
u, w, M, y and C. Different chunks give the same name different meanings --
`m` is the field-setter helper in language/css/monaco.contribution.js but is
Object.defineProperty in yaml.contribution.6f43d486.js -- so whichever chunk
script evaluates last wins.

When yaml wins, the css chunk's `r(this, "_options")` call resolves to
Object.defineProperty(this, "_options", undefined) and throws
"Property description must be an object: undefined". The editor.main callback
then never fires, ensureMonaco() times out after 30s, and submitpage.php falls
back to the bare textarea. Evaluation order depends on network timing, which
is why it only reproduces on some page loads.

This is upstream microsoft/monaco-editor#5015, fixed in 0.54.0. cdnjs has no
stable release newer than 0.53.0, and the rest of the script sources from
cdnjs, so pin back to 0.52.2 -- it ships as a single bundle and leaks no
globals at all.

Also make ensureMonaco() single-flight. Overlapping callers (submit editor,
merge view, freopen snippets) each appended their own loader.js, and the
second loader resets the AMD registry while the first is still resolving
modules. A failed load clears the cached promise so later calls can retry.

Closes #991

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix: pin Monaco to 0.52.2 to stop intermittent editor load failures

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @boomzero, your pull request is larger than the review limit of 150000 diff characters

@hendragon-bot hendragon-bot Bot added GitHub-related user-script This issue or pull request is related to the main user script labels Jul 27, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 27, 2026

Copy link
Copy Markdown

Deploying xmoj-script-dev-channel with  Cloudflare Pages  Cloudflare Pages

Latest commit: a8e9df1
Status: ✅  Deploy successful!
Preview URL: https://e95b0306.xmoj-script-dev-channel.pages.dev

View logs

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

Copy link
Copy Markdown

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: 0da6227794

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Update.json
},
"3.5.4": {
"UpdateDate": 1785114353055,
"Prerelease": true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Mark the shipped release as non-prerelease

Because the production updater scans Update.json for the newest entry whose Prerelease is false, leaving the new 3.5.4 entry marked as a prerelease means non-debug users on 3.5.0 will still consider 3.5.0 the latest production version and won't be prompted to install this release. For a master release, this entry needs to be published as a release (or accompanied by a newer non-prerelease entry) before the site is deployed.

Useful? React with 👍 / 👎.

Comment thread XMOJ.user.js
const done = () => { if (check) clearInterval(check); clearTimeout(timeout); resolve(); };
const timeout = setTimeout(() => { if (check) clearInterval(check); reject(new Error('Monaco load timeout')); }, 30000);
try {
require(['vs/editor/editor.main'], function() { done(); });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Resolve Monaco from the page context

When this runs in a sandboxed userscript context (for example Tampermonkey with grants, or Violentmonkey when it falls back to content mode), the loader script appended above creates require/monaco on the page window, but this unqualified require(...) lookup happens in the userscript sandbox. In that environment ensureMonaco() times out, so submitpage waits 30 seconds before falling back and the new CodeMirror(...) read-only views just remove their loading marker without rendering code; load the loader via @require/sandbox page mode or reference the page global explicitly.

Useful? React with 👍 / 👎.

@boomzero

boomzero commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

Re: the red CodeQL check

The failing check is the github-advanced-security CodeQL check-run, not the analysis itself — the Analyze (javascript-typescript) job and the CodeQL Actions job both passed. The red X is purely alert attribution, and it is a false attribution.

All 4 flagged alerts are pre-existing on master. They are the same alert IDs, the same rule, and the same source lines — only the line numbers moved, because the Monaco work in #989 shifted roughly 4,000 lines in XMOJ.user.js:

Alert Rule master line dev line Code
#24 js/xss-through-dom 1881 2491 location.href = "...userinfo.php?user=" + CurrentUsername
#25 js/xss-through-dom 2769 3379 #simform.outerHTML
#26 js/xss-through-dom 3188 3798 AutoCheatButton.innerHTML = "正在提交 " + PID
#27 js/xss-through-dom 3752 4506 body > div > div.innerHTML

All four were first raised on 2024-08-07 and are open on master today. CodeQL flagged the caveat itself in its check summary:

Alerts not introduced by this pull request might have been detected because the code changes were too large.

This PR neither introduces nor worsens them; it only renumbers their lines.

Disposition

All four have now been reviewed and dismissed as false positives, with per-alert reasoning recorded on each:

Not blocking

master branch protection defines no required status checks (required_status_checks.contexts: []), so this check does not gate the merge. The BLOCKED merge state is from required_approving_review_count: 1 — this PR needs one approving review.

@boomzero
boomzero requested a review from PythonSmall-Q July 27, 2026 02:24
@boomzero
boomzero enabled auto-merge July 27, 2026 02:24
@boomzero
boomzero disabled auto-merge July 27, 2026 05:42
@boomzero
boomzero merged commit 1aa1b02 into master Jul 27, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

GitHub-related size/XXL user-script This issue or pull request is related to the main user script

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants