asset details (WIP)
This commit is contained in:
parent
91799bccd7
commit
a958052bbf
@ -1,6 +1,7 @@
|
||||
package p.packer.models;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@ -10,7 +11,8 @@ public record PackerFileCacheEntry(
|
||||
long size,
|
||||
long lastModified,
|
||||
String fingerprint,
|
||||
Map<String, Object> metadata) {
|
||||
Map<String, Object> metadata,
|
||||
List<PackerDiagnostic> diagnostics) {
|
||||
|
||||
public PackerFileCacheEntry {
|
||||
relativePath = normalizeRelativePath(relativePath);
|
||||
@ -23,6 +25,7 @@ public record PackerFileCacheEntry(
|
||||
}
|
||||
fingerprint = normalizeOptional(fingerprint);
|
||||
metadata = Map.copyOf(new LinkedHashMap<>(Objects.requireNonNull(metadata, "metadata")));
|
||||
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
||||
}
|
||||
|
||||
private static String normalizeRelativePath(String relativePath) {
|
||||
|
||||
@ -7,8 +7,11 @@ import p.packer.PackerWorkspacePaths;
|
||||
import p.packer.exceptions.PackerRegistryException;
|
||||
import p.packer.messages.PackerProjectContext;
|
||||
import p.packer.models.PackerAssetCacheEntry;
|
||||
import p.packer.models.PackerDiagnostic;
|
||||
import p.packer.models.PackerFileCacheEntry;
|
||||
import p.packer.models.PackerWorkspaceCacheState;
|
||||
import p.packer.messages.diagnostics.PackerDiagnosticCategory;
|
||||
import p.packer.messages.diagnostics.PackerDiagnosticSeverity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@ -54,7 +57,8 @@ public final class FileSystemPackerCacheRepository {
|
||||
file.size,
|
||||
file.lastModified,
|
||||
file.fingerprint,
|
||||
file.metadata == null ? Map.of() : file.metadata));
|
||||
file.metadata == null ? Map.of() : file.metadata,
|
||||
loadDiagnostics(file.diagnostics)));
|
||||
}
|
||||
}
|
||||
validateAssetId(asset.assetId);
|
||||
@ -92,7 +96,8 @@ public final class FileSystemPackerCacheRepository {
|
||||
file.size(),
|
||||
file.lastModified(),
|
||||
file.fingerprint(),
|
||||
file.metadata()))
|
||||
file.metadata(),
|
||||
saveDiagnostics(file.diagnostics())))
|
||||
.toList()))
|
||||
.toList();
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(cachePath.toFile(), document);
|
||||
@ -101,6 +106,39 @@ public final class FileSystemPackerCacheRepository {
|
||||
}
|
||||
}
|
||||
|
||||
private List<PackerDiagnostic> loadDiagnostics(List<CacheDiagnosticDocument> diagnostics) {
|
||||
if (diagnostics == null || diagnostics.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
final List<PackerDiagnostic> loaded = new ArrayList<>();
|
||||
for (CacheDiagnosticDocument diagnostic : diagnostics) {
|
||||
if (diagnostic == null || diagnostic.severity == null || diagnostic.category == null || diagnostic.message == null) {
|
||||
continue;
|
||||
}
|
||||
loaded.add(new PackerDiagnostic(
|
||||
diagnostic.severity,
|
||||
diagnostic.category,
|
||||
diagnostic.message,
|
||||
diagnostic.evidencePath == null || diagnostic.evidencePath.isBlank() ? null : Path.of(diagnostic.evidencePath),
|
||||
diagnostic.blocking));
|
||||
}
|
||||
return List.copyOf(loaded);
|
||||
}
|
||||
|
||||
private List<CacheDiagnosticDocument> saveDiagnostics(List<PackerDiagnostic> diagnostics) {
|
||||
if (diagnostics == null || diagnostics.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
return diagnostics.stream()
|
||||
.map(diagnostic -> new CacheDiagnosticDocument(
|
||||
diagnostic.severity(),
|
||||
diagnostic.category(),
|
||||
diagnostic.message(),
|
||||
diagnostic.evidencePath() == null ? null : diagnostic.evidencePath().toString(),
|
||||
diagnostic.blocking()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private void validateDuplicateAssetIds(List<PackerAssetCacheEntry> assets) {
|
||||
final Set<Integer> assetIds = new HashSet<>();
|
||||
for (PackerAssetCacheEntry asset : assets) {
|
||||
@ -162,6 +200,16 @@ public final class FileSystemPackerCacheRepository {
|
||||
@JsonProperty("size") long size,
|
||||
@JsonProperty("last_modified") long lastModified,
|
||||
@JsonProperty("fingerprint") String fingerprint,
|
||||
@JsonProperty("metadata") Map<String, Object> metadata) {
|
||||
@JsonProperty("metadata") Map<String, Object> metadata,
|
||||
@JsonProperty("diagnostics") List<CacheDiagnosticDocument> diagnostics) {
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private record CacheDiagnosticDocument(
|
||||
@JsonProperty("severity") PackerDiagnosticSeverity severity,
|
||||
@JsonProperty("category") PackerDiagnosticCategory category,
|
||||
@JsonProperty("message") String message,
|
||||
@JsonProperty("evidence_path") String evidencePath,
|
||||
@JsonProperty("blocking") boolean blocking) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +73,10 @@ public abstract class PackerAbstractBankWalker<R> {
|
||||
final var fileProbe = fileProbeMaybe.get();
|
||||
final var cachedEntry = resolvePriorFileCache(assetRoot, fileProbe.path(), priorAssetCache);
|
||||
if (cachedEntry.isPresent() && canReuseMetadata(fileProbe, cachedEntry.get())) {
|
||||
probeResults.add(new PackerProbeResult(fileProbe, cachedEntry.get().metadata(), List.of()));
|
||||
probeResults.add(new PackerProbeResult(
|
||||
fileProbe,
|
||||
cachedEntry.get().metadata(),
|
||||
cachedEntry.get().diagnostics()));
|
||||
continue;
|
||||
}
|
||||
probeResults.add(processFileProbe(fileProbe, requirements));
|
||||
|
||||
@ -74,7 +74,8 @@ public final class PackerRuntimeAssetMaterializer {
|
||||
file.size(),
|
||||
file.lastModified(),
|
||||
file.fingerprint(),
|
||||
file.metadata()))
|
||||
file.metadata(),
|
||||
file.diagnostics()))
|
||||
.toList());
|
||||
return new PackerRuntimeAssetMaterialization(runtimeAsset, Optional.of(assetCacheEntry));
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import p.packer.messages.diagnostics.PackerDiagnosticCategory;
|
||||
import p.packer.messages.diagnostics.PackerDiagnosticSeverity;
|
||||
import p.packer.models.*;
|
||||
import p.packer.repositories.FileSystemPackerCacheRepository;
|
||||
import p.packer.repositories.PackerContractFingerprint;
|
||||
import p.packer.repositories.PackerRuntimeRegistry;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -646,7 +647,11 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
}
|
||||
|
||||
try {
|
||||
final String previousContractFingerprint = contractFingerprint(manifest);
|
||||
patchManifestContract(manifest, request);
|
||||
if (!Objects.equals(previousContractFingerprint, contractFingerprint(manifest))) {
|
||||
clearManifestBankComposition(manifest);
|
||||
}
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
|
||||
final var runtime = runtimeRegistry.update(project, (currentSnapshot, generation) -> runtimePatchService.afterUpdateAssetContract(
|
||||
currentSnapshot,
|
||||
@ -770,6 +775,35 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
}
|
||||
}
|
||||
|
||||
private String contractFingerprint(ObjectNode manifest) {
|
||||
final JsonNode outputNode = manifest.path("output");
|
||||
if (!(outputNode instanceof ObjectNode outputObject)) {
|
||||
return PackerContractFingerprint.metadataFingerprint(Map.of());
|
||||
}
|
||||
final JsonNode metadataNode = outputObject.path("metadata");
|
||||
if (!(metadataNode instanceof ObjectNode metadataObject)) {
|
||||
return PackerContractFingerprint.metadataFingerprint(Map.of());
|
||||
}
|
||||
final Map<String, String> metadata = new LinkedHashMap<>();
|
||||
metadataObject.fields().forEachRemaining(entry -> {
|
||||
if (entry.getKey() == null || entry.getKey().isBlank()) {
|
||||
return;
|
||||
}
|
||||
final JsonNode valueNode = entry.getValue();
|
||||
if (valueNode == null || valueNode.isContainerNode()) {
|
||||
return;
|
||||
}
|
||||
metadata.put(entry.getKey().trim(), valueNode.isNull() ? "" : valueNode.asText(""));
|
||||
});
|
||||
return PackerContractFingerprint.metadataFingerprint(metadata);
|
||||
}
|
||||
|
||||
private void clearManifestBankComposition(ObjectNode manifest) {
|
||||
final ObjectNode inputsNode = mutableObject(manifest, "inputs");
|
||||
inputsNode.removeAll();
|
||||
manifest.putArray("artifacts");
|
||||
}
|
||||
|
||||
private boolean isTrustedRelativePath(String value) {
|
||||
final Path path = Path.of(value).normalize();
|
||||
return !path.isAbsolute() && !path.startsWith("..");
|
||||
|
||||
@ -44,6 +44,7 @@ public final class PackerAssetDetailsService {
|
||||
final var parsed = runtimeAsset.parsedDeclaration();
|
||||
diagnostics.addAll(parsed.diagnostics());
|
||||
diagnostics.addAll(runtimeAsset.walkDiagnostics());
|
||||
appendWalkFileDiagnostics(runtimeAsset, diagnostics);
|
||||
if (!parsed.valid()) {
|
||||
return failureResult(project, request.assetReference(), resolved, diagnostics);
|
||||
}
|
||||
@ -253,6 +254,18 @@ public final class PackerAssetDetailsService {
|
||||
true));
|
||||
}
|
||||
|
||||
private void appendWalkFileDiagnostics(
|
||||
PackerRuntimeAsset runtimeAsset,
|
||||
List<PackerDiagnostic> diagnostics) {
|
||||
for (PackerRuntimeWalkFile file : runtimeAsset.walkProjection().buildCandidateFiles()) {
|
||||
for (PackerDiagnostic diagnostic : file.diagnostics()) {
|
||||
if (!diagnostics.contains(diagnostic)) {
|
||||
diagnostics.add(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AssetReference canonicalReference(
|
||||
final PackerProjectContext project,
|
||||
final Path assetRoot,
|
||||
|
||||
@ -7,8 +7,11 @@ import p.packer.PackerWorkspacePaths;
|
||||
import p.packer.exceptions.PackerRegistryException;
|
||||
import p.packer.messages.PackerProjectContext;
|
||||
import p.packer.models.PackerAssetCacheEntry;
|
||||
import p.packer.models.PackerDiagnostic;
|
||||
import p.packer.models.PackerFileCacheEntry;
|
||||
import p.packer.models.PackerWorkspaceCacheState;
|
||||
import p.packer.messages.diagnostics.PackerDiagnosticCategory;
|
||||
import p.packer.messages.diagnostics.PackerDiagnosticSeverity;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -46,14 +49,21 @@ final class FileSystemPackerCacheRepositoryTest {
|
||||
128L,
|
||||
42L,
|
||||
"abc123",
|
||||
Map.of("tile_count", 4)),
|
||||
Map.of("tile_count", 4),
|
||||
List.of(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.WARNING,
|
||||
PackerDiagnosticCategory.HYGIENE,
|
||||
"cached warning",
|
||||
Path.of("/tmp/ui.png"),
|
||||
false))),
|
||||
new PackerFileCacheEntry(
|
||||
"tiles/ui-2.png",
|
||||
"image/png",
|
||||
256L,
|
||||
84L,
|
||||
"def456",
|
||||
Map.of()))),
|
||||
Map.of(),
|
||||
List.of()))),
|
||||
new PackerAssetCacheEntry(1, "contract-1", List.of(
|
||||
new PackerFileCacheEntry(
|
||||
"audio/click.wav",
|
||||
@ -61,7 +71,8 @@ final class FileSystemPackerCacheRepositoryTest {
|
||||
512L,
|
||||
21L,
|
||||
null,
|
||||
Map.of("sample_count", 8))))));
|
||||
Map.of("sample_count", 8),
|
||||
List.of())))));
|
||||
|
||||
repository.save(project, state);
|
||||
final var loaded = repository.load(project);
|
||||
@ -70,6 +81,8 @@ final class FileSystemPackerCacheRepositoryTest {
|
||||
assertEquals("contract-3", loaded.findAsset(3).orElseThrow().contractFingerprint());
|
||||
assertTrue(loaded.findAsset(3).flatMap(asset -> asset.findFile("tiles/ui.png")).isPresent());
|
||||
assertEquals(128L, loaded.findAsset(3).orElseThrow().findFile("tiles/ui.png").orElseThrow().size());
|
||||
assertEquals(1, loaded.findAsset(3).orElseThrow().findFile("tiles/ui.png").orElseThrow().diagnostics().size());
|
||||
assertEquals("cached warning", loaded.findAsset(3).orElseThrow().findFile("tiles/ui.png").orElseThrow().diagnostics().getFirst().message());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -114,7 +127,7 @@ final class FileSystemPackerCacheRepositoryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotSerializeDiagnostics() throws Exception {
|
||||
void serializesDiagnosticsWhenPresent() throws Exception {
|
||||
final var repository = new FileSystemPackerCacheRepository(MAPPER);
|
||||
final var project = project(tempDir.resolve("project"));
|
||||
final var state = new PackerWorkspaceCacheState(
|
||||
@ -126,13 +139,19 @@ final class FileSystemPackerCacheRepositoryTest {
|
||||
128L,
|
||||
42L,
|
||||
"abc123",
|
||||
Map.of("warning_count", 2))))));
|
||||
Map.of("warning_count", 2),
|
||||
List.of(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.WARNING,
|
||||
PackerDiagnosticCategory.HYGIENE,
|
||||
"cached diagnostic",
|
||||
Path.of("/tmp/ui.png"),
|
||||
false)))))));
|
||||
|
||||
repository.save(project, state);
|
||||
final String json = Files.readString(PackerWorkspacePaths.cachePath(project));
|
||||
|
||||
assertFalse(json.contains("diagnostic"));
|
||||
assertFalse(json.contains("diagnostics"));
|
||||
assertTrue(json.contains("\"diagnostics\""));
|
||||
assertTrue(json.contains("\"cached diagnostic\""));
|
||||
assertTrue(json.contains("\"asset_id\""));
|
||||
assertTrue(json.contains("\"contract_fingerprint\""));
|
||||
assertTrue(json.contains("\"metadata\""));
|
||||
|
||||
@ -29,7 +29,7 @@ final class PackerAbstractBankWalkerTest {
|
||||
final var filePath = writeText(assetRoot.resolve("entry.txt"), "hello world");
|
||||
final long lastModified = Files.getLastModifiedTime(filePath).toMillis();
|
||||
final var priorCache = new PackerAssetCacheEntry(1, "contract", List.of(
|
||||
new PackerFileCacheEntry("entry.txt", "text/plain", 1L, lastModified, "cached", Map.of("source", "cache"))));
|
||||
new PackerFileCacheEntry("entry.txt", "text/plain", 1L, lastModified, "cached", Map.of("source", "cache"), List.of())));
|
||||
|
||||
final var result = walker.walk(assetRoot, "requirements", java.util.Optional.of(priorCache));
|
||||
|
||||
@ -51,7 +51,8 @@ final class PackerAbstractBankWalkerTest {
|
||||
Files.size(filePath),
|
||||
currentLastModified - 1L,
|
||||
"cached",
|
||||
Map.of("source", "cache"))));
|
||||
Map.of("source", "cache"),
|
||||
List.of())));
|
||||
|
||||
final var result = walker.walk(assetRoot, "requirements", java.util.Optional.of(priorCache));
|
||||
|
||||
@ -74,13 +75,21 @@ final class PackerAbstractBankWalkerTest {
|
||||
Files.size(filePath),
|
||||
lastModified,
|
||||
fingerprint,
|
||||
Map.of("source", "cache"))));
|
||||
Map.of("source", "cache"),
|
||||
List.of(new PackerDiagnostic(
|
||||
p.packer.messages.diagnostics.PackerDiagnosticSeverity.WARNING,
|
||||
p.packer.messages.diagnostics.PackerDiagnosticCategory.HYGIENE,
|
||||
"cached file-scoped diagnostic",
|
||||
filePath,
|
||||
false)))));
|
||||
|
||||
final var result = walker.walk(assetRoot, "requirements", java.util.Optional.of(priorCache));
|
||||
|
||||
assertEquals(0, walker.processCount());
|
||||
assertEquals(1, walker.fingerprintCount());
|
||||
assertEquals("cache", result.probeResults().getFirst().metadata().get("source"));
|
||||
assertEquals(1, result.probeResults().getFirst().diagnostics().size());
|
||||
assertTrue(result.probeResults().getFirst().diagnostics().getFirst().message().contains("cached"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -96,7 +105,8 @@ final class PackerAbstractBankWalkerTest {
|
||||
Files.size(filePath),
|
||||
lastModified,
|
||||
null,
|
||||
Map.of("source", "cache"))));
|
||||
Map.of("source", "cache"),
|
||||
List.of())));
|
||||
|
||||
final var result = walker.walk(assetRoot, "requirements", java.util.Optional.of(priorCache));
|
||||
|
||||
|
||||
@ -15,6 +15,8 @@ import p.packer.repositories.PackerRuntimeRegistry;
|
||||
import p.packer.repositories.PackerRuntimeSnapshotLoader;
|
||||
import p.packer.testing.PackerFixtureLocator;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
@ -245,6 +247,41 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertEquals(1, loader.loadCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateAssetContractDropsPersistedBankCompositionWhenContractFingerprintChanges() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("update-contract-drops-bank"));
|
||||
final Path assetRoot = projectRoot.resolve("assets/ui/atlas");
|
||||
writeTilePng(assetRoot.resolve("confirm.png"), 16);
|
||||
writeTilePng(assetRoot.resolve("cancel.png"), 16);
|
||||
final FileSystemPackerWorkspaceService service = service();
|
||||
|
||||
final var applyResult = service.applyBankComposition(new ApplyBankCompositionRequest(
|
||||
project(projectRoot),
|
||||
AssetReference.forAssetId(1),
|
||||
List.of("cancel.png", "confirm.png")));
|
||||
assertTrue(applyResult.success());
|
||||
|
||||
final var updateResult = service.updateAssetContract(new UpdateAssetContractRequest(
|
||||
project(projectRoot),
|
||||
AssetReference.forAssetId(1),
|
||||
true,
|
||||
OutputCodecCatalog.NONE,
|
||||
Map.of(),
|
||||
Map.of("tile_size", "32x32")));
|
||||
assertTrue(updateResult.success());
|
||||
|
||||
final var manifest = MAPPER.readTree(assetRoot.resolve("asset.json").toFile());
|
||||
assertTrue(manifest.path("inputs").isObject());
|
||||
assertTrue(manifest.path("inputs").isEmpty());
|
||||
assertTrue(manifest.path("artifacts").isArray());
|
||||
assertTrue(manifest.path("artifacts").isEmpty());
|
||||
|
||||
final var detailsResult = service.getAssetDetails(new GetAssetDetailsRequest(
|
||||
project(projectRoot),
|
||||
AssetReference.forAssetId(1)));
|
||||
assertTrue(detailsResult.details().bankComposition().selectedFiles().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyBankCompositionWritesArtifactsAndRefreshesSnapshotWithoutWholeProjectReload() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("apply-bank-composition"));
|
||||
@ -284,6 +321,44 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertEquals(1, loader.loadCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deepSyncPreservesContractDrivenBankInvalidationAfterPatchUpdate() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("deep-sync-bank-composition"));
|
||||
final Path assetRoot = projectRoot.resolve("assets/ui/atlas");
|
||||
writeTilePng(assetRoot.resolve("confirm.png"), 16);
|
||||
writeTilePng(assetRoot.resolve("cancel.png"), 16);
|
||||
final FileSystemPackerWorkspaceService service = service();
|
||||
|
||||
final var applyResult = service.applyBankComposition(new ApplyBankCompositionRequest(
|
||||
project(projectRoot),
|
||||
AssetReference.forAssetId(1),
|
||||
List.of("cancel.png", "confirm.png")));
|
||||
assertTrue(applyResult.success());
|
||||
|
||||
final var updateResult = service.updateAssetContract(new UpdateAssetContractRequest(
|
||||
project(projectRoot),
|
||||
AssetReference.forAssetId(1),
|
||||
true,
|
||||
OutputCodecCatalog.NONE,
|
||||
Map.of(),
|
||||
Map.of("tile_size", "32x32")));
|
||||
assertTrue(updateResult.success());
|
||||
|
||||
final var patchedDetails = service.getAssetDetails(new GetAssetDetailsRequest(
|
||||
project(projectRoot),
|
||||
AssetReference.forAssetId(1)));
|
||||
assertTrue(patchedDetails.details().bankComposition().availableFiles().isEmpty());
|
||||
assertTrue(patchedDetails.details().bankComposition().selectedFiles().isEmpty());
|
||||
|
||||
service.listAssets(new ListAssetsRequest(project(projectRoot), true));
|
||||
|
||||
final var refreshedDetails = service.getAssetDetails(new GetAssetDetailsRequest(
|
||||
project(projectRoot),
|
||||
AssetReference.forAssetId(1)));
|
||||
assertTrue(refreshedDetails.details().bankComposition().availableFiles().isEmpty());
|
||||
assertTrue(refreshedDetails.details().bankComposition().selectedFiles().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsFailureWhenAssetManifestIsMissingOnContractUpdate() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("update-contract-missing-manifest"));
|
||||
@ -786,4 +861,15 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
}
|
||||
return targetRoot;
|
||||
}
|
||||
|
||||
private void writeTilePng(Path path, int tileSize) throws Exception {
|
||||
Files.createDirectories(path.getParent());
|
||||
final BufferedImage image = new BufferedImage(tileSize, tileSize, BufferedImage.TYPE_INT_ARGB);
|
||||
for (int y = 0; y < tileSize; y += 1) {
|
||||
for (int x = 0; x < tileSize; x += 1) {
|
||||
image.setRGB(x, y, 0xFFFF0000);
|
||||
}
|
||||
}
|
||||
ImageIO.write(image, "png", path.toFile());
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,6 +107,22 @@ final class PackerAssetDetailsServiceTest {
|
||||
result.details().bankComposition().selectedFiles().stream().map(file -> file.path()).toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
void includesFileScopedDiagnosticsInAssetDetailsDiagnostics() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("managed-file-diagnostics"));
|
||||
final Path assetRoot = projectRoot.resolve("assets/ui/atlas");
|
||||
writeTilePng(assetRoot.resolve("wrong-palette.png"), 32);
|
||||
|
||||
final PackerAssetDetailsService service = service();
|
||||
final var result = service.getAssetDetails(new GetAssetDetailsRequest(project(projectRoot), AssetReference.forAssetId(1)));
|
||||
|
||||
assertEquals(PackerOperationStatus.PARTIAL, result.status());
|
||||
assertTrue(result.diagnostics().stream()
|
||||
.anyMatch(diagnostic -> diagnostic.message().contains("Invalid tile dimensions for wrong-palette.png")));
|
||||
assertTrue(result.details().diagnostics().stream()
|
||||
.anyMatch(diagnostic -> diagnostic.message().contains("Invalid tile dimensions for wrong-palette.png")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsUnregisteredDetailsForValidUnregisteredRootReference() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/orphan-valid", tempDir.resolve("orphan"));
|
||||
@ -224,4 +240,15 @@ final class PackerAssetDetailsServiceTest {
|
||||
}
|
||||
return targetRoot;
|
||||
}
|
||||
|
||||
private void writeTilePng(Path path, int tileSize) throws Exception {
|
||||
Files.createDirectories(path.getParent());
|
||||
final BufferedImage image = new BufferedImage(tileSize, tileSize, BufferedImage.TYPE_INT_ARGB);
|
||||
for (int y = 0; y < tileSize; y += 1) {
|
||||
for (int x = 0; x < tileSize; x += 1) {
|
||||
image.setRGB(x, y, 0xFFFF0000);
|
||||
}
|
||||
}
|
||||
ImageIO.write(image, "png", path.toFile());
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,6 +104,7 @@ public enum I18n {
|
||||
ASSETS_SECTION_ACTIONS("assets.section.actions"),
|
||||
ASSETS_ACTIONS_EMPTY("assets.actions.empty"),
|
||||
ASSETS_ACTION_REGISTER("assets.action.register"),
|
||||
ASSETS_ACTION_ANALYSE("assets.action.analyse"),
|
||||
ASSETS_ACTION_DELETE("assets.action.delete"),
|
||||
ASSETS_ACTION_INCLUDE_IN_BUILD("assets.action.includeInBuild"),
|
||||
ASSETS_ACTION_EXCLUDE_FROM_BUILD("assets.action.excludeFromBuild"),
|
||||
@ -162,6 +163,11 @@ public enum I18n {
|
||||
ASSETS_LOGS_TITLE("assets.logs.title"),
|
||||
ASSETS_INPUTS_EMPTY("assets.inputs.empty"),
|
||||
ASSETS_DIAGNOSTICS_EMPTY("assets.diagnostics.empty"),
|
||||
ASSETS_DIAGNOSTICS_DIALOG_TITLE("assets.diagnostics.dialog.title"),
|
||||
ASSETS_DIAGNOSTICS_DIALOG_SUMMARY("assets.diagnostics.dialog.summary"),
|
||||
ASSETS_DIAGNOSTICS_LABEL_CATEGORY("assets.diagnostics.label.category"),
|
||||
ASSETS_DIAGNOSTICS_LABEL_EVIDENCE("assets.diagnostics.label.evidence"),
|
||||
ASSETS_DIAGNOSTICS_LABEL_BLOCKING("assets.diagnostics.label.blocking"),
|
||||
ASSETS_PREVIEW_EMPTY("assets.preview.empty"),
|
||||
ASSETS_PREVIEW_ZOOM("assets.preview.zoom"),
|
||||
ASSETS_PREVIEW_TEXT_ERROR("assets.preview.textError"),
|
||||
|
||||
@ -10,6 +10,7 @@ import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
import p.packer.dtos.PackerAssetActionAvailabilityDTO;
|
||||
import p.packer.dtos.PackerAssetDetailsDTO;
|
||||
import p.packer.dtos.PackerDiagnosticDTO;
|
||||
import p.packer.messages.*;
|
||||
import p.studio.Container;
|
||||
import p.studio.controls.forms.StudioFormEditScopeChangedEvent;
|
||||
@ -17,6 +18,7 @@ import p.studio.controls.forms.StudioSection;
|
||||
import p.studio.events.StudioWorkspaceEventBus;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
import p.studio.workspaces.assets.dialogs.AssetDiagnosticsDialog;
|
||||
import p.studio.workspaces.assets.details.bank.AssetDetailsBankCompositionControl;
|
||||
import p.studio.workspaces.assets.details.contract.AssetDetailsContractControl;
|
||||
import p.studio.workspaces.assets.details.summary.AssetDetailsSummaryControl;
|
||||
@ -162,7 +164,10 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
final var actionsResponse = workspaceService.getAssetActions(new GetAssetActionsRequest(
|
||||
projectReference.toPackerProjectContext(),
|
||||
assetReference));
|
||||
final AssetWorkspaceAssetDetails details = mapDetails(response.details(), actionsResponse.actions());
|
||||
final AssetWorkspaceAssetDetails details = mapDetails(
|
||||
response.details(),
|
||||
response.diagnostics(),
|
||||
actionsResponse.actions());
|
||||
Platform.runLater(() -> applyLoadedDetails(generation, assetReference, details));
|
||||
} catch (RuntimeException exception) {
|
||||
Platform.runLater(() -> applyLoadFailure(generation, assetReference, exception));
|
||||
@ -305,7 +310,7 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
final var visibleActions = viewState.selectedAssetDetails().actions().stream()
|
||||
.filter(AssetWorkspaceAssetAction::visible)
|
||||
.toList();
|
||||
if (visibleActions.isEmpty()) {
|
||||
if (visibleActions.isEmpty() && viewState.selectedAssetDetails().diagnostics().isEmpty()) {
|
||||
nodes.add(AssetDetailsUiSupport.createSectionMessage(Container.i18n().text(I18n.ASSETS_ACTIONS_EMPTY)));
|
||||
return nodes;
|
||||
}
|
||||
@ -315,6 +320,10 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
button.setOnAction(ignored -> executeAction(action));
|
||||
nodes.add(button);
|
||||
}
|
||||
final Button analyseButton = AssetDetailsUiSupport.createActionButton(Container.i18n().text(I18n.ASSETS_ACTION_ANALYSE));
|
||||
analyseButton.setDisable(viewState.selectedAssetDetails().diagnostics().isEmpty());
|
||||
analyseButton.setOnAction(ignored -> openDiagnosticsDialog());
|
||||
nodes.add(analyseButton);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@ -365,6 +374,17 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
Container.backgroundTasks().submit(() -> deleteSelectedAsset(assetReference));
|
||||
}
|
||||
|
||||
private void openDiagnosticsDialog() {
|
||||
if (viewState.selectedAssetDetails() == null || getScene() == null) {
|
||||
return;
|
||||
}
|
||||
AssetDiagnosticsDialog.showAndWait(
|
||||
getScene().getWindow(),
|
||||
projectReference,
|
||||
viewState.selectedAssetDetails().summary().assetName(),
|
||||
viewState.selectedAssetDetails().diagnostics());
|
||||
}
|
||||
|
||||
private void registerSelectedAsset(AssetReference assetReference) {
|
||||
try {
|
||||
final RegisterAssetResult result = Container.packer().workspaceService().registerAsset(new RegisterAssetRequest(
|
||||
@ -428,7 +448,14 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
|
||||
private AssetWorkspaceAssetDetails mapDetails(
|
||||
PackerAssetDetailsDTO details,
|
||||
java.util.List<PackerDiagnosticDTO> diagnostics,
|
||||
java.util.List<PackerAssetActionAvailabilityDTO> actions) {
|
||||
final java.util.List<PackerDiagnosticDTO> mergedDiagnostics = new java.util.ArrayList<>(details.diagnostics());
|
||||
for (PackerDiagnosticDTO diagnostic : diagnostics) {
|
||||
if (!mergedDiagnostics.contains(diagnostic)) {
|
||||
mergedDiagnostics.add(diagnostic);
|
||||
}
|
||||
}
|
||||
return new AssetWorkspaceAssetDetails(
|
||||
AssetListPackerMappings.mapSummary(details.summary()),
|
||||
actions.stream()
|
||||
@ -444,7 +471,8 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
details.codecConfigurationFieldsByCodec(),
|
||||
details.metadataFields(),
|
||||
mapBankComposition(details.bankComposition()),
|
||||
Map.copyOf(details.inputsByRole()));
|
||||
Map.copyOf(details.inputsByRole()),
|
||||
mergedDiagnostics);
|
||||
}
|
||||
|
||||
private AssetWorkspaceBankCompositionDetails mapBankComposition(p.packer.dtos.PackerBankCompositionDetailsDTO details) {
|
||||
|
||||
@ -0,0 +1,138 @@
|
||||
package p.studio.workspaces.assets.dialogs;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.Window;
|
||||
import p.packer.dtos.PackerDiagnosticDTO;
|
||||
import p.packer.messages.diagnostics.PackerDiagnosticSeverity;
|
||||
import p.studio.Container;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
import p.studio.workspaces.assets.details.AssetDetailsUiSupport;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class AssetDiagnosticsDialog {
|
||||
private final Stage stage;
|
||||
private final ProjectReference projectReference;
|
||||
private final String assetName;
|
||||
private final List<PackerDiagnosticDTO> diagnostics;
|
||||
|
||||
private AssetDiagnosticsDialog(
|
||||
Window owner,
|
||||
ProjectReference projectReference,
|
||||
String assetName,
|
||||
List<PackerDiagnosticDTO> diagnostics) {
|
||||
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
|
||||
this.assetName = Objects.requireNonNull(assetName, "assetName");
|
||||
this.diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
||||
this.stage = new Stage();
|
||||
stage.initOwner(owner);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
stage.setTitle(Container.i18n().text(I18n.ASSETS_DIAGNOSTICS_DIALOG_TITLE));
|
||||
stage.setScene(new Scene(buildRoot(), 720, 560));
|
||||
stage.getScene().getStylesheets().add(Container.theme().getDefaultTheme());
|
||||
}
|
||||
|
||||
public static void showAndWait(
|
||||
Window owner,
|
||||
ProjectReference projectReference,
|
||||
String assetName,
|
||||
List<PackerDiagnosticDTO> diagnostics) {
|
||||
new AssetDiagnosticsDialog(owner, projectReference, assetName, diagnostics).stage.showAndWait();
|
||||
}
|
||||
|
||||
private VBox buildRoot() {
|
||||
final Label title = new Label(Container.i18n().text(I18n.ASSETS_DIAGNOSTICS_DIALOG_TITLE));
|
||||
title.getStyleClass().add("studio-launcher-section-title");
|
||||
|
||||
final Label subtitle = new Label(Container.i18n().format(
|
||||
I18n.ASSETS_DIAGNOSTICS_DIALOG_SUMMARY,
|
||||
diagnostics.size(),
|
||||
assetName));
|
||||
subtitle.getStyleClass().add("studio-launcher-subtitle");
|
||||
subtitle.setWrapText(true);
|
||||
|
||||
final VBox diagnosticsContent = new VBox(10);
|
||||
diagnosticsContent.getStyleClass().add("assets-diagnostics-dialog-content");
|
||||
if (diagnostics.isEmpty()) {
|
||||
diagnosticsContent.getChildren().add(AssetDetailsUiSupport.createSectionMessage(
|
||||
Container.i18n().text(I18n.ASSETS_DIAGNOSTICS_EMPTY)));
|
||||
} else {
|
||||
diagnostics.forEach(diagnostic -> diagnosticsContent.getChildren().add(createDiagnosticCard(diagnostic)));
|
||||
}
|
||||
|
||||
final ScrollPane scrollPane = new ScrollPane(diagnosticsContent);
|
||||
scrollPane.setFitToWidth(true);
|
||||
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
scrollPane.getStyleClass().add("assets-diagnostics-dialog-scroll");
|
||||
|
||||
final Button closeButton = new Button();
|
||||
closeButton.textProperty().bind(Container.i18n().bind(I18n.WIZARD_CANCEL));
|
||||
closeButton.getStyleClass().addAll("studio-button", "studio-button-cancel");
|
||||
closeButton.setOnAction(ignored -> stage.close());
|
||||
|
||||
final HBox actions = new HBox(closeButton);
|
||||
actions.setAlignment(Pos.CENTER_RIGHT);
|
||||
|
||||
final VBox root = new VBox(16, title, subtitle, scrollPane, actions);
|
||||
root.setPadding(new Insets(24));
|
||||
VBox.setVgrow(scrollPane, Priority.ALWAYS);
|
||||
return root;
|
||||
}
|
||||
|
||||
private VBox createDiagnosticCard(PackerDiagnosticDTO diagnostic) {
|
||||
final Label severity = new Label(severityLabel(diagnostic));
|
||||
severity.getStyleClass().add("assets-details-diagnostic-severity");
|
||||
|
||||
final Label message = new Label(diagnostic.message());
|
||||
message.getStyleClass().add("assets-details-diagnostic-message");
|
||||
message.setWrapText(true);
|
||||
|
||||
final VBox card = new VBox(8, severity, message);
|
||||
card.getStyleClass().addAll("assets-details-diagnostic-card", severityToneClass(diagnostic.severity()));
|
||||
|
||||
card.getChildren().add(AssetDetailsUiSupport.createKeyValueRow(
|
||||
Container.i18n().text(I18n.ASSETS_DIAGNOSTICS_LABEL_CATEGORY),
|
||||
diagnostic.category().name()));
|
||||
card.getChildren().add(AssetDetailsUiSupport.createKeyValueRow(
|
||||
Container.i18n().text(I18n.ASSETS_DIAGNOSTICS_LABEL_BLOCKING),
|
||||
AssetDetailsUiSupport.booleanLabel(diagnostic.blocking())));
|
||||
card.getChildren().add(AssetDetailsUiSupport.createKeyValueRow(
|
||||
Container.i18n().text(I18n.ASSETS_DIAGNOSTICS_LABEL_EVIDENCE),
|
||||
evidenceLabel(diagnostic.evidencePath())));
|
||||
return card;
|
||||
}
|
||||
|
||||
private String severityLabel(PackerDiagnosticDTO diagnostic) {
|
||||
return diagnostic.blocking()
|
||||
? diagnostic.severity().name() + " / BLOCKING"
|
||||
: diagnostic.severity().name();
|
||||
}
|
||||
|
||||
private String severityToneClass(PackerDiagnosticSeverity severity) {
|
||||
return switch (severity) {
|
||||
case ERROR -> "assets-details-diagnostic-blocker";
|
||||
case WARNING -> "assets-details-diagnostic-warning";
|
||||
case INFO -> "assets-details-diagnostic-hint";
|
||||
};
|
||||
}
|
||||
|
||||
private String evidenceLabel(Path evidencePath) {
|
||||
if (evidencePath == null) {
|
||||
return "—";
|
||||
}
|
||||
return AssetDetailsUiSupport.projectRelativePath(projectReference, evidencePath);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package p.studio.workspaces.assets.messages;
|
||||
|
||||
import p.packer.dtos.PackerCodecConfigurationFieldDTO;
|
||||
import p.packer.dtos.PackerDiagnosticDTO;
|
||||
import p.packer.messages.assets.OutputCodecCatalog;
|
||||
import p.packer.messages.assets.OutputFormatCatalog;
|
||||
|
||||
@ -18,7 +19,8 @@ public record AssetWorkspaceAssetDetails(
|
||||
Map<OutputCodecCatalog, List<PackerCodecConfigurationFieldDTO>> codecConfigurationFieldsByCodec,
|
||||
List<PackerCodecConfigurationFieldDTO> metadataFields,
|
||||
AssetWorkspaceBankCompositionDetails bankComposition,
|
||||
Map<String, List<Path>> inputsByRole) {
|
||||
Map<String, List<Path>> inputsByRole,
|
||||
List<PackerDiagnosticDTO> diagnostics) {
|
||||
|
||||
public AssetWorkspaceAssetDetails {
|
||||
Objects.requireNonNull(summary, "summary");
|
||||
@ -30,5 +32,6 @@ public record AssetWorkspaceAssetDetails(
|
||||
metadataFields = List.copyOf(Objects.requireNonNull(metadataFields, "metadataFields"));
|
||||
bankComposition = Objects.requireNonNull(bankComposition, "bankComposition");
|
||||
inputsByRole = Map.copyOf(Objects.requireNonNull(inputsByRole, "inputsByRole"));
|
||||
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,6 +94,7 @@ assets.section.diagnostics=Diagnostics
|
||||
assets.section.actions=Actions
|
||||
assets.actions.empty=No actions available for this asset.
|
||||
assets.action.register=Register
|
||||
assets.action.analyse=Analyse
|
||||
assets.action.delete=Delete
|
||||
assets.deleteDialog.title=Delete Asset
|
||||
assets.deleteDialog.description=Type the asset name below to confirm deletion of {0}.
|
||||
@ -153,6 +154,11 @@ assets.progress.loadingDetails=Loading selected asset details...
|
||||
assets.logs.title=Logs
|
||||
assets.inputs.empty=No previewable inputs are currently declared for this asset.
|
||||
assets.diagnostics.empty=No diagnostics are currently attached to this asset.
|
||||
assets.diagnostics.dialog.title=Asset Diagnostics
|
||||
assets.diagnostics.dialog.summary={0} diagnostics for {1}
|
||||
assets.diagnostics.label.category=Category
|
||||
assets.diagnostics.label.evidence=Evidence
|
||||
assets.diagnostics.label.blocking=Blocking
|
||||
assets.preview.empty=Select an input to preview it here.
|
||||
assets.preview.zoom=Zoom
|
||||
assets.preview.textError=Unable to read this text-like input for preview.
|
||||
|
||||
@ -572,6 +572,19 @@
|
||||
-fx-spacing: 10;
|
||||
}
|
||||
|
||||
.assets-diagnostics-dialog-scroll {
|
||||
-fx-background-color: transparent;
|
||||
-fx-fit-to-width: true;
|
||||
}
|
||||
|
||||
.assets-diagnostics-dialog-scroll > .viewport {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.assets-diagnostics-dialog-content {
|
||||
-fx-spacing: 10;
|
||||
}
|
||||
|
||||
.assets-details-action-button {
|
||||
-fx-max-width: Infinity;
|
||||
-fx-padding: 6 10 6 10;
|
||||
|
||||
@ -1649,20 +1649,105 @@
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Studio",
|
||||
"message" : "Project ready",
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Studio",
|
||||
"message" : "Project loading started",
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Studio",
|
||||
"message" : "Project opened",
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: Bigode",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: Bigode",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
@ -2270,49 +2355,9 @@
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: Bigode",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
@ -2360,49 +2405,9 @@
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: Bigode",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
@ -2448,6 +2453,11 @@
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
@ -2488,14 +2498,4 @@
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
} ]
|
||||
@ -19,7 +19,28 @@
|
||||
"height" : 16,
|
||||
"paletteIndices" : "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAEBAQEBAQEBAQEBAQAAAAABAQEBAQEBAQEBAQEAAAAAAQEBAQEBAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAABAQEBAQEAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAABAQEBAQEBAQEAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAABAQEBAQEBAQEAAAAAAAAAAQEBAQEBAQEBAAAAAAAAAAEBAQEBAQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"diagnostics" : [ ]
|
||||
}, {
|
||||
"relative_path" : "wrong-palette.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 248,
|
||||
"last_modified" : 1773911050482,
|
||||
"fingerprint" : "15850f68547775866b01a0fe0b0012bb0243dec303ce1f9c3e02220e05b593e6",
|
||||
"metadata" : { },
|
||||
"diagnostics" : [ {
|
||||
"severity" : "ERROR",
|
||||
"category" : "STRUCTURAL",
|
||||
"message" : "Invalid tile dimensions for wrong-palette.png: expected 16x16 but got 32x32",
|
||||
"evidence_path" : "/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/studio/test-projects/main/assets/ui/atlas2/wrong-palette.png",
|
||||
"blocking" : true
|
||||
}, {
|
||||
"severity" : "ERROR",
|
||||
"category" : "STRUCTURAL",
|
||||
"message" : "Tile image exceeds color limit for wrong-palette.png: expected at most 15 colors for indices 1..15",
|
||||
"evidence_path" : "/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/studio/test-projects/main/assets/ui/atlas2/wrong-palette.png",
|
||||
"blocking" : true
|
||||
} ]
|
||||
} ]
|
||||
}, {
|
||||
"asset_id" : 7,
|
||||
@ -35,24 +56,21 @@
|
||||
"files" : [ ]
|
||||
}, {
|
||||
"asset_id" : 12,
|
||||
"contract_fingerprint" : "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"contract_fingerprint" : "d27435217b20e485d55447c55427995bec564b27481b76fd9d74798adf835005",
|
||||
"files" : [ {
|
||||
"relative_path" : "confirm.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 137,
|
||||
"last_modified" : 1773253076764,
|
||||
"fingerprint" : "aa7d241deabcebe29a6096e14eaf16fdc06cf06380c11a507620b00fc7bff094",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
"originalArgb8888" : [ -265674 ],
|
||||
"convertedRgb565" : [ -122 ]
|
||||
},
|
||||
"tile" : {
|
||||
"width" : 16,
|
||||
"height" : 16,
|
||||
"paletteIndices" : "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAEBAQEBAQEBAQEBAQAAAAABAQEBAQEBAQEBAQEAAAAAAQEBAQEBAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAABAQEBAQEAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAABAQEBAQEBAQEAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAABAQEBAQEBAQEAAAAAAAAAAQEBAQEBAQEBAAAAAAAAAAEBAQEBAQ=="
|
||||
}
|
||||
}
|
||||
"metadata" : { },
|
||||
"diagnostics" : [ {
|
||||
"severity" : "ERROR",
|
||||
"category" : "STRUCTURAL",
|
||||
"message" : "Invalid tile dimensions for confirm.png: expected 32x32 but got 16x16",
|
||||
"evidence_path" : "/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/studio/test-projects/main/assets/recovered/atlas2/confirm.png",
|
||||
"blocking" : true
|
||||
} ]
|
||||
} ]
|
||||
}, {
|
||||
"asset_id" : 13,
|
||||
|
||||
@ -7,13 +7,13 @@
|
||||
"output" : {
|
||||
"format" : "TILES/indexed_v1",
|
||||
"codec" : "NONE",
|
||||
"codec_configuration" : { }
|
||||
"codec_configuration" : { },
|
||||
"metadata" : {
|
||||
"tile_size" : "32x32"
|
||||
}
|
||||
},
|
||||
"preload" : {
|
||||
"enabled" : false
|
||||
},
|
||||
"artifacts" : [ {
|
||||
"file" : "confirm.png",
|
||||
"index" : 0
|
||||
} ]
|
||||
"artifacts" : [ ]
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
{
|
||||
"originalArgb8888" : [ -265674 ],
|
||||
"convertedRgb565" : [ 65414 ]
|
||||
"convertedRgb565" : [ 65414 ],
|
||||
"originalArgb8888" : [ -265674 ]
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"paletteIndices" : [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 ],
|
||||
"height" : 16,
|
||||
"width" : 16,
|
||||
"height" : 16
|
||||
"paletteIndices" : [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 ]
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"width" : 16,
|
||||
"height" : 16,
|
||||
"paletteIndices" : [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 ]
|
||||
"paletteIndices" : [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 ],
|
||||
"width" : 16
|
||||
}
|
||||
BIN
test-projects/main/assets/ui/atlas2/wrong-palette.png
Normal file
BIN
test-projects/main/assets/ui/atlas2/wrong-palette.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 248 B |
Loading…
x
Reference in New Issue
Block a user