asset metadata form added

This commit is contained in:
bQUARKz 2026-03-17 11:47:45 +00:00
parent f34c5a7691
commit 4050fcbcaa
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
17 changed files with 313 additions and 60 deletions

View File

@ -14,6 +14,7 @@ public record PackerAssetDetailsDTO(
OutputCodecCatalog outputCodec,
List<OutputCodecCatalog> availableOutputCodecs,
Map<OutputCodecCatalog, List<PackerCodecConfigurationFieldDTO>> codecConfigurationFieldsByCodec,
List<PackerCodecConfigurationFieldDTO> metadataFields,
Map<String, List<Path>> inputsByRole,
List<PackerDiagnosticDTO> diagnostics) {
@ -23,6 +24,7 @@ public record PackerAssetDetailsDTO(
outputCodec = Objects.requireNonNullElse(outputCodec, OutputCodecCatalog.UNKNOWN);
availableOutputCodecs = List.copyOf(Objects.requireNonNull(availableOutputCodecs, "availableOutputCodecs"));
codecConfigurationFieldsByCodec = Map.copyOf(Objects.requireNonNull(codecConfigurationFieldsByCodec, "codecConfigurationFieldsByCodec"));
metadataFields = List.copyOf(Objects.requireNonNull(metadataFields, "metadataFields"));
inputsByRole = Map.copyOf(Objects.requireNonNull(inputsByRole, "inputsByRole"));
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
}

View File

@ -10,12 +10,14 @@ public record UpdateAssetContractRequest(
AssetReference assetReference,
boolean preloadEnabled,
OutputCodecCatalog outputCodec,
Map<String, String> codecFieldValues) {
Map<String, String> codecFieldValues,
Map<String, String> metadataFieldValues) {
public UpdateAssetContractRequest {
Objects.requireNonNull(project, "project");
Objects.requireNonNull(assetReference, "assetReference");
outputCodec = Objects.requireNonNullElse(outputCodec, OutputCodecCatalog.UNKNOWN);
codecFieldValues = Map.copyOf(Objects.requireNonNullElse(codecFieldValues, Map.of()));
metadataFieldValues = Map.copyOf(Objects.requireNonNullElse(metadataFieldValues, Map.of()));
}
}

View File

@ -17,6 +17,7 @@ public record PackerAssetDeclaration(
Map<String, List<String>> inputsByRole,
OutputFormatCatalog outputFormat,
OutputCodecCatalog outputCodec,
Map<String, String> outputMetadata,
boolean preloadEnabled) {
public PackerAssetDeclaration {
@ -29,6 +30,7 @@ public record PackerAssetDeclaration(
inputsByRole = Map.copyOf(Objects.requireNonNull(inputsByRole, "inputsByRole"));
outputFormat = Objects.requireNonNull(outputFormat, "outputFormat");
outputCodec = Objects.requireNonNull(outputCodec, "outputCodec");
outputMetadata = Map.copyOf(Objects.requireNonNull(outputMetadata, "outputMetadata"));
if (StringUtils.isBlank(assetUuid) || StringUtils.isBlank(name)) {
throw new IllegalArgumentException("declaration fields must not be blank");
}

View File

@ -14,6 +14,7 @@ public record PackerAssetDetails(
OutputCodecCatalog outputCodec,
List<OutputCodecCatalog> availableOutputCodecs,
Map<OutputCodecCatalog, List<PackerCodecConfigurationField>> codecConfigurationFieldsByCodec,
List<PackerCodecConfigurationField> metadataFields,
Map<String, List<Path>> inputsByRole,
List<PackerDiagnostic> diagnostics) {
@ -23,6 +24,7 @@ public record PackerAssetDetails(
outputCodec = Objects.requireNonNullElse(outputCodec, OutputCodecCatalog.UNKNOWN);
availableOutputCodecs = List.copyOf(Objects.requireNonNull(availableOutputCodecs, "availableOutputCodecs"));
codecConfigurationFieldsByCodec = Map.copyOf(Objects.requireNonNull(codecConfigurationFieldsByCodec, "codecConfigurationFieldsByCodec"));
metadataFields = List.copyOf(Objects.requireNonNull(metadataFields, "metadataFields"));
inputsByRole = Map.copyOf(Objects.requireNonNull(inputsByRole, "inputsByRole"));
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
}

View File

@ -672,6 +672,17 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
}
codecConfigurationNode.put(normalizedFieldKey, Objects.requireNonNullElse(fieldValue, ""));
});
final ObjectNode metadataNode = mutableObject(outputNode, "metadata");
metadataNode.removeAll();
request.metadataFieldValues().entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> {
if (entry.getKey() == null || entry.getKey().isBlank()) {
return;
}
metadataNode.put(entry.getKey().trim(), Objects.requireNonNullElse(entry.getValue(), ""));
});
}
private ObjectNode mutableObject(ObjectNode parent, String fieldName) {

View File

@ -47,6 +47,7 @@ public final class PackerAssetDeclarationParser {
final Map<String, List<String>> inputsByRole = requiredInputs(root.path("inputs"), diagnostics, manifestPath);
final OutputFormatCatalog outputFormat = requiredOutputFormat(root.path("output"), diagnostics, manifestPath);
final OutputCodecCatalog outputCodec = requiredOutputCodec(root.path("output"), diagnostics, manifestPath);
final Map<String, String> outputMetadata = optionalOutputMetadata(root.path("output"), diagnostics, manifestPath);
final Boolean preloadEnabled = requiredBoolean(root.path("preload"), "enabled", diagnostics, manifestPath);
if (schemaVersion != null && schemaVersion != SUPPORTED_SCHEMA_VERSION) {
@ -71,6 +72,7 @@ public final class PackerAssetDeclarationParser {
inputsByRole,
outputFormat,
outputCodec,
outputMetadata,
preloadEnabled),
diagnostics);
}
@ -156,6 +158,53 @@ public final class PackerAssetDeclarationParser {
return outputCodec;
}
private Map<String, String> optionalOutputMetadata(
JsonNode outputNode,
List<PackerDiagnostic> diagnostics,
Path manifestPath) {
final JsonNode metadataNode = outputNode.path("metadata");
if (metadataNode.isMissingNode() || metadataNode.isNull()) {
return Map.of();
}
if (!metadataNode.isObject()) {
diagnostics.add(new PackerDiagnostic(
PackerDiagnosticSeverity.ERROR,
PackerDiagnosticCategory.STRUCTURAL,
"Field 'output.metadata' must be a JSON object.",
manifestPath,
true));
return Map.of();
}
final Map<String, String> metadata = new LinkedHashMap<>();
metadataNode.fields().forEachRemaining(entry -> {
final String key = Objects.requireNonNullElse(entry.getKey(), "").trim();
if (key.isBlank()) {
diagnostics.add(new PackerDiagnostic(
PackerDiagnosticSeverity.ERROR,
PackerDiagnosticCategory.STRUCTURAL,
"Field 'output.metadata' has an invalid empty key.",
manifestPath,
true));
return;
}
final JsonNode valueNode = entry.getValue();
if (valueNode == null || valueNode.isContainerNode()) {
diagnostics.add(new PackerDiagnostic(
PackerDiagnosticSeverity.ERROR,
PackerDiagnosticCategory.STRUCTURAL,
"Field 'output.metadata' values must be scalar (text/number/boolean).",
manifestPath,
true));
return;
}
metadata.put(key, valueNode.asText());
});
return Map.copyOf(metadata);
}
private Map<String, List<String>> requiredInputs(JsonNode inputsNode, List<PackerDiagnostic> diagnostics, Path manifestPath) {
if (!inputsNode.isObject()) {
diagnostics.add(missingOrInvalid("inputs", "object of input roles", manifestPath));

View File

@ -73,6 +73,7 @@ public final class PackerAssetDetailsService {
declaration.outputCodec(),
outputContract.availableCodecs(),
outputContract.codecConfigurationFieldsByCodec(),
metadataFields(declaration.outputMetadata()),
resolveInputs(resolved.assetRoot(), declaration.inputsByRole()),
diagnostics);
return new GetAssetDetailsResult(
@ -111,6 +112,7 @@ public final class PackerAssetDetailsService {
OutputCodecCatalog.UNKNOWN,
List.of(OutputCodecCatalog.NONE),
Map.of(OutputCodecCatalog.NONE, List.of()),
List.of(),
Map.of(),
diagnostics);
return new GetAssetDetailsResult(
@ -130,6 +132,18 @@ public final class PackerAssetDetailsService {
return Map.copyOf(resolved);
}
private List<PackerCodecConfigurationField> metadataFields(Map<String, String> outputMetadata) {
return outputMetadata.entrySet().stream()
.map(entry -> new PackerCodecConfigurationField(
entry.getKey(),
entry.getKey(),
PackerCodecConfigurationFieldType.TEXT,
entry.getValue(),
false,
List.of()))
.toList();
}
private List<PackerDiagnostic> identityMismatchDiagnostics(
final Optional<PackerRegistryEntry> registryEntry,
final PackerAssetDeclaration declaration,

View File

@ -30,6 +30,7 @@ public final class PackerReadMessageMapper {
details.outputCodec(),
details.availableOutputCodecs(),
toCodecConfigurationFieldsByCodecDTO(details.codecConfigurationFieldsByCodec()),
toCodecConfigurationFieldDTOs(details.metadataFields()),
details.inputsByRole(),
toDiagnosticDTOs(details.diagnostics()));
}
@ -74,6 +75,11 @@ public final class PackerReadMessageMapper {
field.options());
}
private static List<PackerCodecConfigurationFieldDTO> toCodecConfigurationFieldDTOs(
List<PackerCodecConfigurationField> fields) {
return fields.stream().map(PackerReadMessageMapper::toCodecConfigurationFieldDTO).toList();
}
private static PackerDiagnosticDTO toDiagnosticDTO(PackerDiagnostic diagnostic) {
return new PackerDiagnosticDTO(
diagnostic.severity(),

View File

@ -191,7 +191,10 @@ final class FileSystemPackerWorkspaceServiceTest {
OutputCodecCatalog.NONE,
Map.of(
"NONE:packMode", "tight",
"NONE:palette", "mono")));
"NONE:palette", "mono"),
Map.of(
"tile_size", "16",
"channels", "1")));
assertTrue(result.success());
assertNull(result.errorMessage());
@ -202,6 +205,8 @@ final class FileSystemPackerWorkspaceServiceTest {
assertEquals("NONE", manifest.path("output").path("codec").asText());
assertEquals("tight", manifest.path("output").path("codec_configuration").path("packMode").asText());
assertEquals("mono", manifest.path("output").path("codec_configuration").path("palette").asText());
assertEquals("16", manifest.path("output").path("metadata").path("tile_size").asText());
assertEquals("1", manifest.path("output").path("metadata").path("channels").asText());
}
@Test
@ -218,7 +223,8 @@ final class FileSystemPackerWorkspaceServiceTest {
AssetReference.forAssetId(1),
false,
OutputCodecCatalog.NONE,
Map.of("NONE:packMode", "tight")));
Map.of("NONE:packMode", "tight"),
Map.of()));
assertTrue(updateResult.success());
assertEquals(1, loader.loadCount());
@ -243,6 +249,7 @@ final class FileSystemPackerWorkspaceServiceTest {
AssetReference.forAssetId(1),
true,
OutputCodecCatalog.NONE,
Map.of(),
Map.of()));
assertFalse(result.success());

