Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,35 @@ loom {
}

repositories {
mavenLocal() // TODO: Remove me after Greenhouse Config releases.
mavenCentral()
maven("https://maven.parchmentmc.org/")
maven("https://maven.spiritstudios.dev/releases/")
maven("https://moehreag.duckdns.org/maven/releases") {
content {
includeGroup("io.github.axolotlclient.AxolotlClient")
includeGroup("io.github.axolotlclient.AxolotlClient-config")
}
}
maven("https://maven.greenhouse.lgbt/releases/")
}

dependencies {
minecraft(libs.minecraft)
mappings(variantOf(libs.yarn) { classifier("v2") })
mappings(loom.layered {
officialMojangMappings()
parchment(libs.parchment)
})
modImplementation(libs.fabric.loader)

modImplementation(libs.fabric.api)

include(libs.bundles.specter)
modImplementation(libs.bundles.specter)

include(libs.greenhouse.config)
modImplementation(libs.greenhouse.config)

implementation(libs.objc.bridge)
}

Expand Down
15 changes: 7 additions & 8 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,38 +1,37 @@
[versions]
fabric_loom = "1.13-SNAPSHOT"
fabric_loom = "1.14-SNAPSHOT"
minotaur = "2.+"

minecraft = "1.21.10"
yarn = "1.21.10+build.2"
parchment = "2025.10.12"

fabric_loader = "0.17.3"
fabric_api = "0.138.3+1.21.10"

specter = "1.3.2"
greenhouse_config = "3.0.0-beta.1+1.21.10"
objc_bridge = "1.0.0"

[plugins]
fabric_loom = { id = "fabric-loom", version.ref = "fabric_loom" }
fabric_loom = { id = "net.fabricmc.fabric-loom-remap", version.ref = "fabric_loom" }
minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" }

[libraries]
minecraft = { group = "mojang", name = "minecraft", version.ref = "minecraft" }
yarn = { group = "net.fabricmc", name = "yarn", version.ref = "yarn" }
parchment = { module = "org.parchmentmc.data:parchment-1.21.10", version.ref = "parchment" }

fabric_loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabric_loader" }
fabric_api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric_api" }

specter_config = { group = "dev.spiritstudios.specter", name = "config", version.ref = "specter" }
specter_core = { group = "dev.spiritstudios.specter", name = "core", version.ref = "specter" }
specter_serialization = { group = "dev.spiritstudios.specter", name = "serialization", version.ref = "specter" }
specter_gui = { group = "dev.spiritstudios.specter", name = "gui", version.ref = "specter" }

greenhouse_config = { group = "lgbt.greenhouse.config", name = "greenhouseconfig-fabric", version.ref = "greenhouse_config" }

objc_bridge = { group = "ca.weblite", name = "java-objc-bridge", version.ref = "objc_bridge" }

[bundles]
specter = [
"specter_serialization",
"specter_core",
"specter_config",
"specter_gui"
]
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
15 changes: 4 additions & 11 deletions src/client/java/dev/spiritstudios/snapper/Snapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@
import dev.spiritstudios.snapper.util.PlatformHelper;
import dev.spiritstudios.snapper.util.actions.GeneralPlatformActions;
import dev.spiritstudios.snapper.util.actions.MacPlatformActions;
import dev.spiritstudios.snapper.util.config.DirectoryConfigUtil;
import dev.spiritstudios.snapper.util.uploading.ScreenshotUploading;
import dev.spiritstudios.specter.api.config.client.ConfigScreenWidgets;
import dev.spiritstudios.specter.api.config.client.ModMenuHelper;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.minecraft.util.Identifier;
import net.minecraft.resources.ResourceLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.file.Path;
import java.util.Locale;

