This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new 1f3cebbfe4 issue #7111 : Preview in HTML for MarkDown files (#7396)
1f3cebbfe4 is described below
commit 1f3cebbfe4343a4bc026c8dce3cab1d451145ba7
Author: Matt Casters <[email protected]>
AuthorDate: Thu Jul 2 14:27:49 2026 +0200
issue #7111 : Preview in HTML for MarkDown files (#7396)
* issue #7111 : Preview in HTML for MarkDown files
* issue #7111 : Preview in HTML for MarkDown files (lib sync)
---
plugins/misc/documentation/pom.xml | 4 -
plugins/transforms/textfile/pom.xml | 6 +
.../transforms/types/MarkDownExplorerFileType.java | 13 +-
.../types/MarkDownExplorerFileTypeHandler.java | 259 +++++++++++++++++++++
.../types/messages/messages_en_US.properties | 1 +
.../types/messages/messages_pt_BR.properties | 1 +
pom.xml | 1 +
7 files changed, 275 insertions(+), 10 deletions(-)
diff --git a/plugins/misc/documentation/pom.xml
b/plugins/misc/documentation/pom.xml
index 25b231fea9..41fc1f4d1a 100644
--- a/plugins/misc/documentation/pom.xml
+++ b/plugins/misc/documentation/pom.xml
@@ -29,10 +29,6 @@
<packaging>jar</packaging>
<name>Hop Plugins Miscellaneous Documentation</name>
- <properties>
- <commonmark.version>0.27.1</commonmark.version>
- </properties>
-
<dependencyManagement>
<dependencies>
<dependency>
diff --git a/plugins/transforms/textfile/pom.xml
b/plugins/transforms/textfile/pom.xml
index b53fe4f30f..5cd80103f0 100644
--- a/plugins/transforms/textfile/pom.xml
+++ b/plugins/transforms/textfile/pom.xml
@@ -40,6 +40,12 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.commonmark</groupId>
+ <artifactId>commonmark</artifactId>
+ <version>${commonmark.version}</version>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/MarkDownExplorerFileType.java
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/MarkDownExplorerFileType.java
index 27f797e4da..ec4664b625 100644
---
a/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/MarkDownExplorerFileType.java
+++
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/MarkDownExplorerFileType.java
@@ -6,13 +6,14 @@
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
+ *
*/
package org.apache.hop.pipeline.transforms.types;
@@ -42,7 +43,7 @@ import
org.apache.hop.ui.hopgui.perspective.explorer.file.types.text.BaseTextExp
description = "MarkDown file handling in the explorer perspective",
image = "markdown.svg")
public class MarkDownExplorerFileType
- extends BaseTextExplorerFileType<TextExplorerFileTypeHandler> {
+ extends BaseTextExplorerFileType<MarkDownExplorerFileTypeHandler> {
public static final Class<?> PKG = MarkDownExplorerFileType.class;
public static final String ACTION_ID_NEW_MARKDOWN = "NewMarkdown";
@@ -64,17 +65,17 @@ public class MarkDownExplorerFileType
}
@Override
- public TextExplorerFileTypeHandler createFileTypeHandler(
+ public MarkDownExplorerFileTypeHandler createFileTypeHandler(
HopGui hopGui, ExplorerPerspective perspective, ExplorerFile file) {
- return new TextExplorerFileTypeHandler(hopGui, perspective, file);
+ return new MarkDownExplorerFileTypeHandler(hopGui, perspective, file);
}
@Override
public IHopFileTypeHandler newFile(HopGui hopGui, IVariables variables)
throws HopException {
ExplorerFile explorerFile = new ExplorerFile("New markdown", null, this);
ExplorerPerspective explorerPerspective =
ExplorerPerspective.getInstance();
- TextExplorerFileTypeHandler fileHandler =
- new TextExplorerFileTypeHandler(hopGui, explorerPerspective,
explorerFile);
+ MarkDownExplorerFileTypeHandler fileHandler =
+ new MarkDownExplorerFileTypeHandler(hopGui, explorerPerspective,
explorerFile);
explorerPerspective.addFile(fileHandler);
explorerPerspective.activate();
diff --git
a/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/MarkDownExplorerFileTypeHandler.java
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/MarkDownExplorerFileTypeHandler.java
new file mode 100644
index 0000000000..7a0508b07b
--- /dev/null
+++
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/MarkDownExplorerFileTypeHandler.java
@@ -0,0 +1,259 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.hop.pipeline.transforms.types;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import org.apache.hop.core.Props;
+import org.apache.hop.core.gui.plugin.GuiPlugin;
+import org.apache.hop.core.gui.plugin.toolbar.GuiToolbarElement;
+import org.apache.hop.core.gui.plugin.toolbar.GuiToolbarElementType;
+import org.apache.hop.ui.core.PropsUi;
+import org.apache.hop.ui.core.dialog.ErrorDialog;
+import org.apache.hop.ui.core.gui.GuiToolbarWidgets;
+import org.apache.hop.ui.core.gui.IToolbarContainer;
+import org.apache.hop.ui.hopgui.ContentEditorFacade;
+import org.apache.hop.ui.hopgui.HopGui;
+import org.apache.hop.ui.hopgui.ToolbarFacade;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerFile;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
+import
org.apache.hop.ui.hopgui.perspective.explorer.file.types.text.BaseTextExplorerFileTypeHandler;
+import org.apache.hop.ui.util.EnvironmentUtils;
+import org.commonmark.node.Node;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.html.HtmlRenderer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** How do we handle a markdown file in the file explorer perspective? */
+@GuiPlugin
+public class MarkDownExplorerFileTypeHandler extends
BaseTextExplorerFileTypeHandler {
+
+ private static final Class<?> PKG = MarkDownExplorerFileType.class;
+
+ public static final String TOOLBAR_PARENT_ID =
"MarkDownExplorerFileTypeHandler-Toolbar";
+ public static final String TOOLBAR_ITEM_PREVIEW =
+ "MarkDownExplorerFileTypeHandler-ToolBar-Preview";
+
+ public MarkDownExplorerFileTypeHandler(
+ HopGui hopGui, ExplorerPerspective perspective, ExplorerFile
explorerFile) {
+ super(hopGui, perspective, explorerFile);
+ }
+
+ @Override
+ protected String getLanguageId() {
+ return "markdown";
+ }
+
+ @Override
+ public void renderFile(Composite composite) {
+ // Create the toolbar container using the facade
+ IToolbarContainer toolBarContainer =
+ ToolbarFacade.createToolbarContainer(composite, SWT.WRAP | SWT.LEFT |
SWT.HORIZONTAL);
+ Control toolBarControl = toolBarContainer.getControl();
+
+ // Create the toolbar widgets using GuiToolbarWidgets
+ GuiToolbarWidgets toolBarWidgets = new GuiToolbarWidgets();
+ toolBarWidgets.registerGuiPluginObject(this);
+ toolBarWidgets.createToolbarWidgets(toolBarContainer, TOOLBAR_PARENT_ID);
+
+ // Layout data for the toolbar control
+ FormData fdToolBar = new FormData();
+ fdToolBar.left = new FormAttachment(0, 0);
+ fdToolBar.right = new FormAttachment(100, 0);
+ fdToolBar.top = new FormAttachment(0, 0);
+ toolBarControl.setLayoutData(fdToolBar);
+ toolBarControl.pack();
+ PropsUi.setLook(toolBarControl, Props.WIDGET_STYLE_TOOLBAR);
+
+ // Create the standard editor widget
+ editorWidget = ContentEditorFacade.createContentEditor(composite,
getLanguageId());
+
+ // Position the editor widget below the toolbar
+ Control editorControl = editorWidget.getControl();
+ FormData fdEditor = new FormData();
+ fdEditor.left = new FormAttachment(0, 0);
+ fdEditor.right = new FormAttachment(100, 0);
+ fdEditor.top = new FormAttachment(toolBarControl, 0);
+ fdEditor.bottom = new FormAttachment(100, 0);
+ editorControl.setLayoutData(fdEditor);
+
+ // If it's a new file, there's no need to reload it
+ if (this.getFilename() != null) {
+ reload();
+ }
+
+ reloadListener = true;
+ editorWidget.addModifyListener(
+ e -> {
+ if (reloadListener) {
+ this.setChanged();
+ perspective.updateGui();
+ }
+ });
+ }
+
+ @GuiToolbarElement(
+ root = TOOLBAR_PARENT_ID,
+ id = TOOLBAR_ITEM_PREVIEW,
+ toolTip = "i18n::MarkDownFileTypeHandler.Preview.Tooltip",
+ type = GuiToolbarElementType.BUTTON,
+ image = "ui/images/preview.svg")
+ public void previewMarkdown() {
+ try {
+ String markdown = editorWidget.getText();
+
+ // Parse markdown to HTML body content
+ Parser parser = Parser.builder().build();
+ HtmlRenderer renderer = HtmlRenderer.builder().build();
+ Node document = parser.parse(markdown);
+ String htmlContent = renderer.render(document);
+
+ // Wrap the content with styled CSS, supporting dark mode if configured
+ StringBuilder html = new StringBuilder();
+ html.append("<!DOCTYPE html>\n<html>\n<head>\n<meta
charset=\"UTF-8\">\n<style>\n");
+ html.append("body {\n");
+ html.append(
+ " font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe
UI', Roboto, Helvetica, Arial, sans-serif;\n");
+ html.append(" line-height: 1.625;\n");
+ html.append(" margin: 40px auto;\n");
+ html.append(" max-width: 800px;\n");
+ html.append(" padding: 0 20px;\n");
+ html.append("}\n");
+ html.append("h1, h2, h3, h4, h5, h6 {\n");
+ html.append(" font-weight: 600;\n");
+ html.append("}\n");
+ html.append("h1 {\n");
+ html.append(" font-size: 2.25rem;\n");
+ html.append(" padding-bottom: 0.3em;\n");
+ html.append(" border-bottom: 1px solid;\n");
+ html.append("}\n");
+ html.append("h2 {\n");
+ html.append(" font-size: 1.5rem;\n");
+ html.append(" padding-bottom: 0.3em;\n");
+ html.append(" border-bottom: 1px solid;\n");
+ html.append("}\n");
+ html.append("a {\n");
+ html.append(" text-decoration: none;\n");
+ html.append(" font-weight: 500;\n");
+ html.append("}\n");
+ html.append("a:hover {\n");
+ html.append(" text-decoration: underline;\n");
+ html.append("}\n");
+ html.append("pre, code {\n");
+ html.append(
+ " font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo,
monospace;\n");
+ html.append(" font-size: 0.9em;\n");
+ html.append(" border-radius: 6px;\n");
+ html.append("}\n");
+ html.append("code {\n");
+ html.append(" padding: 0.2em 0.4em;\n");
+ html.append("}\n");
+ html.append("pre {\n");
+ html.append(" padding: 16px;\n");
+ html.append(" overflow-x: auto;\n");
+ html.append("}\n");
+ html.append("pre code {\n");
+ html.append(" padding: 0;\n");
+ html.append(" background-color: transparent;\n");
+ html.append(" border-radius: 0;\n");
+ html.append(" border: none;\n");
+ html.append("}\n");
+ html.append("blockquote {\n");
+ html.append(" margin: 1.5em 0;\n");
+ html.append(" padding: 0.5em 1em;\n");
+ html.append(" border-left-width: 4px;\n");
+ html.append(" border-left-style: solid;\n");
+ html.append("}\n");
+
+ if (PropsUi.getInstance().isDarkMode()) {
+ html.append("body {\n");
+ html.append(" background-color: #0b0f19;\n");
+ html.append(" color: #94a3b8;\n");
+ html.append("}\n");
+ html.append("h1, h2, h3, h4, h5, h6 {\n");
+ html.append(" color: #f8fafc;\n");
+ html.append("}\n");
+ html.append("h1, h2 {\n");
+ html.append(" border-bottom-color: #1e293b;\n");
+ html.append("}\n");
+ html.append("a {\n");
+ html.append(" color: #38bdf8;\n");
+ html.append("}\n");
+ html.append("pre, code {\n");
+ html.append(" background-color: #1e293b;\n");
+ html.append(" border: 1px solid #334155;\n");
+ html.append(" color: #e2e8f0;\n");
+ html.append("}\n");
+ html.append("blockquote {\n");
+ html.append(" border-left-color: #475569;\n");
+ html.append(" color: #94a3b8;\n");
+ html.append(" background-color: #0f172a;\n");
+ html.append("}\n");
+ } else {
+ html.append("body {\n");
+ html.append(" background-color: #f8fafc;\n");
+ html.append(" color: #334155;\n");
+ html.append("}\n");
+ html.append("h1, h2, h3, h4, h5, h6 {\n");
+ html.append(" color: #0f172a;\n");
+ html.append("}\n");
+ html.append("h1, h2 {\n");
+ html.append(" border-bottom-color: #e2e8f0;\n");
+ html.append("}\n");
+ html.append("a {\n");
+ html.append(" color: #2563eb;\n");
+ html.append("}\n");
+ html.append("pre, code {\n");
+ html.append(" background-color: #f1f5f9;\n");
+ html.append(" border: 1px solid #e2e8f0;\n");
+ html.append(" color: #334155;\n");
+ html.append("}\n");
+ html.append("blockquote {\n");
+ html.append(" border-left-color: #cbd5e1;\n");
+ html.append(" color: #64748b;\n");
+ html.append(" background-color: #f8fafc;\n");
+ html.append("}\n");
+ }
+ html.append("</style>\n</head>\n<body>\n");
+ html.append(htmlContent);
+ html.append("\n</body>\n</html>");
+
+ // Create a temporary file
+ File tempFile = File.createTempFile("markdown_preview_", ".html");
+ tempFile.deleteOnExit();
+
+ // Write the HTML to the temp file
+ try (OutputStream outputStream = new FileOutputStream(tempFile)) {
+ outputStream.write(html.toString().getBytes(StandardCharsets.UTF_8));
+ }
+
+ // Open in browser
+ EnvironmentUtils.getInstance().openUrl(tempFile.toURI().toString());
+ } catch (Exception e) {
+ new ErrorDialog(
+ hopGui.getShell(), "Error", "Error generating or displaying Markdown
preview", e);
+ }
+ }
+}
diff --git
a/plugins/transforms/textfile/src/main/resources/org/apache/hop/pipeline/transforms/types/messages/messages_en_US.properties
b/plugins/transforms/textfile/src/main/resources/org/apache/hop/pipeline/transforms/types/messages/messages_en_US.properties
index 95bd580999..c5e97cfede 100644
---
a/plugins/transforms/textfile/src/main/resources/org/apache/hop/pipeline/transforms/types/messages/messages_en_US.properties
+++
b/plugins/transforms/textfile/src/main/resources/org/apache/hop/pipeline/transforms/types/messages/messages_en_US.properties
@@ -20,3 +20,4 @@ MarkDownFileType.GuiAction.NewMarkDown.Name=Markdown
MarkDownFileType.GuiAction.NewMarkDown.Tooltip=Create a new Markdown file
MarkDownFileType.ErrorDialog.NewMarkDownCreation.Header=Error creating
Markdown file
MarkDownFileType.ErrorDialog.NewMarkDownCreation.Message=An error occurred
while creating the Markdown file:
+MarkDownFileTypeHandler.Preview.Tooltip=Preview the HTML version of this
Markdown file in a browser
diff --git
a/plugins/transforms/textfile/src/main/resources/org/apache/hop/pipeline/transforms/types/messages/messages_pt_BR.properties
b/plugins/transforms/textfile/src/main/resources/org/apache/hop/pipeline/transforms/types/messages/messages_pt_BR.properties
index 6bc211ef01..e656253da5 100644
---
a/plugins/transforms/textfile/src/main/resources/org/apache/hop/pipeline/transforms/types/messages/messages_pt_BR.properties
+++
b/plugins/transforms/textfile/src/main/resources/org/apache/hop/pipeline/transforms/types/messages/messages_pt_BR.properties
@@ -21,3 +21,4 @@ MarkDownFileType.GuiAction.NewMarkDown.Name=Markdown
MarkDownFileType.GuiAction.NewMarkDown.Tooltip=Criar um novo arquivo Markdown
MarkDownFileType.ErrorDialog.NewMarkDownCreation.Header=Erro ao criar o
arquivo Markdown
MarkDownFileType.ErrorDialog.NewMarkDownCreation.Message=Um erro ocorreu ao
criar o arquivo Markdown:
+MarkDownFileTypeHandler.Preview.Tooltip=Visualizar a versão HTML deste arquivo
Markdown no navegador
diff --git a/pom.xml b/pom.xml
index 89d800c5a1..fc6e04269d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -104,6 +104,7 @@
<azure-sdk-bom.version>1.2.26</azure-sdk-bom.version>
<build.dependenciesDirectory>${project.build.directory}/dependency</build.dependenciesDirectory>
<byte-buddy.version>1.18.7</byte-buddy.version>
+ <commonmark.version>0.29.0</commonmark.version>
<cyclonedx-maven-plugin.version>2.8.1</cyclonedx-maven-plugin.version>
<grpc.version>1.80.0</grpc.version>
<hadoop.version>3.4.2</hadoop.version>