asset details (WIP)

This commit is contained in:
bQUARKz 2026-03-19 09:28:06 +00:00
parent c0051e62cb
commit 9cf59c3c1f
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
5 changed files with 292 additions and 114 deletions

View File

@ -314,16 +314,27 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
nodes.add(AssetDetailsUiSupport.createSectionMessage(Container.i18n().text(I18n.ASSETS_ACTIONS_EMPTY)));
return nodes;
}
for (AssetWorkspaceAssetAction action : visibleActions) {
final Button analyseButton = AssetDetailsUiSupport.createActionButton(Container.i18n().text(I18n.ASSETS_ACTION_ANALYSE));
AssetDetailsUiSupport.applyActionTone(analyseButton, "studio-button-warning");
analyseButton.setDisable(actionRunning || viewState.selectedAssetDetails().diagnostics().isEmpty());
analyseButton.setOnAction(ignored -> openDiagnosticsDialog());
nodes.add(analyseButton);
final var orderedActions = visibleActions.stream()
.sorted(java.util.Comparator.comparingInt(action -> switch (action.action()) {
case DELETE -> 2;
default -> 1;
}))
.toList();
for (AssetWorkspaceAssetAction action : orderedActions) {
final Button button = AssetDetailsUiSupport.createActionButton(AssetDetailsUiSupport.actionLabel(action.action()));
if (action.action() == p.packer.messages.assets.AssetAction.DELETE) {
AssetDetailsUiSupport.applyActionTone(button, "studio-button-danger");
}
button.setDisable(actionRunning || !action.enabled());
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;
}

View File

@ -81,6 +81,14 @@ public final class AssetDetailsUiSupport {
return button;
}
public static void applyActionTone(Button button, String toneClass) {
button.getStyleClass().removeAll(
"studio-button-primary",
"studio-button-warning",
"studio-button-danger");
button.getStyleClass().add(toneClass);
}
public static String registrationLabel(AssetWorkspaceAssetState state) {
return switch (state) {
case REGISTERED -> Container.i18n().text(I18n.ASSETS_VALUE_REGISTERED);

View File

@ -20,14 +20,23 @@ import p.studio.utilities.i18n.I18n;
import p.studio.workspaces.assets.details.AssetDetailsUiSupport;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public final class AssetDiagnosticsDialog {
private static final String GENERAL_GROUP_KEY = "__general__";
private final Stage stage;
private final ProjectReference projectReference;
private final String assetName;
private final List<PackerDiagnosticDTO> diagnostics;
private final Map<String, DiagnosticGroup> diagnosticsByGroup;
private final VBox groupList = new VBox(6);
private final VBox diagnosticsContent = new VBox(10);
private String selectedGroupKey;
private AssetDiagnosticsDialog(
Window owner,
@ -37,6 +46,10 @@ public final class AssetDiagnosticsDialog {
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
this.assetName = Objects.requireNonNull(assetName, "assetName");
this.diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
this.diagnosticsByGroup = buildGroups(this.diagnostics);
this.selectedGroupKey = diagnosticsByGroup.containsKey(GENERAL_GROUP_KEY)
? GENERAL_GROUP_KEY
: diagnosticsByGroup.keySet().stream().findFirst().orElse(GENERAL_GROUP_KEY);
this.stage = new Stage();
stage.initOwner(owner);
stage.initModality(Modality.WINDOW_MODAL);
@ -64,20 +77,25 @@ public final class AssetDiagnosticsDialog {
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");
groupList.getStyleClass().add("assets-diagnostics-dialog-groups");
final ScrollPane groupScroll = new ScrollPane(groupList);
groupScroll.setFitToWidth(true);
groupScroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
groupScroll.getStyleClass().add("assets-diagnostics-dialog-groups-scroll");
final HBox browser = new HBox(16, groupScroll, scrollPane);
browser.getStyleClass().add("assets-diagnostics-dialog-browser");
HBox.setHgrow(scrollPane, Priority.ALWAYS);
renderGroups();
renderSelectedGroup();
final Button closeButton = new Button();
closeButton.textProperty().bind(Container.i18n().bind(I18n.WIZARD_CANCEL));
closeButton.getStyleClass().addAll("studio-button", "studio-button-cancel");
@ -86,12 +104,49 @@ public final class AssetDiagnosticsDialog {
final HBox actions = new HBox(closeButton);
actions.setAlignment(Pos.CENTER_RIGHT);
final VBox root = new VBox(16, title, subtitle, scrollPane, actions);
final VBox root = new VBox(16, title, subtitle, browser, actions);
root.setPadding(new Insets(24));
VBox.setVgrow(scrollPane, Priority.ALWAYS);
VBox.setVgrow(browser, Priority.ALWAYS);
return root;
}
private void renderGroups() {
groupList.getChildren().clear();
if (diagnosticsByGroup.isEmpty()) {
return;
}
for (DiagnosticGroup group : diagnosticsByGroup.values()) {
final Button button = new Button(group.title() + " (" + group.diagnostics().size() + ")");
button.getStyleClass().addAll("assets-diagnostics-dialog-group-button");
if (Objects.equals(selectedGroupKey, group.key())) {
button.getStyleClass().add("assets-diagnostics-dialog-group-button-selected");
}
button.setMaxWidth(Double.MAX_VALUE);
button.setAlignment(Pos.CENTER_LEFT);
button.setOnAction(ignored -> {
selectedGroupKey = group.key();
renderGroups();
renderSelectedGroup();
});
groupList.getChildren().add(button);
}
}
private void renderSelectedGroup() {
diagnosticsContent.getChildren().clear();
final DiagnosticGroup group = diagnosticsByGroup.get(selectedGroupKey);
if (group == null || group.diagnostics().isEmpty()) {
diagnosticsContent.getChildren().add(AssetDetailsUiSupport.createSectionMessage(
Container.i18n().text(I18n.ASSETS_DIAGNOSTICS_EMPTY)));
return;
}
final Label groupTitle = new Label(group.title());
groupTitle.getStyleClass().add("assets-diagnostics-dialog-group-title");
diagnosticsContent.getChildren().add(groupTitle);
group.diagnostics().forEach(diagnostic -> diagnosticsContent.getChildren().add(createDiagnosticCard(diagnostic)));
}
private VBox createDiagnosticCard(PackerDiagnosticDTO diagnostic) {
final Label severity = new Label(severityLabel(diagnostic));
severity.getStyleClass().add("assets-details-diagnostic-severity");
@ -135,4 +190,58 @@ public final class AssetDiagnosticsDialog {
}
return AssetDetailsUiSupport.projectRelativePath(projectReference, evidencePath);
}
private Map<String, DiagnosticGroup> buildGroups(List<PackerDiagnosticDTO> diagnostics) {
final Map<String, DiagnosticGroup> grouped = new LinkedHashMap<>();
for (PackerDiagnosticDTO diagnostic : diagnostics) {
final String key = groupKey(diagnostic.evidencePath());
final String title = groupTitle(key, diagnostic.evidencePath());
grouped.computeIfAbsent(key, ignored -> new DiagnosticGroup(key, title, new java.util.ArrayList<>()))
.diagnostics()
.add(diagnostic);
}
final Map<String, DiagnosticGroup> ordered = new LinkedHashMap<>();
ordered.put(GENERAL_GROUP_KEY, grouped.getOrDefault(
GENERAL_GROUP_KEY,
new DiagnosticGroup(GENERAL_GROUP_KEY, "General", new java.util.ArrayList<>())));
for (Map.Entry<String, DiagnosticGroup> entry : grouped.entrySet()) {
if (!GENERAL_GROUP_KEY.equals(entry.getKey())) {
ordered.put(entry.getKey(), entry.getValue());
}
}
return ordered.entrySet().stream()
.filter(entry -> !entry.getValue().diagnostics().isEmpty() || GENERAL_GROUP_KEY.equals(entry.getKey()))
.collect(java.util.stream.Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(left, right) -> left,
LinkedHashMap::new));
}
private String groupKey(Path evidencePath) {
if (evidencePath == null) {
return GENERAL_GROUP_KEY;
}
final String relativePath = AssetDetailsUiSupport.projectRelativePath(projectReference, evidencePath);
if (relativePath.endsWith("/asset.json") || relativePath.equals("asset.json")) {
return GENERAL_GROUP_KEY;
}
return relativePath;
}
private String groupTitle(String key, Path evidencePath) {
if (GENERAL_GROUP_KEY.equals(key)) {
return "General";
}
if (evidencePath == null) {
return "General";
}
return evidencePath.getFileName() == null ? key : evidencePath.getFileName().toString();
}
private record DiagnosticGroup(
String key,
String title,
List<PackerDiagnosticDTO> diagnostics) {
}
}

View File

@ -585,6 +585,56 @@
-fx-spacing: 10;
}
.assets-diagnostics-dialog-browser {
-fx-alignment: top-left;
}
.assets-diagnostics-dialog-groups-scroll {
-fx-background-color: #0f1318;
-fx-background-radius: 10;
-fx-border-color: #2f3a47;
-fx-border-radius: 10;
-fx-min-width: 220;
-fx-pref-width: 220;
-fx-max-width: 220;
}
.assets-diagnostics-dialog-groups-scroll > .viewport {
-fx-background-color: transparent;
}
.assets-diagnostics-dialog-groups {
-fx-padding: 10;
-fx-spacing: 6;
}
.assets-diagnostics-dialog-group-button {
-fx-background-color: #141b22;
-fx-border-color: #2f3a47;
-fx-border-radius: 8;
-fx-background-radius: 8;
-fx-text-fill: #d9e5f1;
-fx-padding: 8 10 8 10;
-fx-font-size: 12px;
}
.assets-diagnostics-dialog-group-button:hover {
-fx-background-color: #1a242d;
-fx-border-color: #49627a;
}
.assets-diagnostics-dialog-group-button-selected {
-fx-background-color: #203549;
-fx-border-color: #6ca8d8;
-fx-text-fill: #f4fbff;
}
.assets-diagnostics-dialog-group-title {
-fx-text-fill: #eef4fb;
-fx-font-size: 14px;
-fx-font-weight: bold;
}
.assets-details-action-button {
-fx-max-width: Infinity;
-fx-padding: 6 10 6 10;

View File

@ -58,6 +58,106 @@
"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",
"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",
"severity" : "SUCCESS",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Asset scan diagnostics updated.",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: sound",
@ -2398,104 +2498,4 @@
"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",
"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
} ]