View File

@ -1,6 +1,7 @@
package p.packer.services;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import p.packer.messages.AssetReference;
@ -16,6 +17,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
@ -57,6 +59,30 @@ final class PackerAssetDetailsServiceTest {
assertEquals(List.of(), result.details().codecConfigurationFieldsByCodec().get(OutputCodecCatalog.NONE));
}
@Test
void exposesMetadataFieldsFromOutputMetadataContract() throws Exception {
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("managed-with-metadata"));
final Path manifestPath = projectRoot.resolve("assets/ui/atlas/asset.json");
final ObjectMapper mapper = new ObjectMapper();
final var manifest = mapper.readTree(manifestPath.toFile());
final ObjectNode output = (ObjectNode) manifest.path("output");
final ObjectNode metadata = output.putObject("metadata");
metadata.put("tile_size", "16");
metadata.put("channels", "1");
mapper.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
final PackerAssetDetailsService service = service();
final var result = service.getAssetDetails(new GetAssetDetailsRequest(project(projectRoot), AssetReference.forAssetId(1)));
assertEquals(PackerOperationStatus.SUCCESS, result.status());
assertEquals(
Map.of("tile_size", "16", "channels", "1"),
result.details().metadataFields().stream().collect(java.util.stream.Collectors.toMap(
field -> field.key(),
field -> field.value())));
}
@Test
void returnsInvalidDetailsForInvalidDeclaration() throws Exception {
final Path projectRoot = copyFixture("workspaces/invalid-missing-fields", tempDir.resolve("invalid"));

View File

@ -97,6 +97,7 @@ public enum I18n {
ASSETS_SECTION_SUMMARY("assets.section.summary"),
ASSETS_SECTION_RUNTIME_CONTRACT("assets.section.runtimeContract"),
ASSETS_SUBSECTION_CODEC_CONFIGURATION("assets.subsection.codecConfiguration"),
ASSETS_SUBSECTION_METADATA("assets.subsection.metadata"),
ASSETS_SECTION_INPUTS_PREVIEW("assets.section.inputsPreview"),
ASSETS_SECTION_DIAGNOSTICS("assets.section.diagnostics"),
ASSETS_SECTION_ACTIONS("assets.section.actions"),
@ -177,6 +178,7 @@ public enum I18n {
ASSETS_DETAILS_READY("assets.details.ready"),
ASSETS_DETAILS_NO_SELECTION("assets.details.noSelection"),
ASSETS_DETAILS_CODEC_CONFIGURATION_EMPTY("assets.details.codecConfiguration.empty"),
ASSETS_DETAILS_METADATA_EMPTY("assets.details.metadata.empty"),
ASSETS_ADD_WIZARD_TITLE("assets.addWizard.title"),
ASSETS_ADD_WIZARD_DESCRIPTION("assets.addWizard.description"),
ASSETS_ADD_WIZARD_STEP_ROOT_TITLE("assets.addWizard.step.root.title"),

View File

@ -416,6 +416,7 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
details.outputCodec(),
details.availableOutputCodecs(),
details.codecConfigurationFieldsByCodec(),
details.metadataFields(),
Map.copyOf(details.inputsByRole()));
}
}

