promotional bannermobile promotional banner

ApricityUI

Build Minecraft UI with HTML + CSS + JS, follows Web standards as closely as possible.
Back to Files

apricityui-neoforge-26.3 1.3.0

File nameApricityUI-neoforge-26.3-1.3.0.jar
Uploader
Qihuang02Qihuang02
Uploaded
Nov 2, 2026
Downloads
8
Size
2.8 MB
Mod Loaders
NeoForge
File ID
9403776
Type
R
Release
Supported game versions
  • 26.3

Curse Maven Snippet

NeoForge

implementation "curse.maven:apricityui-1470115:9403776"

Learn more about Curse Maven

What's new

Added

  • ApricityEvents.startup gives KubeJS startup_scripts the startup loading screen: the event fires from the loading screen's first frame (by then the startup scripts are loaded) and carries the phase's StartupContext, so a script calls event.context.loadProject('loading') exactly like a Java hook. Annotations are a Java concept, so this event is how scripts reach the same API; the browser starts when the event installs its page, so a script's page is the first one shown too, about half a second later than a @StartupHook page. KubeJS 1.18.2 predates event groups and keeps the ApricityUI.startup() accessor.
  • @StartupHook: a static method annotated with it runs once at the start of client loading, with a StartupContext that shows a page in the startup loading screen (showPage/showPageUrl/loadProject/eval/hide) and reads what the client knows that early ¡ª raster size, physical and scaled window size, GUI scale, game directory, elapsed phase time, and whether the WebView backend is available. loadProject takes a folder project whose entry point is index.html: the instance is checked first (relative to the page root, relative to the game directory, or absolute) and counts only when the folder exists, is not empty and holds an index.html; otherwise the same folder is taken from the jars or resource packs and extracted whole under apricity/ at the corresponding place (bounded, existing files never overwritten), because the browser cannot read a jar. A <instance>/apricity/startup/ project is picked up with no code at all. Local pages are loaded over file: with --allow-file-access-from-files, so fetch and ES modules behave as they would over http. Hooks are discovered through the loader's annotation scanner (any mod, any package), run in priority order with stable ties, and an exception in one is logged without failing the launch. The startup page's browser is started right after the last hook returns, so the page a hook configures is the page it opens. -Dapricityui.startupOverlay=false disables the whole loading screen.

Performance

  • <iframe> capture requests now back off while the page publishes nothing. A capture costs 19¨C56 ms of browser-side encode and IPC whether or not anything changed, so a still page was paying a full round trip at the element's draw rate just to discover it was still: after 0.5 s without a publish the interval is multiplied by 4 (capped at 125 ms), after 2 s by 8 (capped at 250 ms), and any publish or input snaps it back to the draw-matched base interval. Measured on a still page: 62 ¡ú 8 captures/s, with animated pages unchanged at 61¨C62 captures/s. Measured alongside: the WIC decode on our side is 0¨C5 ms even at 1080p, so essentially all of a capture's cost is inside WebView2 ¡ª which is why the raw-pixel path was re-investigated (Windows.Graphics.Capture still returns a single blank frame with the window on screen and topmost, and the composition controller exposes no readback; see the WebView chapter).
  • <iframe> now ships tighter dirty rectangles and uploads them straight from shared memory. The 32¡Á32 tile grid is a detection grid only: each merged box is shrunk to the rows and columns that actually differ before it is published, so moving one 40¡Á40 box costs ~6.4K pixels per frame instead of the ~24.6K the tile-aligned boxes carried. The BGRA¡úRGBA flip moved out of the per-capture whole-canvas pass and into the copy of the rectangles being sent (the reference canvas keeps the source's byte order), and the render thread no longer walks pixels through int[] into a NativeImage: each rectangle is handed to the texture as a direct buffer through the new AuiRenderService.uploadTexturePixels (glTexSubImage2D on 1.18.2¨C1.21.1, CommandEncoder.writeToTexture on 26.1), which also removes the per-pixel setPixelRGBA/setPixelABGR loop for a repainting page.

Changed

  • <iframe> is rasterised at its element's device resolution again, and both ceilings now come from the display instead of a constant: the area limit is the window's framebuffer area (2560¡Á1440 on a 2K screen, 3840¡Á2160 on a 4K one) and the per-edge limit is the longer framebuffer edge clamped to 4096¨C8192, so only an element larger than the whole window is downscaled. The viewport fallback used when the monitor mode cannot be queried is the window framebuffer too, instead of a hardcoded 1920¡Á1080 that laid the page out at ~1080p and scaled it up on bigger screens. The drawn quad is also snapped to whole device pixels, because a half-pixel offset makes the sampler read every texel halfway between two texels and softens a 1:1 texture the same way an upscale does. Capture cost is unchanged per pixel (¡Ö20 ms + 28 ms per megapixel) ¡ª capture-scale is the knob for trading sharpness back, and Iframe.status() keeps reporting raster= / raster capped by area so the actual resolution is visible.
  • <canvas> now rasterises at its content box's device resolution instead of at CSS-pixel resolution. The raster is rebuilt whenever the box or the display scale changes, and existing content is rescaled rather than dropped; the 2D context gets the device scale as a base transform, so scripts keep drawing in CSS pixels at the same coordinates. The drawn quad is snapped to the device pixel grid and sized from the raster, so a 1:1 texture is never sampled at a half-texel offset. width/height attributes are now the design (intrinsic) size, canvas.width/height stays in CSS px, and the pixel-level APIs (getBitmapWidth/getBitmapHeight, getImageData/putImageData, createImageBitmap(canvas)) use raster pixels.

Fixed

  • window.devicePixelRatio reported the Minecraft GUI scale, which is a different number from "physical pixels per document CSS pixel" whenever the display scaling differs from the GUI scale ¡ª so the standard canvas.width = rect.width * devicePixelRatio recipe produced a backing store that was too large or too small and the canvas was resampled either way. It now returns the document viewport's device scale (scissorScale, the value iframes already use for their rasters) and falls back to 1 when no document is active.
  • The client-only /aui worldwindow debug window now has a prominent fixed Close button (revealed only by the client command) that destroys the WorldWindow and its Document. World windows are also cleared automatically when the client leaves the level or returns to the title screen, so a debug window no longer survives a save change.
  • <iframe> froze parts of the picture during large repaints. When the remaining payload budget of a packet could not hold one row of the next dirty rectangle, the packer added the row anyway: payloadBytes then exceeded the 256 KB cap the reader validates, the reader called the packet implausible and dropped it whole ¡ª up to a thousand rectangles at once ¡ª while the host had already advanced the reference canvas it diffs against, so that area stayed at an older frame until it changed again. The packer now stops the packet when no row fits, and the reader tolerates one extra canvas row of payload so a marginally oversized packet is applied instead of discarded. Iframe.status() / the frame-timing HUD reports resync= when a packet is dropped this way.
  • Clicking an <iframe> no longer lets the outer document's text-selection handler move focus to BODY. Mouse input already reached the embedded page, but losing the framework focus prevented keyboard and character input from being forwarded to WebView2.
  • Resizing the game window no longer rebuilds the open AUI page. Screen.resize() calls init(); ApricityScreen and ApricityContainerScreen now reuse the live Document instead of removing and recreating it, so form values, scroll positions, and script state survive a resize. A new Document is still created on first open, after close, or when the old one was disposed.

This mod has no additional files