From 95dafee419ecdbd62f8515cf7be2d3573b567de8 Mon Sep 17 00:00:00 2001
From: ABKQPO <93412322+ABKQPO@users.noreply.github.com>
Date: Wed, 30 Sep 2026 06:23:05 +0800
Subject: [PATCH] bugfix
---
.../guide/document/block/LytFileTree.java | 29 +-
.../document/block/LytMermaidFlowchart.java | 3 +
.../document/block/LytMermaidMindmap.java | 2 +
.../document/block/chart/LytBarChart.java | 16 +
.../document/block/chart/LytChartBase.java | 8 +
.../document/block/chart/LytColumnChart.java | 16 +
.../document/block/chart/LytLineChart.java | 20 +
.../document/block/chart/LytPieChart.java | 8 +
.../document/block/chart/LytScatterChart.java | 8 +
.../site/GuideSitePageAssetExporter.java | 6 +
.../GuideSiteSceneAnnotationSerializer.java | 1063 ++++++++++++++++-
.../guide/siteexport/site/SiteChartAxis.java | 16 +
.../assets/guidenh/siteexport/app.css | 34 +
13 files changed, 1202 insertions(+), 27 deletions(-)
diff --git a/src/main/java/com/hfstudio/guidenh/guide/document/block/LytFileTree.java b/src/main/java/com/hfstudio/guidenh/guide/document/block/LytFileTree.java
index 37538faee..bfe92912a 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/document/block/LytFileTree.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/document/block/LytFileTree.java
@@ -64,6 +64,10 @@ public boolean isEmpty() {
return rows.isEmpty();
}
+ public List getRows() {
+ return List.copyOf(rows);
+ }
+
@Override
public List extends LytNode> getChildren() {
return childNodes;
@@ -228,19 +232,32 @@ private static void drawHorizontalLine(RenderContext context, int xStart, int xE
context.fillRect(new LytRect(left, y, width, CONNECTOR_THICKNESS), color);
}
- private static class Row {
+ public static class Row {
- final List slots;
+ private final List slots;
@Nullable
- final LytBlock iconBlock;
- final LytParagraph payload;
- int rowY;
- int rowHeight;
+ private final LytBlock iconBlock;
+ private final LytParagraph payload;
+ private int rowY;
+ private int rowHeight;
Row(List slots, @Nullable LytBlock iconBlock, LytParagraph payload) {
this.slots = slots;
this.iconBlock = iconBlock;
this.payload = payload;
}
+
+ public List getSlots() {
+ return slots;
+ }
+
+ @Nullable
+ public LytBlock getIconBlock() {
+ return iconBlock;
+ }
+
+ public LytParagraph getPayload() {
+ return payload;
+ }
}
}
diff --git a/src/main/java/com/hfstudio/guidenh/guide/document/block/LytMermaidFlowchart.java b/src/main/java/com/hfstudio/guidenh/guide/document/block/LytMermaidFlowchart.java
index b64b8f8a2..a19cc8706 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/document/block/LytMermaidFlowchart.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/document/block/LytMermaidFlowchart.java
@@ -24,6 +24,8 @@ public class LytMermaidFlowchart extends LytVBox implements InteractiveElement {
@Getter
private final LytCodeBlockToolbar toolbar = new LytCodeBlockToolbar();
private final LytMermaidFlowchartCanvas canvas;
+ @Getter
+ private final Map nodeContent;
public LytMermaidFlowchart(FlowchartDocument flowchart, String sourceText) {
this(flowchart, sourceText, Collections.emptyMap());
@@ -32,6 +34,7 @@ public LytMermaidFlowchart(FlowchartDocument flowchart, String sourceText) {
public LytMermaidFlowchart(FlowchartDocument flowchart, String sourceText, Map nodeContent) {
this.flowchart = flowchart;
this.sourceText = sourceText != null ? sourceText : "";
+ this.nodeContent = nodeContent != null ? Map.copyOf(nodeContent) : Collections.emptyMap();
this.canvas = new LytMermaidFlowchartCanvas(
flowchart,
nodeContent != null ? nodeContent : Collections.emptyMap());
diff --git a/src/main/java/com/hfstudio/guidenh/guide/document/block/LytMermaidMindmap.java b/src/main/java/com/hfstudio/guidenh/guide/document/block/LytMermaidMindmap.java
index 57cc17659..462559033 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/document/block/LytMermaidMindmap.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/document/block/LytMermaidMindmap.java
@@ -22,6 +22,7 @@ public class LytMermaidMindmap extends LytVBox implements InteractiveElement {
private final String sourceText;
private final LytCodeBlockToolbar toolbar = new LytCodeBlockToolbar();
private final LytMermaidMindmapCanvas canvas;
+ private final Map nodeContent;
public LytMermaidMindmap(MindmapDocument mindmap, String sourceText) {
this(mindmap, sourceText, Collections.emptyMap());
@@ -30,6 +31,7 @@ public LytMermaidMindmap(MindmapDocument mindmap, String sourceText) {
public LytMermaidMindmap(MindmapDocument mindmap, String sourceText, Map nodeContent) {
this.mindmap = mindmap;
this.sourceText = sourceText != null ? sourceText : "";
+ this.nodeContent = nodeContent != null ? Map.copyOf(nodeContent) : Collections.emptyMap();
this.canvas = new LytMermaidMindmapCanvas(mindmap, nodeContent != null ? nodeContent : Collections.emptyMap());
setPadding(6);
diff --git a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytBarChart.java b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytBarChart.java
index 06221ae7d..d4f69b6f0 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytBarChart.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytBarChart.java
@@ -43,6 +43,22 @@ public class LytBarChart extends LytChartBase implements DebugComponent {
private ChartAxisOptions yAxis = new ChartAxisOptions();
private float barWidthRatio = 0.7f;
+ public String[] getCategories() {
+ return categories.clone();
+ }
+
+ public ChartAxisOptions getXAxis() {
+ return xAxis;
+ }
+
+ public ChartAxisOptions getYAxis() {
+ return yAxis;
+ }
+
+ public float getBarWidthRatio() {
+ return barWidthRatio;
+ }
+
private LytRect plotCache = LytRect.empty();
private AxisRange xRangeCache;
diff --git a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytChartBase.java b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytChartBase.java
index ae221814e..995198216 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytChartBase.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytChartBase.java
@@ -70,6 +70,14 @@ public abstract class LytChartBase extends LytBlock implements InteractiveElemen
@Setter
private int cornerLegendBackgroundColor = CornerLegendRenderer.DEFAULT_BACKGROUND;
+ public int getExplicitWidth() {
+ return explicitWidth;
+ }
+
+ public int getExplicitHeight() {
+ return explicitHeight;
+ }
+
/** Currently hovered hit key; {@code -1} means none. The exact semantics is decided by each subclass. */
protected int hoveredKey = -1;
diff --git a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytColumnChart.java b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytColumnChart.java
index c8a92d206..76d6d6e14 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytColumnChart.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytColumnChart.java
@@ -43,6 +43,22 @@ public class LytColumnChart extends LytChartBase implements DebugComponent {
private ChartAxisOptions yAxis = new ChartAxisOptions();
private float barWidthRatio = 0.7f;
+ public String[] getCategories() {
+ return categories.clone();
+ }
+
+ public ChartAxisOptions getXAxis() {
+ return xAxis;
+ }
+
+ public ChartAxisOptions getYAxis() {
+ return yAxis;
+ }
+
+ public float getBarWidthRatio() {
+ return barWidthRatio;
+ }
+
private LytRect plotCache = LytRect.empty();
private AxisRange yRangeCache;
diff --git a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytLineChart.java b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytLineChart.java
index 76f30a59b..8f7fc6495 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytLineChart.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytLineChart.java
@@ -35,6 +35,26 @@ public class LytLineChart extends LytChartBase {
@Setter
private boolean showPoints = true;
+ public String[] getCategories() {
+ return categories.clone();
+ }
+
+ public ChartAxisOptions getXAxis() {
+ return xAxis;
+ }
+
+ public ChartAxisOptions getYAxis() {
+ return yAxis;
+ }
+
+ public boolean isNumericX() {
+ return numericX;
+ }
+
+ public boolean isShowPoints() {
+ return showPoints;
+ }
+
private LytRect plotCache = LytRect.empty();
private AxisRange xRangeCache;
private AxisRange yRangeCache;
diff --git a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytPieChart.java b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytPieChart.java
index adb5af01b..bfe41ca19 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytPieChart.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytPieChart.java
@@ -28,6 +28,14 @@ public class LytPieChart extends LytChartBase implements DebugComponent {
@Setter
private boolean clockwise = true;
+ public float getStartAngleDeg() {
+ return startAngleDeg;
+ }
+
+ public boolean isClockwise() {
+ return clockwise;
+ }
+
private float cxCache;
private float cyCache;
private float radiusCache;
diff --git a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytScatterChart.java b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytScatterChart.java
index bc1cb94f8..7d3c13c07 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytScatterChart.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/document/block/chart/LytScatterChart.java
@@ -24,6 +24,14 @@ public class LytScatterChart extends LytChartBase {
private ChartAxisOptions xAxis = new ChartAxisOptions();
private ChartAxisOptions yAxis = new ChartAxisOptions();
+ public ChartAxisOptions getXAxis() {
+ return xAxis;
+ }
+
+ public ChartAxisOptions getYAxis() {
+ return yAxis;
+ }
+
private LytRect plotCache = LytRect.empty();
private AxisRange xRangeCache;
private AxisRange yRangeCache;
diff --git a/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/GuideSitePageAssetExporter.java b/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/GuideSitePageAssetExporter.java
index 99a6cbe90..899e2121a 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/GuideSitePageAssetExporter.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/GuideSitePageAssetExporter.java
@@ -22,12 +22,18 @@ public interface AssetLoader {
private final GuideSiteAssetRegistry assets;
private final AssetLoader assetLoader;
+ private final GuideSiteLatexExporter latexExporter;
private final Map exportedResourcePaths = new HashMap<>();
private final Map exportedSoundPaths = new HashMap<>();
public GuideSitePageAssetExporter(GuideSiteAssetRegistry assets, AssetLoader assetLoader) {
this.assets = assets;
this.assetLoader = assetLoader;
+ this.latexExporter = new GuideSiteLatexExporter(assets);
+ }
+
+ public GuideSiteLatexExporter latexExporter() {
+ return latexExporter;
}
public String resolveImageSrc(String rawUrl, @Nullable ResourceLocation currentPageId) {
diff --git a/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/GuideSiteSceneAnnotationSerializer.java b/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/GuideSiteSceneAnnotationSerializer.java
index a21f27692..072c32fd9 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/GuideSiteSceneAnnotationSerializer.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/GuideSiteSceneAnnotationSerializer.java
@@ -4,11 +4,13 @@
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ResourceLocation;
import org.jetbrains.annotations.Nullable;
@@ -21,19 +23,55 @@
import com.hfstudio.guidenh.guide.PageAnchor;
import com.hfstudio.guidenh.guide.color.ColorUtils;
import com.hfstudio.guidenh.guide.color.ColorValue;
+import com.hfstudio.guidenh.guide.compiler.GuideItemReferenceResolver;
+import com.hfstudio.guidenh.guide.compiler.tags.BlockImageCompiler.BlockImagePlaceholder;
+import com.hfstudio.guidenh.guide.compiler.tags.ItemImageCompiler.ItemImagePlaceholder;
+import com.hfstudio.guidenh.guide.compiler.tags.MermaidCompiler.MermaidPlaceholder;
+import com.hfstudio.guidenh.guide.document.block.ImageRegionAnnotation;
+import com.hfstudio.guidenh.guide.document.block.LytAlertBox;
+import com.hfstudio.guidenh.guide.document.block.LytAlignedBlock;
import com.hfstudio.guidenh.guide.document.block.LytBlock;
+import com.hfstudio.guidenh.guide.document.block.LytCodeBlock;
+import com.hfstudio.guidenh.guide.document.block.LytContentTabsBlock;
+import com.hfstudio.guidenh.guide.document.block.LytDetailsBlock;
import com.hfstudio.guidenh.guide.document.block.LytDocument;
+import com.hfstudio.guidenh.guide.document.block.LytDocumentFloat;
+import com.hfstudio.guidenh.guide.document.block.LytFileTree;
+import com.hfstudio.guidenh.guide.document.block.LytGuiSprite;
+import com.hfstudio.guidenh.guide.document.block.LytHBox;
import com.hfstudio.guidenh.guide.document.block.LytHeading;
import com.hfstudio.guidenh.guide.document.block.LytImage;
+import com.hfstudio.guidenh.guide.document.block.LytImageBlock;
import com.hfstudio.guidenh.guide.document.block.LytItemGrid;
import com.hfstudio.guidenh.guide.document.block.LytItemImage;
+import com.hfstudio.guidenh.guide.document.block.LytLatexBlock;
+import com.hfstudio.guidenh.guide.document.block.LytLatexDisplayBlock;
import com.hfstudio.guidenh.guide.document.block.LytList;
import com.hfstudio.guidenh.guide.document.block.LytListItem;
+import com.hfstudio.guidenh.guide.document.block.LytMermaidFlowchart;
+import com.hfstudio.guidenh.guide.document.block.LytMermaidMindmap;
import com.hfstudio.guidenh.guide.document.block.LytNode;
import com.hfstudio.guidenh.guide.document.block.LytParagraph;
+import com.hfstudio.guidenh.guide.document.block.LytQuoteBox;
+import com.hfstudio.guidenh.guide.document.block.LytSizeBox;
import com.hfstudio.guidenh.guide.document.block.LytSlot;
import com.hfstudio.guidenh.guide.document.block.LytSlotGrid;
+import com.hfstudio.guidenh.guide.document.block.LytStructureView;
import com.hfstudio.guidenh.guide.document.block.LytThematicBreak;
+import com.hfstudio.guidenh.guide.document.block.LytWidthBox;
+import com.hfstudio.guidenh.guide.document.block.chart.ChartAxisOptions;
+import com.hfstudio.guidenh.guide.document.block.chart.ChartSeries;
+import com.hfstudio.guidenh.guide.document.block.chart.LytBarChart;
+import com.hfstudio.guidenh.guide.document.block.chart.LytChartBase;
+import com.hfstudio.guidenh.guide.document.block.chart.LytColumnChart;
+import com.hfstudio.guidenh.guide.document.block.chart.LytLineChart;
+import com.hfstudio.guidenh.guide.document.block.chart.LytPieChart;
+import com.hfstudio.guidenh.guide.document.block.chart.LytScatterChart;
+import com.hfstudio.guidenh.guide.document.block.chart.PieInsetSpec;
+import com.hfstudio.guidenh.guide.document.block.chart.PieSlice;
+import com.hfstudio.guidenh.guide.document.block.functiongraph.LytFunctionGraph;
+import com.hfstudio.guidenh.guide.document.block.recipes.LytGenericRecipeBox;
+import com.hfstudio.guidenh.guide.document.block.recipes.LytStandardRecipeBox;
import com.hfstudio.guidenh.guide.document.block.table.LytTable;
import com.hfstudio.guidenh.guide.document.block.table.LytTableCell;
import com.hfstudio.guidenh.guide.document.block.table.LytTableRow;
@@ -45,11 +83,18 @@
import com.hfstudio.guidenh.guide.document.flow.LytFlowSpan;
import com.hfstudio.guidenh.guide.document.flow.LytFlowText;
import com.hfstudio.guidenh.guide.document.flow.LytSpoilerSpan;
+import com.hfstudio.guidenh.guide.document.flow.LytTooltipSpan;
import com.hfstudio.guidenh.guide.document.interaction.ContentTooltip;
import com.hfstudio.guidenh.guide.document.interaction.GuideTooltip;
import com.hfstudio.guidenh.guide.document.interaction.ItemTooltip;
import com.hfstudio.guidenh.guide.document.interaction.ItemTooltipAppender;
import com.hfstudio.guidenh.guide.document.interaction.TextTooltip;
+import com.hfstudio.guidenh.guide.internal.item.GuideDisplayItemStacks;
+import com.hfstudio.guidenh.guide.internal.markdown.FileTreeParser.SlotKind;
+import com.hfstudio.guidenh.guide.internal.mermaid.MermaidDiagramType;
+import com.hfstudio.guidenh.guide.internal.mermaid.flowchart.FlowchartParser;
+import com.hfstudio.guidenh.guide.internal.mermaid.mindmap.MindmapParser;
+import com.hfstudio.guidenh.guide.internal.structure.GuideTextNbtCodec;
import com.hfstudio.guidenh.guide.internal.tooltip.GuideItemTooltipLines;
import com.hfstudio.guidenh.guide.scene.GuidebookSceneLayerSelection;
import com.hfstudio.guidenh.guide.scene.LytGuidebookScene;
@@ -677,6 +722,156 @@ private static void appendBlock(StringBuilder html, @Nullable LytNode node,
allowNestedItemTooltips);
return;
}
+ case LytImageBlock imageBlock -> {
+ appendImageBlock(html, imageBlock, currentPageId, assetExporter, itemIconResolver, templates);
+ return;
+ }
+ case BlockImagePlaceholder blockImage -> {
+ appendBlockImage(html, blockImage, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ return;
+ }
+ case LytMermaidMindmap mindmap -> {
+ appendMermaidMindmap(html, mindmap, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ return;
+ }
+ case LytMermaidFlowchart flowchart -> {
+ appendMermaidFlowchart(html, flowchart, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ return;
+ }
+ case MermaidPlaceholder mermaid -> {
+ appendMermaidPlaceholder(html, mermaid, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ return;
+ }
+ case LytCodeBlock codeBlock -> {
+ appendCodeBlock(html, codeBlock, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ return;
+ }
+ case LytLatexDisplayBlock latex -> {
+ appendLatex(html, latex.getFormula(), latex.getFillColorArgb(), latex.getSourceScale(),
+ latex.getUserScale(), latex.getLatexTooltip(), true, latex.getOffsetX(), latex.getOffsetY(),
+ currentPageId, assetExporter, itemIconResolver, templates);
+ return;
+ }
+ case LytLatexBlock latex -> {
+ appendLatex(html, latex.getFormula(), latex.getFillColorArgb(), latex.getSourceScale(),
+ latex.getUserScale(), latex.getLatexTooltip(), false, latex.getOffsetX(), latex.getOffsetY(),
+ currentPageId, assetExporter, itemIconResolver, templates);
+ return;
+ }
+ case LytFileTree fileTree -> {
+ appendFileTree(html, fileTree, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ return;
+ }
+ case LytStructureView structure -> {
+ appendStructureView(html, structure, itemIconResolver, currentPageId, assetExporter, templates,
+ allowNestedItemTooltips);
+ return;
+ }
+ case LytGuiSprite sprite -> {
+ appendGuiSprite(html, sprite, assetExporter);
+ return;
+ }
+ case LytStandardRecipeBox recipe -> {
+ html.append("");
+ appendBlockChildren(html, recipe, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ html.append("
");
+ return;
+ }
+ case LytGenericRecipeBox recipe -> {
+ html.append("");
+ appendBlockChildren(html, recipe, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ html.append("
");
+ return;
+ }
+ case LytColumnChart column -> {
+ html.append(renderColumnChart(column));
+ return;
+ }
+ case LytBarChart bar -> {
+ html.append(renderBarChart(bar));
+ return;
+ }
+ case LytLineChart line -> {
+ html.append(renderLineChart(line));
+ return;
+ }
+ case LytPieChart pie -> {
+ html.append(renderPieChart(pie));
+ return;
+ }
+ case LytScatterChart scatter -> {
+ html.append(renderScatterChart(scatter));
+ return;
+ }
+ case LytFunctionGraph functionGraph -> {
+ html.append(GuideSiteGraphRenderer.renderFunctionGraph(
+ functionGraph,
+ assetExporter != null ? assetExporter.latexExporter() : null));
+ return;
+ }
+ case LytDetailsBlock details -> {
+ html.append("");
+ appendBlockChildren(html, details.getSummaryBox(), currentPageId, assetExporter, itemIconResolver,
+ templates, allowNestedItemTooltips);
+ html.append("
");
+ if (details.isOpen()) {
+ appendBlockChildren(html, details.getContentBox(), currentPageId, assetExporter,
+ itemIconResolver, templates, allowNestedItemTooltips);
+ }
+ html.append("
");
+ return;
+ }
+ case LytContentTabsBlock tabs -> {
+ appendContentTabs(html, tabs, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ return;
+ }
+ case LytQuoteBox quote -> {
+ html.append("");
+ appendBlockChildren(html, quote, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ html.append("
");
+ return;
+ }
+ case LytAlertBox alert -> {
+ html.append("");
+ appendBlockChildren(html, alert, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ html.append("
");
+ return;
+ }
+ case LytAlignedBlock aligned -> {
+ html.append("");
+ appendBlockChildren(html, aligned, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ html.append("
");
+ return;
+ }
+ case LytDocumentFloat documentFloat -> {
+ appendBlockChildren(html, node, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ return;
+ }
+ case LytSizeBox sizeBox -> {
+ appendBlockChildren(html, node, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ return;
+ }
+ case ItemImagePlaceholder itemImage -> {
+ appendItemImage(html, itemImage, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ return;
+ }
case LytParagraph paragraph -> {
appendParagraph(
html,
@@ -711,6 +906,17 @@ private static void appendBlock(StringBuilder html, @Nullable LytNode node,
allowNestedItemTooltips);
return;
}
+ case LytHBox row -> {
+ html.append("");
+ for (LytNode child : row.getChildren()) {
+ appendBlock(html, child, currentPageId, assetExporter, itemIconResolver, templates,
+ allowNestedItemTooltips);
+ }
+ html.append("
");
+ return;
+ }
case LytThematicBreak lytThematicBreak -> {
html.append("
");
return;
@@ -791,25 +997,47 @@ private static void appendBlock(StringBuilder html, @Nullable LytNode node,
allowNestedItemTooltips);
}
}
+ if ((children == null || children.isEmpty()) && node.getTextContent().isEmpty()) {
+ html.append("")
+ .append(escapeHtml(node.getClass().getSimpleName()))
+ .append("");
+ }
}
private static void appendParagraph(StringBuilder html, LytParagraph paragraph, String tagName,
@Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
boolean allowNestedItemTooltips) {
- html.append("<")
- .append(tagName);
- appendParagraphStyleAttribute(html, paragraph.resolveStyle());
- if (paragraph instanceof LytHeading heading) {
- String anchor = GuideSiteHrefResolver.headingAnchor(heading.getTextContent());
- if (!anchor.isEmpty()) {
- html.append(" id=\"")
- .append(escapeAttribute(anchor))
- .append("\"");
- }
- }
- html.append(">");
+ boolean open = false;
+ boolean emitted = false;
for (LytFlowContent content : paragraph.getContent()) {
+ boolean blockLayout = "p".equals(tagName) && content instanceof LytFlowInlineBlock inline
+ && (inline.getBlock() instanceof LytHBox || inline.getBlock() instanceof LytWidthBox);
+ if (blockLayout) {
+ if (open) {
+ html.append("
");
+ open = false;
+ }
+ } else if (!open) {
+ if (emitted && content instanceof LytFlowText text
+ && text.getText()
+ .isBlank()) {
+ continue;
+ }
+ html.append("<")
+ .append(tagName);
+ appendParagraphStyleAttribute(html, paragraph.resolveStyle());
+ if (paragraph instanceof LytHeading heading) {
+ String anchor = GuideSiteHrefResolver.headingAnchor(heading.getTextContent());
+ if (!anchor.isEmpty()) {
+ html.append(" id=\"")
+ .append(escapeAttribute(anchor))
+ .append("\"");
+ }
+ }
+ html.append(">");
+ open = true;
+ }
appendFlowContent(
html,
content,
@@ -818,10 +1046,19 @@ private static void appendParagraph(StringBuilder html, LytParagraph paragraph,
itemIconResolver,
templates,
allowNestedItemTooltips);
+ emitted = true;
+ }
+ if (open) {
+ html.append("")
+ .append(tagName)
+ .append(">");
+ } else if (!emitted) {
+ html.append("<")
+ .append(tagName)
+ .append(">")
+ .append(tagName)
+ .append(">");
}
- html.append("")
- .append(tagName)
- .append(">");
}
private static void appendList(StringBuilder html, LytList list, @Nullable ResourceLocation currentPageId,
@@ -963,7 +1200,8 @@ private static void appendFlowContent(StringBuilder html, @Nullable LytFlowConte
itemIconResolver,
templates,
allowNestedItemTooltips);
- case LytFlowLink link -> appendLink(html, link, currentPageId, itemIconResolver, templates, allowNestedItemTooltips);
+ case LytFlowLink link -> appendLink(html, link, currentPageId, assetExporter, itemIconResolver,
+ templates, allowNestedItemTooltips);
case LytFlowSpan span -> appendStyledFlowContainer(
html,
span,
@@ -1010,8 +1248,8 @@ private static void appendInlineBlock(StringBuilder html, LytFlowInlineBlock inl
}
private static void appendLink(StringBuilder html, LytFlowLink link, @Nullable ResourceLocation currentPageId,
- GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
- boolean allowNestedItemTooltips) {
+ @Nullable GuideSitePageAssetExporter assetExporter, GuideSiteItemIconResolver itemIconResolver,
+ @Nullable GuideSiteTemplateRegistry templates, boolean allowNestedItemTooltips) {
String href = null;
PageAnchor pageAnchor = link.getPageAnchor();
if (pageAnchor != null) {
@@ -1028,7 +1266,7 @@ private static void appendLink(StringBuilder html, LytFlowLink link, @Nullable R
link,
href != null && !href.isEmpty() ? "a" : "span",
currentPageId,
- null,
+ assetExporter,
itemIconResolver,
templates,
allowNestedItemTooltips,
@@ -1041,14 +1279,37 @@ private static void appendStyledFlowContainer(StringBuilder html, LytFlowSpan sp
boolean allowNestedItemTooltips, @Nullable String href) {
html.append("<")
.append(tagName);
+ String templateId = null;
+ if (templates != null && span instanceof LytTooltipSpan tooltipSpan) {
+ templateId = createTemplateId(
+ tooltipSpan.getTooltip(0, 0)
+ .orElse(null),
+ templates,
+ currentPageId,
+ assetExporter,
+ itemIconResolver);
+ }
if ("a".equals(tagName) && href != null && !href.isEmpty()) {
html.append(" href=\"")
.append(escapeAttribute(href))
.append("\"");
}
boolean insideSpoiler = isInsideSpoiler(span);
- if (span instanceof LytSpoilerSpan) {
- html.append(" class=\"guide-spoiler\" tabindex=\"0\"");
+ boolean spoiler = span instanceof LytSpoilerSpan;
+ if (templateId != null || spoiler) {
+ html.append(" class=\"")
+ .append(templateId != null ? "guide-tooltip" : "")
+ .append(templateId != null && spoiler ? " " : "")
+ .append(spoiler ? "guide-spoiler" : "")
+ .append("\"");
+ }
+ if (templateId != null) {
+ html.append(" data-template=\"")
+ .append(escapeAttribute(templateId))
+ .append("\"");
+ }
+ if (spoiler) {
+ html.append(" tabindex=\"0\"");
}
appendInlineStyleAttribute(html, span.resolveStyle(), insideSpoiler);
html.append(">");
@@ -1071,6 +1332,766 @@ private static boolean isInsideSpoiler(LytFlowContent content) {
return content.findAncestor(LytSpoilerSpan.class) != null;
}
+ private static void appendBlockChildren(StringBuilder html, LytNode node,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
+ boolean allowNestedItemTooltips) {
+ for (LytNode child : node.getChildren()) {
+ if (child instanceof LytBlock childBlock) {
+ appendBlock(
+ html,
+ childBlock,
+ currentPageId,
+ assetExporter,
+ itemIconResolver,
+ templates,
+ allowNestedItemTooltips);
+ }
+ }
+ }
+
+ private static void appendCodeBlock(StringBuilder html, LytCodeBlock codeBlock,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
+ boolean allowNestedItemTooltips) {
+ String language = codeBlock.getDetectedLanguageId();
+ if ((language == null || language.isBlank() || "text".equalsIgnoreCase(language))
+ && codeBlock.getLanguageFenceName() != null
+ && !codeBlock.getLanguageFenceName()
+ .isBlank()) {
+ language = codeBlock.getLanguageFenceName();
+ }
+ String source = codeBlock.getCodeText() != null ? codeBlock.getCodeText() : "";
+ if ("csv".equalsIgnoreCase(language)) {
+ html.append(GuideSiteGraphRenderer.renderCsvTable(source, true));
+ return;
+ }
+ if ("tree".equalsIgnoreCase(language) || "filetree".equalsIgnoreCase(language)) {
+ html.append(GuideSiteGraphRenderer.renderFileTree(source));
+ return;
+ }
+ if ("mermaid".equalsIgnoreCase(language)) {
+ appendMermaidSource(html, source);
+ return;
+ }
+ html.append(" 0) {
+ html.append(" style=\"max-height:")
+ .append(codeBlock.getForcedBodyHeight())
+ .append("px;overflow:auto;\"");
+ }
+ html.append(">")
+ .append(escapeHtml(source))
+ .append("");
+ }
+
+ private static void appendMermaidSource(StringBuilder html, String source) {
+ try {
+ MermaidDiagramType type = MermaidDiagramType.detect(source);
+ if (type == MermaidDiagramType.MINDMAP) {
+ html.append(GuideSiteGraphRenderer.renderMermaidTree(MindmapParser.parse(source)));
+ } else if (type == MermaidDiagramType.FLOWCHART) {
+ html.append(GuideSiteGraphRenderer.renderFlowchart(FlowchartParser.parse(source)));
+ } else {
+ html.append("")
+ .append(escapeHtml(source))
+ .append("
");
+ }
+ } catch (RuntimeException exception) {
+ html.append("")
+ .append(escapeHtml(source))
+ .append("
");
+ }
+ }
+
+ private static void appendFileTree(StringBuilder html, LytFileTree fileTree,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
+ boolean allowNestedItemTooltips) {
+ html.append("");
+ for (LytFileTree.Row row : fileTree.getRows()) {
+ html.append("
");
+ html.append("")
+ .append(escapeHtml(fileTreePrefix(row.getSlots())))
+ .append("");
+ if (row.getIconBlock() != null) {
+ html.append("");
+ appendBlock(
+ html,
+ row.getIconBlock(),
+ currentPageId,
+ assetExporter,
+ itemIconResolver,
+ templates,
+ allowNestedItemTooltips);
+ html.append("");
+ }
+ html.append("");
+ appendBlock(
+ html,
+ row.getPayload(),
+ currentPageId,
+ assetExporter,
+ itemIconResolver,
+ templates,
+ allowNestedItemTooltips);
+ html.append("");
+ html.append("
");
+ }
+ html.append("
");
+ }
+
+ private static String fileTreePrefix(List slots) {
+ StringBuilder prefix = new StringBuilder();
+ for (SlotKind slot : slots) {
+ switch (slot) {
+ case VERTICAL -> prefix.append("| ");
+ case BRANCH -> prefix.append("|-- ");
+ case LAST_BRANCH -> prefix.append("`-- ");
+ case EMPTY -> prefix.append(" ");
+ }
+ }
+ return prefix.toString();
+ }
+
+ private static void appendGuiSprite(StringBuilder html, LytGuiSprite sprite,
+ @Nullable GuideSitePageAssetExporter assetExporter) {
+ int width = Math.max(
+ 1,
+ sprite.getSize()
+ .width());
+ int height = Math.max(
+ 1,
+ sprite.getSize()
+ .height());
+ if (sprite.getSprite() == null || assetExporter == null) {
+ html.append("");
+ return;
+ }
+ String src = assetExporter.exportResource(
+ sprite.getSprite()
+ .getTexture());
+ if (src.isEmpty()) {
+ html.append("");
+ return;
+ }
+ html.append("");
+ }
+
+ private static void appendStructureView(StringBuilder html, LytStructureView structure,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable ResourceLocation currentPageId,
+ @Nullable GuideSitePageAssetExporter assetExporter, @Nullable GuideSiteTemplateRegistry templates,
+ boolean allowNestedItemTooltips) {
+ html.append("");
+ for (LytStructureView.BlockEntry entry : structure.getBlocks()) {
+ GuideSiteExportedItem item = GuideSiteItemSupport.export(entry.stack, itemIconResolver);
+ String templateId = allowNestedItemTooltips && templates != null
+ ? createNestedItemTemplateId(entry.stack, currentPageId, assetExporter, itemIconResolver, templates)
+ : null;
+ html.append("");
+ GuideSiteItemHtml.appendIcon(html, item, "guide-structure-block-icon");
+ html.append("");
+ }
+ html.append("
");
+ }
+
+ private static void appendLatex(StringBuilder html, String formula, int fillColorArgb, float sourceScale,
+ float userScale, @Nullable GuideTooltip tooltip, boolean display, int offsetX, int offsetY,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates) {
+ GuideSiteLatexExporter.ExportedLatex exported = assetExporter != null ? assetExporter.latexExporter()
+ .export(formula, fillColorArgb, sourceScale) : null;
+ String templateId = templates != null
+ ? createTemplateId(
+ tooltip != null ? tooltip : new TextTooltip(formula),
+ templates,
+ currentPageId,
+ assetExporter,
+ itemIconResolver)
+ : null;
+ String tag = display ? "div" : "span";
+ html.append("<")
+ .append(tag)
+ .append(" class=\"guide-latex ")
+ .append(display ? "guide-latex-display" : "guide-latex-inline");
+ if (templateId != null) {
+ html.append(" guide-tooltip\" data-template=\"")
+ .append(escapeAttribute(templateId));
+ } else {
+ html.append("\"");
+ }
+ if (offsetX != 0 || offsetY != 0) {
+ html.append(" style=\"transform:translate(")
+ .append(offsetX)
+ .append("px,")
+ .append(offsetY)
+ .append("px)\"");
+ }
+ html.append(">");
+ if (exported != null) {
+ int width = Math.max(1, Math.round(exported.widthPx() * userScale));
+ int height = Math.max(1, Math.round(exported.heightPx() * userScale));
+ html.append("
");
+ } else {
+ html.append(escapeHtml(formula));
+ }
+ html.append("")
+ .append(tag)
+ .append(">");
+ }
+
+ private static void appendContentTabs(StringBuilder html, LytContentTabsBlock tabs,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
+ boolean allowNestedItemTooltips) {
+ html.append("");
+ List extends LytNode> children = tabs.getChildren();
+ if (!children.isEmpty() && children.getFirst() instanceof LytParagraph title) {
+ appendBlock(
+ html,
+ title,
+ currentPageId,
+ assetExporter,
+ itemIconResolver,
+ templates,
+ allowNestedItemTooltips);
+ }
+ html.append("
");
+ for (int i = 1; i < children.size(); i++) {
+ appendBlock(
+ html,
+ children.get(i),
+ currentPageId,
+ assetExporter,
+ itemIconResolver,
+ templates,
+ allowNestedItemTooltips);
+ }
+ html.append("
");
+ }
+
+ private static GuideSiteGraphRenderer.ChartStyle chartStyle(LytChartBase chart,
+ @Nullable ChartAxisOptions xAxis, @Nullable ChartAxisOptions yAxis, float barWidthRatio,
+ float pieStartAngle, boolean pieClockwise) {
+ return new GuideSiteGraphRenderer.ChartStyle(
+ chart.getExplicitWidth(),
+ chart.getExplicitHeight(),
+ chart.getBackgroundColor(),
+ chart.getBorderColor(),
+ chart.getTitle(),
+ chart.getLegendPosition(),
+ chart.getLabelPosition(),
+ chart.getLabelColor(),
+ SiteChartAxis.from(xAxis, GuideSiteGraphRenderer.DEFAULT_GRID_COLOR),
+ SiteChartAxis.from(yAxis, GuideSiteGraphRenderer.DEFAULT_GRID_COLOR),
+ chart.getTitleColor(),
+ barWidthRatio,
+ pieStartAngle,
+ pieClockwise);
+ }
+
+ private static List seriesData(List series) {
+ List result = new ArrayList<>();
+ for (ChartSeries item : series) {
+ result.add(
+ new GuideSiteGraphRenderer.SeriesData(item.getName(), item.getColor(), item.getXs(), item.getYs()));
+ }
+ return result;
+ }
+
+ private static String[] categories(String[] values) {
+ return values != null ? values : new String[0];
+ }
+
+ private static String renderColumnChart(LytColumnChart chart) {
+ GuideSiteGraphRenderer.ChartStyle style = chartStyle(
+ chart,
+ chart.getXAxis(),
+ chart.getYAxis(),
+ chart.getBarWidthRatio(),
+ -90f,
+ true);
+ GuideSiteGraphRenderer.PieInsetData inset = chart.getPieInset() != null ? pieInsetData(chart.getPieInset())
+ : null;
+ List series = seriesData(chart.getSeries());
+ for (ChartSeries overlay : chart.getLineOverlays()) {
+ series.add(
+ new GuideSiteGraphRenderer.SeriesData(
+ overlay.getName(),
+ overlay.getColor(),
+ overlay.getXs(),
+ overlay.getYs(),
+ GuideSiteGraphRenderer.SeriesData.TYPE_LINE));
+ }
+ return GuideSiteGraphRenderer
+ .renderColumnChart(style, categories(chart.getCategories()), series, inset, null);
+ }
+
+ private static String renderBarChart(LytBarChart chart) {
+ return GuideSiteGraphRenderer.renderBarChart(
+ chartStyle(chart, chart.getXAxis(), chart.getYAxis(), chart.getBarWidthRatio(), -90f, true),
+ categories(chart.getCategories()),
+ seriesData(chart.getSeries()));
+ }
+
+ private static String renderLineChart(LytLineChart chart) {
+ return GuideSiteGraphRenderer.renderLineChart(
+ chartStyle(chart, chart.getXAxis(), chart.getYAxis(), 0.7f, -90f, true),
+ categories(chart.getCategories()),
+ seriesData(chart.getSeries()),
+ chart.isNumericX(),
+ chart.isShowPoints(),
+ chart.getCornerLegendPosition(),
+ chart.getCornerLegendWidth(),
+ chart.getCornerLegendHeight(),
+ chart.getCornerLegendBackgroundColor());
+ }
+
+ private static String renderPieChart(LytPieChart chart) {
+ List slices = new ArrayList<>();
+ for (PieSlice slice : chart.getSlices()) {
+ slices.add(new GuideSiteGraphRenderer.SliceData(slice.getLabel(), slice.getValue(), slice.getColor()));
+ }
+ return GuideSiteGraphRenderer.renderPieChart(
+ chartStyle(chart, null, null, 0.7f, chart.getStartAngleDeg(), chart.isClockwise()),
+ slices);
+ }
+
+ private static String renderScatterChart(LytScatterChart chart) {
+ return GuideSiteGraphRenderer.renderScatterChart(
+ chartStyle(chart, chart.getXAxis(), chart.getYAxis(), 0.7f, -90f, true),
+ seriesData(chart.getSeries()),
+ chart.getCornerLegendPosition(),
+ chart.getCornerLegendWidth(),
+ chart.getCornerLegendHeight(),
+ chart.getCornerLegendBackgroundColor());
+ }
+
+ private static GuideSiteGraphRenderer.PieInsetData pieInsetData(PieInsetSpec inset) {
+ List slices = new ArrayList<>();
+ for (PieSlice slice : inset.getSlices()) {
+ slices.add(new GuideSiteGraphRenderer.SliceData(slice.getLabel(), slice.getValue(), slice.getColor()));
+ }
+ return new GuideSiteGraphRenderer.PieInsetData(
+ slices,
+ inset.getSize(),
+ inset.getPosition()
+ .name()
+ .toLowerCase(Locale.ROOT)
+ .replace('_', '-'),
+ inset.getTitle());
+ }
+
+ private static void appendImageBlock(StringBuilder html, LytImageBlock image,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates) {
+ String source = image.getSrc();
+ if (source == null || source.isBlank()) {
+ return;
+ }
+ String src = source;
+ if (assetExporter != null) {
+ try {
+ src = assetExporter.exportResource(new ResourceLocation(source));
+ } catch (IllegalArgumentException e) {
+ GuideDebugLog.warnAlways("[GuideNH] [SceneExport] Invalid tooltip image source: {}", source);
+ return;
+ }
+ }
+ if (src.isEmpty()) {
+ GuideDebugLog.warnAlways("[GuideNH] [SceneExport] Missing tooltip image: {}", source);
+ return;
+ }
+
+ boolean cropped = image.getCropWidth() > 0 && image.getCropHeight() > 0;
+ int displayWidth = image.getDisplayWidth();
+ int displayHeight = image.getDisplayHeight();
+ if (cropped) {
+ if (displayWidth <= 0 && displayHeight > 0) {
+ displayWidth = Math.max(
+ 1,
+ (int) Math.round(displayHeight * image.getCropWidth() / (double) image.getCropHeight()));
+ } else if (displayWidth <= 0) {
+ displayWidth = Math.max(1, (int) Math.round(image.getCropWidth() * image.getScaleX()));
+ }
+ if (displayHeight <= 0) {
+ displayHeight = Math
+ .max(1, (int) Math.round(displayWidth * image.getCropHeight() / (double) image.getCropWidth()));
+ }
+ }
+
+ html.append(" 0) {
+ html.append(" style=\"width:")
+ .append(displayWidth)
+ .append("px;\"");
+ }
+ html.append(">
0 && displayWidth <= 0) {
+ html.append(" data-display-height=\"")
+ .append(displayHeight)
+ .append("\"");
+ }
+ html.append(" loading=\"lazy\" decoding=\"async\">");
+ for (ImageRegionAnnotation annotation : image.getAnnotations()) {
+ String templateId = templates != null
+ ? createTemplateId(
+ annotation.getTooltip(),
+ templates,
+ currentPageId,
+ assetExporter,
+ itemIconResolver)
+ : null;
+ html.append("");
+ }
+ html.append("");
+ if (image.getTitle() != null && !image.getTitle()
+ .isEmpty()) {
+ html.append("")
+ .append(escapeHtml(image.getTitle()))
+ .append("");
+ }
+ html.append("");
+ }
+
+ private static void appendBlockImage(StringBuilder html, BlockImagePlaceholder blockImage,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
+ boolean allowNestedItemTooltips) {
+ GuideItemReferenceResolver.ResolvedBlockReference block = GuideItemReferenceResolver
+ .resolveBlockReference("minecraft", blockImage.id, blockImage.ore);
+ if (block == null || block.stack() == null) {
+ html.append("")
+ .append(
+ escapeHtml(
+ blockImage.id != null ? blockImage.id
+ : blockImage.ore != null ? blockImage.ore : "BlockImage"))
+ .append("");
+ return;
+ }
+ ItemStack stack = block.stack()
+ .copy();
+ if (blockImage.meta != Integer.MIN_VALUE && stack.getItem() != null) {
+ stack.setItemDamage(blockImage.meta);
+ }
+ if (blockImage.nbt != null && !blockImage.nbt.isBlank()) {
+ try {
+ NBTTagCompound explicitTag = GuideTextNbtCodec.readTextSafeCompound(blockImage.nbt.trim());
+ if (stack.stackTagCompound == null) {
+ stack.stackTagCompound = explicitTag;
+ } else {
+ for (String key : explicitTag.func_150296_c()) {
+ stack.stackTagCompound.setTag(key, explicitTag.getTag(key));
+ }
+ }
+ } catch (Exception exception) {
+ GuideDebugLog.warnAlways(
+ "[GuideNH] [SceneExport] Invalid tooltip BlockImage NBT: {}",
+ blockImage.id != null ? blockImage.id : blockImage.ore);
+ }
+ }
+ String templateId = allowNestedItemTooltips && templates != null
+ ? createNestedItemTemplateId(stack, currentPageId, assetExporter, itemIconResolver, templates)
+ : null;
+ GuideSiteExportedItem item = GuideSiteItemSupport.export(stack, itemIconResolver);
+ html.append("");
+ GuideSiteItemHtml.appendIcon(html, item, "guide-inline-item-icon", blockImage.scale);
+ html.append("");
+ }
+
+ private static void appendMermaidMindmap(StringBuilder html, LytMermaidMindmap mindmap,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
+ boolean allowNestedItemTooltips) {
+ Map nodeHtml = renderMermaidNodeContent(
+ mindmap.getNodeContent(),
+ currentPageId,
+ assetExporter,
+ itemIconResolver,
+ templates,
+ allowNestedItemTooltips);
+ html.append(GuideSiteGraphRenderer.renderMermaidTree(mindmap.getMindmap(), nodeHtml));
+ }
+
+ private static void appendMermaidFlowchart(StringBuilder html, LytMermaidFlowchart flowchart,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
+ boolean allowNestedItemTooltips) {
+ Map nodeHtml = renderMermaidNodeContent(
+ flowchart.getNodeContent(),
+ currentPageId,
+ assetExporter,
+ itemIconResolver,
+ templates,
+ allowNestedItemTooltips);
+ html.append(GuideSiteGraphRenderer.renderFlowchart(flowchart.getFlowchart(), nodeHtml));
+ }
+
+ private static void appendMermaidPlaceholder(StringBuilder html, MermaidPlaceholder mermaid,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
+ boolean allowNestedItemTooltips) {
+ String source = mermaid.sourceText != null ? mermaid.sourceText.trim() : "";
+ if (source.isEmpty()) {
+ html.append("Mermaid source is unavailable for this tooltip.
");
+ return;
+ }
+ try {
+ Map nodeHtml = renderMermaidNodeContent(
+ mermaid.nodeContentBlocks,
+ currentPageId,
+ assetExporter,
+ itemIconResolver,
+ templates,
+ allowNestedItemTooltips);
+ if (MermaidDiagramType.MINDMAP == mermaid.diagramType) {
+ html.append(GuideSiteGraphRenderer.renderMermaidTree(MindmapParser.parse(source), nodeHtml));
+ } else if (MermaidDiagramType.FLOWCHART == mermaid.diagramType) {
+ html.append(GuideSiteGraphRenderer.renderFlowchart(FlowchartParser.parse(source), nodeHtml));
+ } else {
+ html.append("")
+ .append(escapeHtml(source))
+ .append("
");
+ }
+ } catch (RuntimeException exception) {
+ html.append("")
+ .append(escapeHtml(source))
+ .append("
");
+ }
+ }
+
+ private static Map renderMermaidNodeContent(Map nodeContent,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
+ boolean allowNestedItemTooltips) {
+ Map result = new LinkedHashMap<>();
+ for (Map.Entry entry : nodeContent.entrySet()) {
+ StringBuilder body = new StringBuilder();
+ appendBlock(
+ body,
+ entry.getValue(),
+ currentPageId,
+ assetExporter,
+ itemIconResolver,
+ templates,
+ allowNestedItemTooltips);
+ result.put(entry.getKey(), body.toString());
+ }
+ return result;
+ }
+
+ private static void appendItemImage(StringBuilder html, ItemImagePlaceholder image,
+ @Nullable ResourceLocation currentPageId, @Nullable GuideSitePageAssetExporter assetExporter,
+ GuideSiteItemIconResolver itemIconResolver, @Nullable GuideSiteTemplateRegistry templates,
+ boolean allowNestedItemTooltips) {
+ ItemStack stack = GuideDisplayItemStacks.resolveItemStack(image.itemId, "minecraft");
+ if (stack == null && image.ore != null) {
+ stack = GuideDisplayItemStacks.resolveOreStack(image.ore);
+ }
+ if (stack == null) {
+ html.append("")
+ .append(escapeHtml(image.itemId))
+ .append("");
+ return;
+ }
+ if (image.nbt != null && !image.nbt.isBlank()) {
+ try {
+ NBTTagCompound explicitTag = GuideTextNbtCodec.readTextSafeCompound(image.nbt.trim());
+ if (stack.stackTagCompound == null) {
+ stack.stackTagCompound = explicitTag;
+ } else {
+ for (String key : explicitTag.func_150296_c()) {
+ stack.stackTagCompound.setTag(key, explicitTag.getTag(key));
+ }
+ }
+ } catch (Exception e) {
+ GuideDebugLog.warnAlways("[GuideNH] [SceneExport] Invalid tooltip ItemImage NBT: {}", image.itemId);
+ }
+ }
+
+ GuideSiteExportedItem item = GuideSiteItemSupport.export(stack, itemIconResolver);
+ String templateId = image.showTooltip && allowNestedItemTooltips && templates != null
+ ? createNestedItemTemplateId(stack, currentPageId, assetExporter, itemIconResolver, templates)
+ : null;
+ html.append("");
+ String label = image.labelPosition != null ? stack.getDisplayName() : null;
+ if (label != null && "left".equals(image.labelPosition)) {
+ appendItemImageLabel(html, label, image);
+ }
+ if (!Boolean.FALSE.equals(image.showIcon)) {
+ GuideSiteItemHtml.appendIcon(html, item, "guide-inline-item-icon", image.scale);
+ }
+ if (label != null && !"left".equals(image.labelPosition)) {
+ appendItemImageLabel(html, label, image);
+ }
+ html.append("");
+ }
+
+ private static void appendItemImageLabel(StringBuilder html, String label, ItemImagePlaceholder image) {
+ String formatted = image.labelFormat != null && image.labelFormat.contains("%s")
+ ? image.labelFormat.replace("%s", label)
+ : image.labelFormat != null ? image.labelFormat : label;
+ html.append("")
+ .append(escapeHtml(formatted))
+ .append("");
+ }
+
private static void appendImage(StringBuilder html, LytImage image,
@Nullable GuideSitePageAssetExporter assetExporter) {
String src = "";
diff --git a/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/SiteChartAxis.java b/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/SiteChartAxis.java
index 057a1411c..8523b2cfc 100644
--- a/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/SiteChartAxis.java
+++ b/src/main/java/com/hfstudio/guidenh/guide/siteexport/site/SiteChartAxis.java
@@ -7,6 +7,7 @@
import com.hfstudio.guidenh.guide.compiler.tags.MdxAttrs;
import com.hfstudio.guidenh.guide.compiler.tags.chart.ChartAttrParser;
+import com.hfstudio.guidenh.guide.document.block.chart.ChartAxisOptions;
import com.hfstudio.guidenh.libs.mdast.mdx.model.MdxJsxElementFields;
public class SiteChartAxis {
@@ -60,6 +61,21 @@ public static SiteChartAxis automatic(int defaultGridColor) {
return new SiteChartAxis(null, null, null, null, null, null, true, defaultGridColor);
}
+ public static SiteChartAxis from(ChartAxisOptions axis, int defaultGridColor) {
+ if (axis == null) {
+ return automatic(defaultGridColor);
+ }
+ return new SiteChartAxis(
+ axis.getLabel(),
+ axis.getMin(),
+ axis.getMax(),
+ axis.getStep(),
+ axis.getTickFormat(),
+ axis.getUnit(),
+ axis.isGridVisible(),
+ axis.getGridColor());
+ }
+
@Nullable
public String label() {
return label;
diff --git a/src/main/resources/assets/guidenh/siteexport/app.css b/src/main/resources/assets/guidenh/siteexport/app.css
index ad667e0c4..68d3f0598 100644
--- a/src/main/resources/assets/guidenh/siteexport/app.css
+++ b/src/main/resources/assets/guidenh/siteexport/app.css
@@ -896,11 +896,44 @@ p {
border-width: calc(1px * var(--gui-scale));
border-image-slice: 1;
background: rgba(16, 0, 16, 0.94);
+ box-sizing: border-box;
padding: calc(2px * var(--gui-scale));
line-height: 1;
max-width: calc(220px * var(--gui-scale));
}
+.guide-tooltip-popup:has(.guide-tooltip-layout) {
+ width: max-content;
+ max-width: min(calc(320px * var(--gui-scale)), calc(100vw - 24px));
+}
+
+.guide-tooltip-layout {
+ max-width: 100%;
+}
+
+.guide-tooltip-layout > * {
+ min-width: 0;
+}
+
+.guide-gui-sprite-placeholder {
+ display: inline-block;
+ min-width: 16px;
+ min-height: 16px;
+ background: rgba(128, 128, 128, 0.18);
+ border: 1px solid rgba(128, 128, 128, 0.5);
+ vertical-align: middle;
+}
+
+.guide-gui-sprite {
+ vertical-align: middle;
+ image-rendering: pixelated;
+}
+
+.guide-export-placeholder {
+ color: var(--guide-text-muted, rgba(190, 190, 190, 0.9));
+ font-style: italic;
+}
+
.guide-tooltip-popup::before {
content: "";
position: absolute;
@@ -950,6 +983,7 @@ p {
border-width: calc(1px * var(--gui-scale));
border-image-slice: 1;
background: rgba(16, 0, 16, 0.94);
+ box-sizing: border-box;
padding: calc(2px * var(--gui-scale));
line-height: 1;
max-width: calc(120px * var(--gui-scale));