View File

@ -14,45 +14,60 @@ public record AssetContractDraft(
boolean preload,
OutputFormatCatalog bank,
OutputCodecCatalog selectedCodec,
Map<String, String> codecFieldValues) {
Map<String, String> codecFieldValues,
Map<String, String> metadataFieldValues) {
public AssetContractDraft {
Objects.requireNonNull(assetReference, "assetReference");
bank = Objects.requireNonNullElse(bank, OutputFormatCatalog.UNKNOWN);
selectedCodec = Objects.requireNonNullElse(selectedCodec, OutputCodecCatalog.UNKNOWN);
codecFieldValues = Map.copyOf(Objects.requireNonNull(codecFieldValues, "codecFieldValues"));
metadataFieldValues = Map.copyOf(Objects.requireNonNull(metadataFieldValues, "metadataFieldValues"));
}
public static AssetContractDraft fromDetails(AssetWorkspaceAssetDetails details) {
final Map<String, String> codecFieldValues = new LinkedHashMap<>();
details.codecConfigurationFieldsByCodec().forEach((codec, fields) ->
fields.forEach(field -> codecFieldValues.put(keyOf(codec, field.key()), field.value())));
final Map<String, String> metadataFieldValues = new LinkedHashMap<>();
details.metadataFields().forEach(field -> metadataFieldValues.put(field.key(), field.value()));
return new AssetContractDraft(
details.summary().assetReference(),
details.summary().preload(),
details.outputFormat(),
details.outputCodec(),
codecFieldValues);
codecFieldValues,
metadataFieldValues);
}
public AssetContractDraft withPreload(boolean preload) {
return new AssetContractDraft(assetReference, preload, bank, selectedCodec, codecFieldValues);
return new AssetContractDraft(assetReference, preload, bank, selectedCodec, codecFieldValues, metadataFieldValues);
}
public AssetContractDraft withSelectedCodec(OutputCodecCatalog selectedCodec) {
return new AssetContractDraft(assetReference, preload, bank, selectedCodec, codecFieldValues);
return new AssetContractDraft(assetReference, preload, bank, selectedCodec, codecFieldValues, metadataFieldValues);
}
public AssetContractDraft withCodecFieldValue(OutputCodecCatalog codec, String fieldKey, String value) {
final Map<String, String> nextValues = new LinkedHashMap<>(codecFieldValues);
nextValues.put(keyOf(codec, fieldKey), Objects.requireNonNullElse(value, ""));
return new AssetContractDraft(assetReference, preload, bank, selectedCodec, nextValues);
return new AssetContractDraft(assetReference, preload, bank, selectedCodec, nextValues, metadataFieldValues);
}
public AssetContractDraft withMetadataFieldValue(String fieldKey, String value) {
final Map<String, String> nextValues = new LinkedHashMap<>(metadataFieldValues);
nextValues.put(fieldKey, Objects.requireNonNullElse(value, ""));
return new AssetContractDraft(assetReference, preload, bank, selectedCodec, codecFieldValues, nextValues);
}
public String codecFieldValue(OutputCodecCatalog codec, String fieldKey, String fallback) {
return codecFieldValues.getOrDefault(keyOf(codec, fieldKey), Objects.requireNonNullElse(fallback, ""));
}
public String metadataFieldValue(String fieldKey, String fallback) {
return metadataFieldValues.getOrDefault(fieldKey, Objects.requireNonNullElse(fallback, ""));
}
private static String keyOf(OutputCodecCatalog codec, String fieldKey) {
return codec.name() + ":" + fieldKey;
}

