GUI Overhaul with Dark Mode - #1145
Conversation
|
Im not keen on adding 3rd party deps, and this PR is basically impossible to review. Please at least provide some guideance to what you have changed in the PR description, the video is great advertising but doesnt make all of the details clear. There is a huge amount of code here that we need to be able to review, test and maintain. Did you use AI tools to code this? There is nothing wrong inherently wrong if you did, we just need to take extra care when reviewing/testing it. |
|
I understand that you're not keen on 3rd party dependencies, if I added FlatLaf just for dark mode with no redesign, justifying the added dependency would've been much more difficult to do. I also understand that a big PR like this is very difficult to review. Under normal circumstances I would make smaller PRs to build up to this. I had a small window of free time to work on this so even though it isn’t ideal, I wanted to get as much done as possible and taking small steps takes time that I just don’t currently have for this. Even if this PR doesn't get merged, later on parts of it can be taken and implemented in smaller steps by other people in the community who also want to see this change, so I figured that no harm could come from making the PR anyways since it'll bring it to people's attention. And to answer your question, yes, I heavily used AI in an iterative manner to help make the majority of these changes, then manually reviewed and tested the results. |
|
Who's going to read the thousands lines of code produced by an LLM? |
| Path tempDir = null; | ||
| Path tempPath = null; | ||
|
|
||
| try { | ||
| tempDir = Files.createTempDirectory("fabric-loader-icon"); | ||
| tempPath = candidate.copyToDir(tempDir, true); | ||
| return readIconBytes(Collections.singletonList(tempPath), iconPath); | ||
| } catch (IOException | RuntimeException ignored) { | ||
| return null; | ||
| } finally { | ||
| if (tempPath != null) { | ||
| try { | ||
| Files.deleteIfExists(tempPath); | ||
| } catch (IOException ignored) { | ||
| // Ignore cleanup failure. | ||
| } | ||
| } | ||
|
|
||
| if (tempDir != null) { | ||
| try { | ||
| Files.deleteIfExists(tempDir); | ||
| } catch (IOException ignored) { | ||
| // Ignore cleanup failure. | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Why are we copying jars to get the icon? If we can't read it, we can't read it.
There was a problem hiding this comment.
This was done to get the icons of jar in jar mods, but you're right that it might be overkill.
So we all know the current GUI:

It isn't the most user friendly GUI and it doesn't support dark mode either. This PR overhauls the GUI and supports dark mode through setting the system property
fabric.loader.gui.darkModetotrue.Here's a demo of the GUI in both dark mode and the default light mode:
Final.GUI.Demo.mp4
GUI Overhaul with Dark Mode - Change Summary
This PR replaces the old raw-tree dependency error view with a structured, user-friendly Swing UI for mod resolution failures, while preserving the existing detailed/copyable error output for support and debugging.
High-level changes
breaksentriesno-nativesartifact.fabric.loader.gui.darkMode=true.Dependency and build changes
build.gradlecom.formdev:flatlaf:3.7.1:no-natives.net.fabricmc.loader.impl.lib.flatlafin the shaded/fat jar.Structured resolver data flow
The old UI only received rendered text. This PR adds a structured data path:
ResultAnalyzerbuildsDependencyGuiDatafrom the resolver result.ModResolverattaches that data toModResolutionException.FabricLoaderImplpreserves the original exception as the cause when wrapping it.FabricGuiEntryfinds the structured data in the exception chain and attaches it toFabricStatusTree.FabricMainWindowrenders the structured data directly.This avoids fragile parsing of strings like
Install ...,requires ..., or localized resolver messages.GUI changes
FabricMainWindowDependency/conflict model changes
FabricStatusTreeDependencyGuiData.ResultAnalyzerbreaksentries to GUI conflicts.Error propagation changes
ModResolutionExceptionDependencyGuiData.ModResolverFabricLoaderImplModResolutionExceptioncause when creating the formatted incompatible-mod exception.FabricGuiEntryModResolutionExceptionand transfers itsDependencyGuiDatainto the status tree.Localization and resources
minecraft_x32.pngjava_x32.pngImportant behavior notes
breaksas user-facing “conflicts.”