All versions
0.0.4
Release
SLib 0.0.43 months ago 47
Compatibility
Minecraft: Java Edition
1.20.1–1.20.6
Platform
Forge
Supported environments
Client and server
Changes
Patch 0.0.4
Fixes
- Fixed Slib UI labels following ModernUI (or similar) preferred-typeface overrides of
minecraft:default. Widgets no longer draw raw strings through the vanilla default face alone. All library text goes throughFontsandStyle.withFont(slib:*)so Slib FontSets stay on screen even when ModernUI replaces the global default. - Fixed width and layout measurement still calling vanilla
Font.width(String)/ default face while the actual glyphs were drawn with Slib fonts. SearchBar, Sidebar, TabBar, Slider, Stepper, Tooltip and WarningScreen wrap now measure with the sameFontsrole used for draw, so hit-boxes and line breaks match the painted text. - Fixed
nullStyleconstructors onToggle,SearchBar,Dropdown,SliderandStepper. The old pattern didthis.style = style == null ? Style.GOLD : styleand then still calledstyle.animationMs()/style.easing()/style.togglePillRatio()on the original parameter, sonew Widget(null, ...)NPEd after the “null-safe” assignment. Constructors resolve a localStyle sfirst and use only that. - Fixed
Dropdownlist selection. Open options are drawn below the closed widget height, but selection went throughsuper.mouseClicked→isMouseOveron the closed bounds only. Clicks on rows never calledindexAt/setSelected.mouseClickednow handles header, list and outside-click close explicitly. - Fixed
Dropdownhit-testing during open animation using the full list height while the scissor only showedopenProgressof the list, so invisible rows were already clickable. Hit height now tracks animated visible height. - Fixed
Dropdown.setSelectedignoring a click on the already-selected row (menu stayed open). Selecting any list row always closes the menu;onChangestill fires only when the index changes. - Fixed
Dropdownopen animation fallback:valueOr(id, newOpen ? 1 : 0)inverted the start value so open/close could snap. Fallback is nownewOpen ? 0 : 1. - Fixed
Dropdownchevron drawinggfx.fill(px, py, px, py, col)(zero-area rect → invisible). Chevron is drawn as a real V made of 1-px spans. - Fixed
Dropdownlist border using an enlarged filled rounded box under the panel instead of a true outline. List border usesRounded.drawOutline. - Fixed SDF rounded shader never loading.
rounded.jsonlisted stages asslib:shaders/core/rounded.{vsh,fsh}, which Forge resolves underassets/slib/shaders/core/again (double path + double extension). Stages are nowslib:rounded/slib:rounded. Without this, every rounded draw fell back to flat quads. - Fixed SDF outline uniforms missing from
rounded.json(OutlineThickness,IsOutline), so even with a working shader binds no-oped and outlines were filled enlarged boxes. Both uniforms are declared with defaults. - Fixed outline quad pad too small for feather AA: pad is now
halfThickness + edge + 1/pixelScaleso soft outline edges are not clipped. - Fixed
PlayerPreviewModel.dispose()treating everyRemotePlayeras synthetic (synthetic = player instanceof RemotePlayer). Online other players areRemotePlayers; calling dispose afterbyUuid(onlineId)discarded them from the client level. Ownership is now an explicitownedSyntheticflag set only when Slib constructs a local fake for preview. - Fixed
PlayerPreviewModelauto-rotate usingMinecraft.getFrameTime()(partial tick in ~[0,1]) as if it were seconds, so degrees-per-second was wrong. Rotation advances from wall-clock delta between frames, clamped to avoid spikes. - Fixed
PlayerPreviewModeldividing GUI coordinates byguiScaleand ignoring the parentPoseStack, which mis-placed the model at GUI scale 2×/3×/4× and under scroll transforms. Render uses GUI-space translation on the passed pose and restoresRenderSystem.setShaderColor(1,1,1,1)in a finally block after applying ARGB alpha. - Fixed the config API:
ConfigSection.bool/integer/decimal/stringcalledbuilder.define(...)but threw away the returnedForgeConfigSpec.ConfigValue.ConfigKeyonly stored path/default/comment with no way to read the live value. Keys now hold theConfigValueand exposeget(),set()andvalue(). - Fixed
SnapshotGuardcounting every non-compound/list/string tag as a flat 16 bytes.TAG_BYTE_ARRAY/TAG_INT_ARRAY/TAG_LONG_ARRAYnow size by real payload length (length,4 * ints,8 * longs) so multi‑MB array bombs cannot pass the default max-bytes limit. - Fixed
Boost.scheduleForPlayerusing the same map key as globalschedule(module::scheduleId). Player B scheduling the same id cancelled player A; logout of A cancelled B’s future. Player schedules usemodule::scheduleId::playerIdand are cancelled only for that player. - Fixed SBS
FrameMonitorfeedingmc.getFrameTime() * 1e9as frame duration. Partial tick is not milliseconds; samples looked like multi-hundred-ms frames and tripped “frame degraded” every tick. Frame time is measured as wall-clock nanos between client ticks, with a 2s throttle on the degrade warn log. - Fixed network protocol version not being enforced.
NetworkRegistry.newSimpleChanneluseds -> trueon both sides, soPROTOCOL_VERSIONwas decorative. Acceptors are nowPROTOCOL_VERSION::equalsso mismatched Slib builds fail the channel handshake instead of desyncing packets. - Fixed
SearchBarlong queries: text always started attextXand scissor only clipped, so the caret could leave the visible field with no way to see the end of the string. HorizontalscrollXtracks the caret inside the text max width. - Fixed
SearchBarclick-to-caret ignoringscrollX, so after scrolling the caret jumped to the wrong character. Hit-test usesmouseX - textX + scrollX. - Fixed
SearchBarpaste accepting newlines and control characters (broke single-line layout). Insert strips\n/\r/\tand other controls. - Fixed hover animations on SearchBar, Toggle, Dropdown and Sidebar re-registering
Animation.of(from, target, fullDuration)every render frame whiletarget != value(), which resetelapsedMsand prevented a real ease. They now match Slider: re-register only when the target changes or the current anim is finished/missing. - Fixed
Fonts.forceapplyingwithFontonly to the rootComponent. Siblings kept their own styles and fell back to ModernUI/default. Force rebuilds viaplainCopyand recursively forces each sibling, while preserving protected non-default / non-slib fonts (icon fonts). - Fixed
RippleEffectdouble-easing:Animationalready easedvalue(), thenrendercalledstyle.easing().apply(t)again, so radius/alpha overshot. Render maps the animation value linearly to radius and alpha. - Fixed
Toast.dismissleaving the slide-in animation running. Progress wasslideIn * slideOut, so a mid-entry dismiss could jump opacity/offset. Dismiss cancels slide-in, then registers slide-out from the current held value. - Fixed
Toast/Tooltipconcurrent mutation: plainArrayList/ArrayDequethrew CME whendismiss()ran duringtick/renderAll. Queues areCopyOnWriteArrayListwith snapshot ticks. - Fixed
Modal.isOpen/Drawer.isOpen/trapsClickreading animation value only. Right afteropen(),elapsedMs == 0sovalue() == 0and input leaked for a tick. ExplicitopenIntent(and per-drawer intent keys) gates interaction immediately. - Fixed
Drawer.toggleusingvalue > 0.5mid-animation (could flip the wrong way). Toggle now opens if closed and closes if open by intent. - Fixed
KeyboardShortcut.handleiterating a liveHashMap(CME if handlers unbind) and firing on release/repeat. Handler list is snapshotted; onlyACTION_PRESSis handled. - Fixed
Binding.setre-entrancy: a listener callingsetnestedfire()and double-notified. Nested sets whilefiringare ignored for notification. - Fixed
AnimationTickeradvancing Toast/Tooltip/Ripple with a fixed50mswhileAnimationRunnerused wall-clock delta, so lifetime and slide animations drifted under lag. UX ticks useAnimationRunner.lastDeltaMs(). - Fixed
TabBar.Justify.DISTRIBUTEbeing a no-op (same layout as START). Leftover width is distributed into gaps between tabs. - Fixed
TabBar.indexAtaccepting clicks anywhere in the fullSafeAreaheight, not only the tab strip. Hit test usesTheme.TAB_HEIGHT. - Fixed
ProgressBarshowValuedoing nothing (value text always drawn whenvalueText != null). Value text is drawn only whenshowValueis true;measureHeight(boolean hasLabel)accounts for an under-bar label. - Fixed
ItemIcon.renderCountIntoplacing the count near the top of the icon becauseFonts.lineHeight()(≥12) madesize - lineHeight≈ 0 on 16px icons. Count is anchored to the bottom-right with a fixed 8px band. - Fixed
Card.bodyOffsetY()always returning 52 even without a subtitle (wasted space / divider mismatch).bodyOffsetY(boolean hasSubtitle)returns 42 / 54. - Fixed
Sidebarhover animation ids keyed onidentityHashCode(gfx), which is not stable across frames. Ids key on the items array instance instead. - Fixed
ScrollContainerscissor begin/end with no exception safety (imbalance if content throws).renderContent(Runnable)pushes clip and always pops infinally. - Fixed
WarningScreenblur/resource dispose only on ESConClose(). The dismiss button calledsetScreenand skipped cleanup. Dispose runs once fromclose()andremoved(). - Fixed
Permissions.isOponly checkingplayerList.getOps().get(profile) != null. Singleplayer hosts and permission-level ops often have no ops-map entry. Op checks also accepthasPermissions(2). - Fixed
Render.runOnRenderThreadnever being drained: tasks queued forever and never ran.SbsClientdrains the queue onRenderGuiEvent.Postwith the GUIPoseStack. - Fixed
NodeRuntime.hasCyclereturning true when DFS depth exceededmaxDepth, so a deep acyclic graph was reported as cyclic andexecutefailed with “graph has a cycle”. Depth overflow is no longer treated as a cycle; only stack re-entry is. - Fixed
Tooltip.rendercallingmc.getWindow()without a null guard whenMinecraft.getInstance()is null. - Fixed
WarningScreenoutline scissorpushwithouttry/finally, so an exception mid-draw left GL scissor and the clip stack unbalanced. - Fixed FontSets with a single TTF and no fallback: missing codepoints became tofu. All EN/RU FontSets reference
minecraft:include/spaceandminecraft:defaultafter the brand TTF so uncovered glyphs still resolve.
Improvements
- Added
en.antinimus.slib.api.ui.Fontsas the single text entry point for the library: roles (TITLE,BUTTON,BODY,BOLD,LABEL,MUTED,REGULAR),literal/force/apply,draw/drawCentered,width,lineHeight, EN/RU pack selection viaoptions.languageCode,isModernUiLoaded,describeActiveTypefaces. - Locale font packs: EN titles/buttons use Plus Jakarta Sans SemiBold; body uses Instrument Sans SemiBold. RU titles/buttons use Manrope SemiBold; body uses Noto Sans Regular. Packs switch live for
Fonts.draw; source TTFs live underassets/slib/fonts/en|ruwith OFL licenses; runtime FontSets underassets/slib/font/en_*andru_*. - Added
FontWidgetsfor opt-in vanilla button labeling only (button/create/buttonLabel/setMessage/restyleButton). Slib does not auto-rewrite everyAbstractButtonon every screen (that broke ModernUI icons). Consumers wire their own buttons throughFontWidgetsor keep drawing withFonts. - Hardened multipayer-facing paths in the same pass: real NBT array size accounting, per-player schedule isolation, protocol version equality, and render-queue drain.
UI / Sync
Fontsis wired throughRoundedButton,SearchBar,Dropdown,Slider,Stepper,TabBar,Sidebar,Card,SectionHeader,StatRow,ProgressBar,ItemIcon,Toast,Tooltip,Modaland SBSWarningScreen.- Dropdown open interaction, TabBar distribute, SDF outline path, and modal/drawer input trapping are aligned with the rest of the UI engine after the audit pass.
- No intentional public API break for normal consumers. Prefer
dependencies.slibrange[0.0.1, 0.1.0)or pin0.0.4for this patch.ConfigKeynow exposes live config access (get/set/value) which the previous descriptor-only type lacked. UseFontWidgetswhen creating vanillaButtons that should use Slib typefaces.
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:CfkGe24d:iDFWYQbe"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:CfkGe24d:iDFWYQbe"
}