View File

@ -105,7 +105,8 @@ public final class AssetDetailsContractControl extends VBox implements StudioCon
createPreloadEditor(draft, editing)),
AssetDetailsUiSupport.createKeyValueRow(
Container.i18n().text(I18n.ASSETS_LABEL_BANK),
draft.bank()));
draft.bank()),
createMetadataSection(details, draft, editing));
final VBox codecColumn = new VBox(10);
codecColumn.getStyleClass().addAll("assets-details-contract-column", "assets-details-contract-codec-column");
@ -220,6 +221,110 @@ public final class AssetDetailsContractControl extends VBox implements StudioCon
return createCodecMetadataPane(title, content);
}
private Node createMetadataSection(
AssetWorkspaceAssetDetails details,
AssetContractDraft draft,
boolean editing) {
final VBox subsection = new VBox(8);
subsection.getStyleClass().add("assets-details-contract-metadata");
final Label title = new Label(Container.i18n().text(I18n.ASSETS_SUBSECTION_METADATA));
title.getStyleClass().add("assets-details-subsection-title");
final VBox content = new VBox(8);
content.getStyleClass().add("assets-details-contract-metadata-content");
if (details.metadataFields().isEmpty()) {
content.getChildren().add(AssetDetailsUiSupport.createSectionMessage(
Container.i18n().text(I18n.ASSETS_DETAILS_METADATA_EMPTY)));
return createCodecMetadataPane(title, content);
}
for (PackerCodecConfigurationFieldDTO field : details.metadataFields()) {
content.getChildren().add(createMetadataFieldRow(field, draft, editing));
}
return createCodecMetadataPane(title, content);
}
private Node createMetadataFieldRow(
PackerCodecConfigurationFieldDTO field,
AssetContractDraft draft,
boolean editing) {
final Node valueNode = switch (field.fieldType()) {
case BOOLEAN -> createMetadataBooleanField(field, draft, editing);
case ENUM -> createMetadataEnumField(field, draft, editing);
case INTEGER, TEXT -> createMetadataTextField(field, draft, editing);
};
return AssetDetailsUiSupport.createKeyValueRow(field.label(), valueNode);
}
private Node createMetadataBooleanField(
PackerCodecConfigurationFieldDTO field,
AssetContractDraft draft,
boolean editing) {
final CheckBox checkBox = new CheckBox();
checkBox.getStyleClass().add("assets-details-readonly-check");
checkBox.setSelected(Boolean.parseBoolean(currentMetadataValue(field, draft)));
if (editing) {
checkBox.selectedProperty().addListener((ignored, oldValue, newValue) -> {
if (!Objects.equals(oldValue, newValue)) {
formSession.updateDraft(current -> current.withMetadataFieldValue(
field.key(),
Boolean.toString(newValue)));
actionBar.updateState(formSession.mode(), formSession.isDirty());
}
});
} else {
configureReadOnlyControl(checkBox);
}
return checkBox;
}
private Node createMetadataEnumField(
PackerCodecConfigurationFieldDTO field,
AssetContractDraft draft,
boolean editing) {
final ComboBox<String> comboBox = new ComboBox<>(FXCollections.observableArrayList(field.options()));
comboBox.getStyleClass().add("assets-details-combo");
comboBox.setMaxWidth(Double.MAX_VALUE);
comboBox.getSelectionModel().select(currentMetadataValue(field, draft));
if (editing) {
comboBox.valueProperty().addListener((ignored, oldValue, newValue) -> {
if (newValue != null && !Objects.equals(oldValue, newValue)) {
formSession.updateDraft(current -> current.withMetadataFieldValue(
field.key(),
newValue));
actionBar.updateState(formSession.mode(), formSession.isDirty());
}
});
} else {
configureReadOnlyControl(comboBox);
}
return comboBox;
}
private Node createMetadataTextField(
PackerCodecConfigurationFieldDTO field,
AssetContractDraft draft,
boolean editing) {
final TextField textField = new TextField(currentMetadataValue(field, draft));
textField.getStyleClass().add("assets-workspace-search");
textField.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(textField, Priority.ALWAYS);
if (editing) {
textField.textProperty().addListener((ignored, oldValue, newValue) -> {
if (!Objects.equals(oldValue, newValue)) {
formSession.updateDraft(current -> current.withMetadataFieldValue(
field.key(),
Objects.requireNonNullElse(newValue, "")));
actionBar.updateState(formSession.mode(), formSession.isDirty());
}
});
} else {
textField.setEditable(false);
configureReadOnlyControl(textField);
}
return textField;
}
private Node createCodecMetadataPane(Label title, VBox content) {
final ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setFitToWidth(true);
@ -320,6 +425,10 @@ public final class AssetDetailsContractControl extends VBox implements StudioCon
return draft.codecFieldValue(draft.selectedCodec(), field.key(), field.value());
}
private String currentMetadataValue(PackerCodecConfigurationFieldDTO field, AssetContractDraft draft) {
return draft.metadataFieldValue(field.key(), field.value());
}
private void configureReadOnlyControl(javafx.scene.control.Control control) {
control.setMouseTransparent(true);
control.setFocusTraversable(false);
@ -344,7 +453,8 @@ public final class AssetDetailsContractControl extends VBox implements StudioCon
draft.assetReference(),
draft.preload(),
draft.selectedCodec(),
draft.codecFieldValues());
draft.codecFieldValues(),
draft.metadataFieldValues());
try {
final var response = Container.packer().workspaceService().updateAssetContract(request);
if (response.success()) {

View File

@ -16,6 +16,7 @@ public record AssetWorkspaceAssetDetails(
OutputCodecCatalog outputCodec,
List<OutputCodecCatalog> availableOutputCodecs,
Map<OutputCodecCatalog, List<PackerCodecConfigurationFieldDTO>> codecConfigurationFieldsByCodec,
List<PackerCodecConfigurationFieldDTO> metadataFields,
Map<String, List<Path>> inputsByRole) {
public AssetWorkspaceAssetDetails {
@ -25,6 +26,7 @@ public record AssetWorkspaceAssetDetails(
outputCodec = Objects.requireNonNullElse(outputCodec, OutputCodecCatalog.UNKNOWN);
availableOutputCodecs = List.copyOf(Objects.requireNonNull(availableOutputCodecs, "availableOutputCodecs"));
codecConfigurationFieldsByCodec = Map.copyOf(Objects.requireNonNull(codecConfigurationFieldsByCodec, "codecConfigurationFieldsByCodec"));
metadataFields = List.copyOf(Objects.requireNonNull(metadataFields, "metadataFields"));
inputsByRole = Map.copyOf(Objects.requireNonNull(inputsByRole, "inputsByRole"));
}
}

View File

@ -87,6 +87,7 @@ assets.badge.diagnostics=Diagnostics
assets.section.summary=Summary
assets.section.runtimeContract=Runtime Contract
assets.subsection.codecConfiguration=Codec Configuration
assets.subsection.metadata=Metadata
assets.section.inputsPreview=Inputs / Preview
assets.section.diagnostics=Diagnostics
assets.section.actions=Actions
@ -168,6 +169,7 @@ assets.details.empty=Create or add assets to this project to start using the Ass
assets.details.ready=Selected asset: {0}\nState: {1}\nRoot: {2}
assets.details.noSelection=Select an asset from the navigator once assets are available.
assets.details.codecConfiguration.empty=This codec does not expose configuration fields yet.
assets.details.metadata.empty=This asset does not expose metadata fields yet.
assets.addWizard.title=Add Asset
assets.addWizard.description=Create a registered asset root through a guided flow.
assets.addWizard.step.root.title=Choose Asset Root

View File

@ -748,6 +748,56 @@
"message" : "Asset scan started",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "8 assets loaded",
"severity" : "SUCCESS",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: test",
"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" : "Asset moved: bbb2 -> recovered/bbb2",
@ -2448,54 +2498,4 @@
"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: 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: test",
"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: bla",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: one-more-atlas",
"severity" : "INFO",
"sticky" : false
} ]