diff --git a/app/build.gradle b/app/build.gradle index e3f5e7fd..4d94df8b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,6 +37,12 @@ dependencies { api 'com.vladsch.flexmark:flexmark-util-format:0.64.8' api 'com.vladsch.flexmark:flexmark-util-html:0.64.8' api 'com.vladsch.flexmark:flexmark-util-visitor:0.64.8' + api 'com.vladsch.flexmark:flexmark-ext-tables:0.64.8' + api 'com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.64.8' + api 'com.vladsch.flexmark:flexmark-ext-gfm-tasklist:0.64.8' + api 'com.vladsch.flexmark:flexmark-ext-footnotes:0.64.8' + api 'com.vladsch.flexmark:flexmark-ext-definition:0.64.8' + api 'com.vladsch.flexmark:flexmark-ext-anchorlink:0.64.8' api files("$rootDir/gradle/gradle_scripts/markdowngenerator-1.3.1.1.jar") api files("$rootDir/gradle/gradle_scripts/vernacular-1.16.jar") diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java index 9859cd03..799d2f5b 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java @@ -17,6 +17,7 @@ import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreColor; import io.xpipe.app.update.XPipeDistributionType; import io.xpipe.app.util.*; +import io.xpipe.core.util.XPipeInstallation; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleStringProperty; import javafx.beans.value.ObservableDoubleValue; @@ -449,7 +450,7 @@ public abstract class StoreEntryComp extends SimpleComp { private static String getDefaultNotes() { if (DEFAULT_NOTES == null) { AppResources.with(AppResources.XPIPE_MODULE, "misc/notes_default.md", f -> { - DEFAULT_NOTES = Files.readString(f); + DEFAULT_NOTES = Files.readString(f).replace("__IMAGE__", XPipeInstallation.getLocalDefaultInstallationIcon().toString()); }); } return DEFAULT_NOTES; diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreNotesComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreNotesComp.java index a3f9ee3b..a7c868c7 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreNotesComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreNotesComp.java @@ -12,11 +12,10 @@ import io.xpipe.app.fxcomps.impl.IconButtonComp; import io.xpipe.app.fxcomps.util.BindingsHelper; import io.xpipe.app.storage.DataStorage; import javafx.application.Platform; +import javafx.beans.property.Property; import javafx.beans.property.SimpleStringProperty; import javafx.event.ActionEvent; -import javafx.geometry.Insets; import javafx.scene.control.Button; -import javafx.scene.layout.Region; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -37,20 +36,50 @@ public class StoreNotesComp extends Comp { .styleClass("notes-button") .grow(false, true) .hide(BindingsHelper.map(n, s -> s.getCommited() == null && s.getCurrent() == null)) - .padding(new Insets(5)) .createStructure().get(); button.prefWidthProperty().bind(button.heightProperty()); var prop = new SimpleStringProperty(n.getValue().getCurrent()); - var md = new MarkdownEditorComp(prop,"notes-" + wrapper.getName().getValue()).createStructure(); var popover = new AtomicReference(); + button.setOnAction(e -> { + if (n.getValue().getCurrent() == null) { + return; + } + + if (popover.get() != null && popover.get().isShowing()) { + e.consume(); + return; + } + + popover.set(createPopover(popover, prop)); + popover.get().show(button); + e.consume(); + }); + prop.addListener((observable, oldValue, newValue) -> { + n.setValue(new StoreNotes(n.getValue().getCommited(), newValue)); + }); + n.addListener((observable, oldValue, s) -> { + prop.set(s.getCurrent()); + if (s.getCurrent() != null && oldValue.getCommited() == null && oldValue.isCommited()) { + Platform.runLater(() -> { + popover.set(createPopover(popover, prop)); + popover.get().show(button); + }); + } + }); + return new Structure(popover.get(), button); + } + + private Popover createPopover(AtomicReference ref, Property prop) { + var n = wrapper.getNotes(); + var md = new MarkdownEditorComp(prop, "notes-" + wrapper.getName().getValue()).createStructure(); var dialog = new DialogComp() { @Override protected void finish() { n.setValue(new StoreNotes(n.getValue().getCurrent(), n.getValue().getCurrent())); - popover.get().hide(); + ref.get().hide(); } @Override @@ -68,7 +97,7 @@ public class StoreNotesComp extends Comp { @Override protected List> customButtons() { return List.of(new ButtonComp(AppI18n.observable("cancel"), () -> { - popover.get().hide(); + ref.get().hide(); })); } @@ -78,23 +107,25 @@ public class StoreNotesComp extends Comp { } }.createRegion(); - popover.set(createPopover(dialog)); - button.setOnAction(e -> { - if (n.getValue().getCurrent() == null) { - return; + var popover = new Popover(dialog); + popover.setCloseButtonEnabled(true); + popover.setHeaderAlwaysVisible(true); + popover.setDetachable(true); + popover.setTitle(wrapper.getName().getValue()); + popover.setMaxWidth(400); + popover.setHeight(600); + popover.showingProperty().addListener((observable, oldValue, newValue) -> { + if (!newValue) { + n.setValue(new StoreNotes(n.getValue().getCommited(), n.getValue().getCommited())); + DataStorage.get().saveAsync(); + ref.set(null); } - - if (popover.get().isShowing()) { - e.consume(); - return; - } - - popover.get().show(button); - e.consume(); }); + AppFont.small(popover.getContentNode()); + md.getEditButton().addEventFilter(ActionEvent.ANY, event -> { - if (!popover.get().isDetached()) { - popover.get().setDetached(true); + if (!popover.isDetached()) { + popover.setDetached(true); event.consume(); Platform.runLater(() -> { Platform.runLater(() -> { @@ -103,35 +134,7 @@ public class StoreNotesComp extends Comp { }); } }); - popover.get().showingProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue) { - n.setValue(new StoreNotes(n.getValue().getCommited(), n.getValue().getCommited())); - DataStorage.get().saveAsync(); - } - }); - prop.addListener((observable, oldValue, newValue) -> { - n.setValue(new StoreNotes(n.getValue().getCommited(), newValue)); - }); - n.addListener((observable, oldValue, s) -> { - prop.set(s.getCurrent()); - if (s.getCurrent() != null && oldValue.getCommited() == null && oldValue.isCommited()) { - Platform.runLater(() -> { - popover.get().show(button); - }); - } - }); - return new Structure(popover.get(), button); - } - private Popover createPopover(Region content) { - var popover = new Popover(content); - popover.setCloseButtonEnabled(true); - popover.setHeaderAlwaysVisible(true); - popover.setDetachable(true); - popover.setTitle(wrapper.getName().getValue()); - popover.setMaxWidth(400); - popover.setHeight(600); - AppFont.small(popover.getContentNode()); return popover; } diff --git a/app/src/main/java/io/xpipe/app/util/MarkdownHelper.java b/app/src/main/java/io/xpipe/app/util/MarkdownHelper.java index 69841224..63916701 100644 --- a/app/src/main/java/io/xpipe/app/util/MarkdownHelper.java +++ b/app/src/main/java/io/xpipe/app/util/MarkdownHelper.java @@ -1,16 +1,39 @@ package io.xpipe.app.util; +import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension; +import com.vladsch.flexmark.ext.definition.DefinitionExtension; +import com.vladsch.flexmark.ext.footnotes.FootnoteExtension; +import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension; +import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension; +import com.vladsch.flexmark.ext.tables.TablesExtension; import com.vladsch.flexmark.html.HtmlRenderer; import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.util.ast.Document; import com.vladsch.flexmark.util.data.MutableDataSet; +import java.util.Arrays; import java.util.function.UnaryOperator; public class MarkdownHelper { public static String toHtml(String value, UnaryOperator htmlTransformation) { - MutableDataSet options = new MutableDataSet(); + MutableDataSet options = new MutableDataSet().set(Parser.EXTENSIONS, Arrays.asList( + StrikethroughExtension.create(), + TaskListExtension.create(), + TablesExtension.create(), + FootnoteExtension.create(), + DefinitionExtension.create(), + AnchorLinkExtension.create() + )) + .set(FootnoteExtension.FOOTNOTE_BACK_LINK_REF_CLASS,"footnotes") + .set(TablesExtension.WITH_CAPTION, false) + .set(TablesExtension.COLUMN_SPANS, false) + .set(TablesExtension.MIN_HEADER_ROWS, 1) + .set(TablesExtension.MAX_HEADER_ROWS, 1) + .set(TablesExtension.APPEND_MISSING_COLUMNS, true) + .set(TablesExtension.DISCARD_EXTRA_COLUMNS, true) + .set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true) + .set(HtmlRenderer.GENERATE_HEADER_ID,true); Parser parser = Parser.builder(options).build(); HtmlRenderer renderer = HtmlRenderer.builder(options).build(); Document document = parser.parse(value); diff --git a/app/src/main/resources/io/xpipe/app/resources/misc/notes_default.md b/app/src/main/resources/io/xpipe/app/resources/misc/notes_default.md index 094f39f1..a0ef4938 100644 --- a/app/src/main/resources/io/xpipe/app/resources/misc/notes_default.md +++ b/app/src/main/resources/io/xpipe/app/resources/misc/notes_default.md @@ -99,29 +99,14 @@ doc](#an-h2-header). Here's a footnote [^1]. Tables can look like this: -size material color ----- ------------ ------------ -9 leather brown -10 hemp canvas natural -11 glass transparent +| size | material | color | +|------|-------------|-------------| +| 9 | leather | brown | +| 10 | hemp canvas | natural | +| 11 | glass | transparent | Table: Shoes, their sizes, and what they're made of -(The above is the caption for the table.) Pandoc also supports -multi-line tables: - --------- ----------------------- -keyword text --------- ----------------------- -red Sunsets, apples, and -other red or reddish -things. - -green Leaves, grass, frogs -and other things it's -not easy being. --------- ----------------------- - A horizontal rule follows. *** @@ -146,7 +131,7 @@ Here's a "line block": and images can be specified like so: -![example image](example-image.jpg "An exemplary image") +![example image](__IMAGE__ "An exemplary image") Inline math equations go in like so: $\omega = d\phi / dt$. Display math should get its own line and be put in in double-dollarsigns: diff --git a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css index d2ff2c73..5502735e 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css @@ -76,7 +76,7 @@ -fx-opacity: 1.0; } -.expand-button:hover, .root:key-navigation .expand-button:focused, .quick-access-button:hover, .root:key-navigation .quick-access-button:focused { +.notes-button:hover, .root:key-navigation .notes-button:focused, .expand-button:hover, .root:key-navigation .expand-button:focused, .quick-access-button:hover, .root:key-navigation .quick-access-button:focused { -fx-background-color: -color-neutral-muted; } diff --git a/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-dark.css b/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-dark.css index 8e961a87..b818a8b7 100644 --- a/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-dark.css +++ b/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-dark.css @@ -7,7 +7,7 @@ html { -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; margin: 0; - padding: 0 1em 1em 1em; + padding: 1em; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; @@ -560,17 +560,21 @@ html { margin-bottom: 16px; } -.markdown-body table th { +.markdown-body table { + color: #c9d1d9; +} + +.markdown-body table thead th { font-weight: 600; } -.markdown-body table th, -.markdown-body table td { - padding: 6px 13px; +.markdown-body table thead th, +.markdown-body table tbody td { + padding: 6px 13px !important; border: 1px solid #30363d; } -.markdown-body table tr { +.markdown-body table thead tr .markdown-body table tbody tr { background-color: #0d1117; border-top: 1px solid #21262d; } diff --git a/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-light.css b/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-light.css index 67a56109..1c792edb 100644 --- a/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-light.css +++ b/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-light.css @@ -662,17 +662,21 @@ html { margin-bottom: 16px; } -.markdown-body table th { +.markdown-body table { + color: #24292f; +} + +.markdown-body table thead th { font-weight: 600; } -.markdown-body table th, -.markdown-body table td { - padding: 6px 13px; +.markdown-body table thead th, +.markdown-body table tbody td { + padding: 6px 13px !important; border: 1px solid #d0d7de; } -.markdown-body table tr { +.markdown-body table thead tr .markdown-body table tbody tr { background-color: #ffffff; border-top: 1px solid hsla(210, 18%, 87%, 1); } diff --git a/gradle/gradle_scripts/modules.gradle b/gradle/gradle_scripts/modules.gradle index 41a253fb..ffa3bb7a 100644 --- a/gradle/gradle_scripts/modules.gradle +++ b/gradle/gradle_scripts/modules.gradle @@ -42,6 +42,12 @@ extraJavaModuleInfo { mergeJar('com.vladsch.flexmark:flexmark-util-collection') mergeJar('com.vladsch.flexmark:flexmark-util-misc') mergeJar('com.vladsch.flexmark:flexmark-util-visitor') + mergeJar('com.vladsch.flexmark:flexmark-ext-tables') + mergeJar('com.vladsch.flexmark:flexmark-ext-gfm-strikethrough') + mergeJar('com.vladsch.flexmark:flexmark-ext-gfm-tasklist') + mergeJar('com.vladsch.flexmark:flexmark-ext-footnotes') + mergeJar('com.vladsch.flexmark:flexmark-ext-definition') + mergeJar('com.vladsch.flexmark:flexmark-ext-anchorlink') exportAllPackages() } }