Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ jobs:
- name: Build release archives
env:
RELEASE_VERSION: ${{ github.ref_name }}
run: ./gradlew distZip distTar -PreleaseVersion="${RELEASE_VERSION#v}" --no-daemon --stacktrace --console=plain
run: ./gradlew distChecksums -PreleaseVersion="${RELEASE_VERSION#v}" --no-daemon --stacktrace --console=plain

- name: Publish release
env:
GH_TOKEN: ${{ github.token }}
run: gh release create "$GITHUB_REF_NAME" build/distributions/*.zip build/distributions/*.tar.gz --verify-tag --title "$GITHUB_REF_NAME" --notes ""
run: gh release create "$GITHUB_REF_NAME" build/distributions/*.zip build/distributions/*.tar.gz build/distributions/*.sha256 --verify-tag --title "$GITHUB_REF_NAME" --notes ""
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
build/
output/
logs/
config/
saves/
runtime-classpath.txt
*.class
.idea/
Expand Down
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,43 @@ public final class MyMachinePreview implements PreviewEntrypoint {

`previewedClass()` records the production GUI class in `bounds.json`. This makes it possible to verify that the screenshot came from production code rather than a lookalike adapter.

The Galaxia Oxygen Filler under `integrations/galaxia-oxygen-filler` demonstrates this integration shape. It requires a compiled Galaxia checkout and its production dependencies in `runtime-classpath.txt`.
For a larger production integration, keep the preview catalog in the target repository, for example under `tools/gui-preview`. The previewer builds the required production classes and discovers their runtime classpath automatically.

### Production GUI catalog

Implement `PreviewCatalog` when one project needs several production GUI states. Each `PreviewScenario` names the production class, creates only the local client-visible state needed by that GUI, and may attach tags, expected assets, or an action script:

```java
public final class MyModPreviews implements PreviewCatalog {

@Override
public List<PreviewScenario> scenarios() {
return List.of(PreviewScenario.define(
"machine/default",
"machine screen with representative local state",
"machine",
MyMachineGui.class,
MyMachinePreview::new)
.tags("default", "interaction")
.actions("actions/machine.txt"));
}
}
```

Set `preview.entrypoint` in `preview.properties` to the catalog class. Scenario IDs use `family/name`. The `default` tag selects the canonical verification state. Every production class in the catalog must have exactly one `default` scenario.

```bat
preview.bat list project-directory
preview.bat doctor project-directory
preview.bat open project-directory machine/default
preview.bat render project-directory machine/default
preview.bat verify project-directory
preview.bat verify project-directory machine
preview.bat verify project-directory --full
preview.bat verify project-directory --failed
```

`verify` runs default scenarios in isolated workers. `--full` also runs non-default states and their action scripts. `--failed` reruns failures from the previous report. Use `list` to discover IDs and `doctor` to check the production classpath, assets, and catalog before rendering.

## Additional project inputs

Expand Down
37 changes: 35 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import java.security.MessageDigest
import java.util.HexFormat

plugins {
java
application
Expand Down Expand Up @@ -44,13 +47,42 @@ dependencies {
}

val distributionZip = tasks.named<Zip>("distZip")
val distributionTar = tasks.named<Tar>("distTar")

val distributionChecksums = tasks.register("distChecksums") {
group = "distribution"
description = "Writes SHA-256 files for the portable release archives."
dependsOn(distributionZip, distributionTar)

doLast {
listOf(distributionZip.get().archiveFile.get().asFile, distributionTar.get().archiveFile.get().asFile)
.forEach { archive ->
val digest = MessageDigest.getInstance("SHA-256")
archive.inputStream().use { input ->
val buffer = ByteArray(8192)
while (true) {
val count = input.read(buffer)
if (count < 0) break
digest.update(buffer, 0, count)
}
}
val checksum = HexFormat.of().formatHex(digest.digest())
file(archive.parentFile.resolve(archive.name + ".sha256"))
.writeText("$checksum ${archive.name}\n")
}
}
}

tasks.test {
useJUnitPlatform()
dependsOn(distributionZip)
dependsOn(distributionChecksums)

doFirst {
systemProperty("preview.distribution.zip", distributionZip.get().archiveFile.get().asFile)
systemProperty(
"preview.distribution.zip.checksum",
distributionZip.get().archiveFile.get().asFile.parentFile.resolve(
distributionZip.get().archiveFile.get().asFile.name + ".sha256"))
systemProperty(
"modularui.test.jar",
bundledRuntime.single { it.name.startsWith("ModularUI2-") })
Expand Down Expand Up @@ -80,12 +112,13 @@ distributions {
from("THIRD_PARTY_NOTICES.md")
from("examples") {
into("examples")
exclude("**/build/**", "**/output/**", "**/logs/**", "**/runtime-classpath.txt")
}
}
}
}