public final class Snapper implements ClientModInitializer {
Expand All @@ -23,16 +19,13 @@ public final class Snapper implements ClientModInitializer {

@Override
public void onInitializeClient() {
ConfigScreenWidgets.add(Path.class, DirectoryConfigUtil.PATH_WIDGET_FACTORY);
SnapperConfig.init();
SnapperKeybindings.init();

ModMenuHelper.addConfig(Snapper.MODID, SnapperConfig.HOLDER.id());

ClientLifecycleEvents.CLIENT_STOPPING.register(client -> ScreenshotUploading.close());
}

public static Identifier id(String path) {
return Identifier.of(MODID, path);
public static ResourceLocation id(String path) {
return ResourceLocation.fromNamespaceAndPath(MODID, path);
}

public static PlatformHelper getPlatformHelper() {
Expand Down
241 changes: 208 additions & 33 deletions src/client/java/dev/spiritstudios/snapper/SnapperConfig.java
Original file line number Diff line number Diff line change
@@ -1,51 +1,226 @@
package dev.spiritstudios.snapper;

import com.mojang.serialization.Codec;
import dev.spiritstudios.snapper.gui.screen.ScreenshotScreen;
import dev.spiritstudios.snapper.util.SnapperUtil;
import dev.spiritstudios.snapper.util.config.DirectoryConfigUtil;
import dev.spiritstudios.snapper.util.uploading.AxolotlClientApi;
import dev.spiritstudios.specter.api.config.Config;
import dev.spiritstudios.specter.api.config.ConfigHolder;
import dev.spiritstudios.specter.api.config.Value;
import lgbt.greenhouse.config.api.v3.GreenhouseConfigSide;
import lgbt.greenhouse.config.api.v3.config.GreenhouseConfigHolder;
import lgbt.greenhouse.config.api.v3.dfu.builder.DataFixerBuilderFunctions;
import lgbt.greenhouse.config.api.v3.dfu.builder.schema.TypeTemplateBuilder;
import lgbt.greenhouse.config.api.v3.dfu.fix.GreenhouseConfigRelocateFieldsFix;
import lgbt.greenhouse.config.api.v3.lang.GreenhouseConfigJsonCLang;
import lgbt.greenhouse.config.api.v3.lang.GreenhouseConfigJsonLang;

import java.nio.file.Path;
import java.util.Map;

public final class SnapperConfig extends Config<SnapperConfig> {
public static final ConfigHolder<SnapperConfig, ?> HOLDER = ConfigHolder.builder(
Snapper.id("snapper"), SnapperConfig.class
).build();
public record SnapperConfig(boolean copyTakenScreenshot,
SnapperButton snapperButton,
ScreenshotScreen.ViewMode viewMode,
SnapperUtil.PanoramaSize panoramaDimensions,
CustomScreenshotFolder customScreenshotPath,
AxolotlClient axolotlClient) {
public static final GreenhouseConfigHolder<SnapperConfig> HOLDER = GreenhouseConfigHolder.register(
SnapperConfig.class,
"snapper",
1,
GreenhouseConfigJsonCLang.INSTANCE,
GreenhouseConfigSide.CLIENT,
configBuilder -> configBuilder
.withValue(
"copy_taken_screenshot",
"Whether to copy screenshots to the clipboard when taken.",
Codec.BOOL,
false,
SnapperConfig::copyTakenScreenshot
).withMapValue(
SnapperButton.class,
"snapper_button",
"Settings relating to the Snapper Button",
SnapperConfig::snapperButton,
mapBuilder -> mapBuilder
.withValue(
"show_on_title_screen",
"Whether to show Snapper button on title screen.",
Codec.BOOL,
true,
SnapperButton::showOnTitleScreen
).withValue(
"show_in_game_menu",
"Whether to show Snapper button in game menu.",
Codec.BOOL,
true,
SnapperButton::showInGameMenu
)
).withValue(
"view_mode",
"""
Whether to show the screenshot menu in a grid or a list.
May be either 'grid' or 'list'.""",
ScreenshotScreen.ViewMode.CODEC,
ScreenshotScreen.ViewMode.GRID,
SnapperConfig::viewMode
).withValue(
"panorama_size",
"""
Dimensions of individual panorama images when saved.
May be 1024, 2048, or 4096.""",
SnapperUtil.PanoramaSize.CODEC,
SnapperUtil.PanoramaSize.ONE_THOUSAND_TWENTY_FOUR,
SnapperConfig::panoramaDimensions
).withMapValue(
CustomScreenshotFolder.class,
"custom_screenshot_path",
"Settings relating to the custom screenshot path",
SnapperConfig::customScreenshotPath,
mapBuilder -> mapBuilder
.withValue(
"enabled",
"Whether to use a custom screenshot path instead of Minecraft's default",
Codec.BOOL,
false,
CustomScreenshotFolder::enabled
).withValue(
"path",
"The path to use if custom screenshot folders are enabled.",
DirectoryConfigUtil.PATH_CODEC,
SnapperUtil.UNIFIED_FOLDER,
CustomScreenshotFolder::path
)
).withMapValue(
AxolotlClient.class,
"axolotl_client",
"Settings relating to Axolotl Client.",
SnapperConfig::axolotlClient,
mapBuilder -> mapBuilder
.withValue(
"terms_status",
"""
Whether the terms of AxolotlClient have been accepted.
These terms must be accepted to share screenshots via AxolotlClient's image host.
May be 'accept', 'deny', or 'unset'.""",
AxolotlClientApi.TermsAcceptance.CODEC,
AxolotlClientApi.TermsAcceptance.UNSET,
AxolotlClient::termsStatus
)
),
dataFixerBuilder -> dataFixerBuilder
.withSchema(
0,
schemaBuilder ->
schemaBuilder
.withField("copyTakenScreenshot", TypeTemplateBuilder.BOOL)
.withField("showSnapperTitleScreen", TypeTemplateBuilder.BOOL)
.withField("showSnapperGameMenu", TypeTemplateBuilder.BOOL)
.withField("viewMode", TypeTemplateBuilder.STRING)
.withField("termsAccepted", TypeTemplateBuilder.STRING)
.withField("panoramaDimensions", TypeTemplateBuilder.STRING)
.withField("useCustomScreenshotFolder", TypeTemplateBuilder.BOOL)
.withField("customScreenshotFolder", TypeTemplateBuilder.STRING)
).withPreviousLang(0, GreenhouseConfigJsonLang.INSTANCE)
.withSchemaAndFixes(
1,
DataFixerBuilderFunctions.create(
builder -> builder
.withField("copy_taken_screenshot", TypeTemplateBuilder.BOOL)
.withField("snapper_button", TypeTemplateBuilder.map(
mapBuilder -> mapBuilder
.withField("show_on_title_screen", TypeTemplateBuilder.BOOL)
.withField("show_in_game_menu", TypeTemplateBuilder.BOOL)
))
.withField("view_mode", TypeTemplateBuilder.STRING)
.withField("panorama_dimensions", TypeTemplateBuilder.STRING)
.withField("custom_screenshot_path", TypeTemplateBuilder.map(
mapBuilder -> mapBuilder
.withField("enabled", TypeTemplateBuilder.BOOL)
.withField("path", TypeTemplateBuilder.STRING)
))
.withField("axolotl_client", TypeTemplateBuilder.map(
mapBuilder -> mapBuilder
.withField("terms_status", TypeTemplateBuilder.STRING)
))
.noInheritance(),
schema -> GreenhouseConfigRelocateFieldsFix.create(
schema,
Map.of(
"copyTakenScreenshot", "copy_taken_screenshot",
"showSnapperTitleScreen", "snapper_button.show_on_title_screen",
"showSnapperGameMenu", "snapper_button.show_in_game_menu",
"viewMode", "view_mode",
"panoramaDimensions", "panorama_dimensions",
"useCustomScreenshotFolder", "custom_screenshot_path.enabled",
"customScreenshotFolder", "custom_screenshot_path.path",
"termsAccepted", "axolotl_client.terms_status"
)
)
)
)
);

public static final SnapperConfig INSTANCE = HOLDER.get();
public record SnapperButton(boolean showOnTitleScreen, boolean showInGameMenu) {}
public record CustomScreenshotFolder(boolean enabled, Path path) {}
public record AxolotlClient(AxolotlClientApi.TermsAcceptance termsStatus) {}

public final Value<Boolean> copyTakenScreenshot = booleanValue(false)
.comment("Whether to copy screenshots to clipboard when taken.")
.build();
public static void init() {}
public static Mutable mutable() {
return new Mutable();
}

public final Value<Boolean> showSnapperTitleScreen = booleanValue(true)
.comment("Whether to show Snapper button on title screen.")
.build();
public static class Mutable {
// Root
public boolean copyTakenScreenshot;
public ScreenshotScreen.ViewMode viewMode;
public SnapperUtil.PanoramaSize panoramaDimensions;

public final Value<Boolean> showSnapperGameMenu = booleanValue(true)
.comment("Whether to show Snapper button in game menu.")
.build();
// Snapper Button
public boolean showOnTitleScreen;
public boolean showInGameMenu;

public final Value<ScreenshotScreen.ViewMode> viewMode = enumValue(ScreenshotScreen.ViewMode.GRID, ScreenshotScreen.ViewMode.class)
.comment("Whether to show screenshot menu with grid or list.")
.build();
// Custom Screenshot Folder
public boolean enabled;
public Path path;

public final Value<AxolotlClientApi.TermsAcceptance> termsAccepted = enumValue(AxolotlClientApi.TermsAcceptance.UNSET, AxolotlClientApi.TermsAcceptance.class)
.comment("Whether the terms of AxolotlClient have been accepted.")
.build();
// Axolotl Client
public AxolotlClientApi.TermsAcceptance termsAccepted;

public final Value<SnapperUtil.PanoramaSize> panoramaDimensions = enumValue(SnapperUtil.PanoramaSize.ONE_THOUSAND_TWENTY_FOUR, SnapperUtil.PanoramaSize.class)
.comment("Dimensions of individual panorama images when saved.")
.build();
public Mutable() {
SnapperConfig config = HOLDER.get();
copyTakenScreenshot = config.copyTakenScreenshot;
viewMode = config.viewMode;
panoramaDimensions = config.panoramaDimensions;

public final Value<Boolean> useCustomScreenshotFolder = booleanValue(false)
.comment("Whether to use a custom screenshot folder instead of Minecraft's default")
.build();
showOnTitleScreen = config.snapperButton.showOnTitleScreen;
showInGameMenu = config.snapperButton.showInGameMenu;

public final Value<Path> customScreenshotFolder = value(SnapperUtil.UNIFIED_FOLDER, DirectoryConfigUtil.PATH_CODEC)
.comment("What folder to use if custom screenshot folders are enabled.")
.build();
}
enabled = config.customScreenshotPath.enabled;
path = config.customScreenshotPath.path;

termsAccepted = config.axolotlClient.termsStatus;
}

public void save() {
HOLDER.save(build(), null);
}

private SnapperConfig build() {
return new SnapperConfig(
copyTakenScreenshot,
new SnapperButton(
showOnTitleScreen,
showInGameMenu
),
viewMode,
panoramaDimensions,
new CustomScreenshotFolder(
enabled,
path
),
new AxolotlClient(
termsAccepted
)
);
}
}
}
Loading