tasks.named<Tar>("distTar") {
distributionTar {
compression = Compression.GZIP
archiveExtension.set("tar.gz")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
preview.entrypoint=example.OxygenFillerPreview
preview.entrypoint=example.CatalogDemo
screen.width=1920
screen.height=1080
gui.scale=auto
Expand Down
40 changes: 40 additions & 0 deletions examples/catalog-demo/src/preview/java/example/CatalogDemo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package example;

import com.cleanroommc.modularui.screen.ModularPanel;
import com.cleanroommc.modularui.widgets.ButtonWidget;
import com.cleanroommc.modularui.widgets.TextWidget;
import dev.modularui.preview.PreviewCatalog;
import dev.modularui.preview.PreviewEntrypoint;
import dev.modularui.preview.PreviewScenario;
import java.util.List;

public final class CatalogDemo implements PreviewCatalog {

@Override
public List<PreviewScenario> scenarios() {
return List.of(PreviewScenario.define(
"demo/default",
"clickable catalog example",
"demo",
CatalogDemo.class,
DemoEntrypoint::new).tags("default", "interaction"));
}

private static final class DemoEntrypoint implements PreviewEntrypoint {

@Override
public Class<?> previewedClass() {
return CatalogDemo.class;
}

@Override
public Object createPanel(Context context) {
return ModularPanel.defaultPanel("catalog_demo", 176, 90)
.child(new ButtonWidget<>()
.pos(58, 32)
.size(60, 24)
.onMousePressed(button -> true)
.child(new TextWidget<>("Click me").coverChildren()));
}
}
}

This file was deleted.

This file was deleted.

4 changes: 0 additions & 4 deletions integrations/galaxia-starmap/actions.txt

This file was deleted.

5 changes: 0 additions & 5 deletions integrations/galaxia-starmap/preview.properties

This file was deleted.

3 changes: 0 additions & 3 deletions integrations/galaxia-starmap/runtime-classpath.example.txt

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion preview.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ if [ ! -f "$preview_launcher" ]; then
fi
fi

exec "$preview_launcher" "$@"
exec sh "$preview_launcher" "$@"
22 changes: 3 additions & 19 deletions src/main/java/com/cleanroommc/modularui/api/drawable/IKey.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.cleanroommc.modularui.api.drawable;

import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Properties;

import com.cleanroommc.modularui.widgets.TextWidget;

import net.minecraft.util.StatCollector;

public interface IKey {

static IKey str(String text) {
Expand Down Expand Up @@ -47,8 +45,6 @@ public String get() {

final class LangKey implements IKey {

private static final Properties TRANSLATIONS = loadTranslations();

private final String key;
private final Object[] arguments;

Expand All @@ -59,20 +55,8 @@ private LangKey(String key, Object[] arguments) {

@Override
public String get() {
String translated = TRANSLATIONS.getProperty(key, key);
String translated = StatCollector.translateToLocal(key);
return arguments.length == 0 ? translated : String.format(Locale.ROOT, translated, arguments);
}

private static Properties loadTranslations() {
Properties translations = new Properties();
try (var stream = IKey.class.getResourceAsStream("/assets/galaxia/lang/en_US.lang")) {
if (stream != null) {
translations.load(new InputStreamReader(stream, StandardCharsets.UTF_8));
}
} catch (IOException exception) {
throw new IllegalStateException("Could not load preview translations", exception);
}
return translations;
}
}
}
Loading
Loading