Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add highlightjs code block extension to the editor #24

Merged
merged 9 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
510 changes: 510 additions & 0 deletions .editorconfig

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ jobs:
ci:
uses: halo-sigs/reusable-workflows/.github/workflows/plugin-ci.yaml@v1
with:
skip-node-setup: true
ui-path: "ui"
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,5 @@ application-local.yml
application-local.yaml
application-local.properties

/admin-frontend/node_modules/
/workplace/
/src/main/resources/console/
22 changes: 22 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
plugins {
id "com.github.node-gradle.node" version "7.0.2"
id "run.halo.plugin.devtools" version "0.0.7"
id "io.freefair.lombok" version "8.0.1"
id 'java'
Expand Down Expand Up @@ -30,6 +31,27 @@ test {
useJUnitPlatform()
}

node {
nodeProjectDir = file("${project.projectDir}/ui")
}

tasks.register('buildFrontend', PnpmTask) {
args = ['build']
dependsOn('installDepsForUI')
}

tasks.register('installDepsForUI', PnpmTask) {
args = ['install']
}


build {
// build frontend before build
tasks.named('compileJava').configure {
dependsOn('buildFrontend')
}
}

halo {
version = "2.12.2"
}
101 changes: 40 additions & 61 deletions src/main/java/run/halo/highlightjs/HighlightJSHeadProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
import org.springframework.web.util.pattern.PathPatternParser;
import org.springframework.web.util.pattern.PathPatternRouteMatcher;
import org.springframework.web.util.pattern.PatternParseException;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.context.Contexts;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IModelFactory;
import org.thymeleaf.processor.element.IElementModelStructureHandler;
import org.thymeleaf.spring6.SpringTemplateEngine;
import org.thymeleaf.web.IWebRequest;
import reactor.core.publisher.Mono;
import run.halo.app.plugin.ReactiveSettingFetcher;
Expand All @@ -37,10 +40,16 @@ public class HighlightJSHeadProcessor implements TemplateHeadProcessor {

private final RouteMatcher routeMatcher = createRouteMatcher();

private final TemplateEngine templateEngine = new SpringTemplateEngine();

@Override
public Mono<Void> process(ITemplateContext context, IModel model, IElementModelStructureHandler structureHandler) {
return reactiveSettingFetcher.fetch("basic", BasicConfig.class)
.doOnNext(basicConfig -> {
if (!basicConfig.enableThemeSideRender) {
return;
}

if (mismatchedRoute(context, basicConfig) && notContentTemplate(context)) {
return;
}
Expand All @@ -57,65 +66,35 @@ public Mono<Void> process(ITemplateContext context, IModel model, IElementModelS
}

private String highlightJsScript(BasicConfig basicConfig) {
StringBuilder injectCode = new StringBuilder();
injectCode.append("""
<!-- PluginHighlightJS start -->
<link href="/plugins/PluginHighlightJS/assets/static/styles/%s" rel="stylesheet"/>
<script defer src="/plugins/PluginHighlightJS/assets/static/highlight.min.js"></script>
""".formatted(basicConfig.getStyle()));

if (basicConfig.isShowCopyButton()) {
injectCode.append("""
<link href="/plugins/PluginHighlightJS/assets/static/plugins/highlightjs-copy.min.css" rel="stylesheet"/>
<script defer src="/plugins/PluginHighlightJS/assets/static/plugins/highlightjs-copy.min.js"></script>
""");
}

injectCode.append("""
<link href="/plugins/PluginHighlightJS/assets/static/plugins/override.css" rel="stylesheet"/>
<script>
""");

injectCode.append("""
document.addEventListener("DOMContentLoaded", async function () {
""");

if (basicConfig.isShowCopyButton()) {
injectCode.append("""
hljs.addPlugin(new CopyButtonPlugin({ lang: "zh"}));
""");
}

var extraLanguages = basicConfig.getExtra_languages();

injectCode.append("""
const extraLanguages = "%s".split(",").filter((x) => x);
for (let i = 0; i < extraLanguages.length; i++) {
const lang = extraLanguages[i];
if (lang) {
await loadScript(`/plugins/PluginHighlightJS/assets/static/languages/${lang}.min.js`);
}
}

document.querySelectorAll("pre code").forEach((el) => {
hljs.highlightElement(el);
});
});

function loadScript(url) {
return new Promise(function (resolve, reject) {
const script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
</script>
<!-- PluginHighlightJS end -->
""".formatted(extraLanguages != null ? extraLanguages : ""));
return injectCode.toString();
var context = new Context();
context.setVariable("config", basicConfig);
var code = templateEngine.process(
"""
<!-- PluginHighlightJS start -->
<link th:href="|/plugins/PluginHighlightJS/assets/static/styles/${config.style}|" rel="stylesheet"/>
<script defer src="/plugins/PluginHighlightJS/assets/static/highlight.min.js"></script>

<th:block th:if="${config.showCopyButton}">
<link href="/plugins/PluginHighlightJS/assets/static/plugins/highlightjs-copy.min.css" rel="stylesheet"/>
<script defer src="/plugins/PluginHighlightJS/assets/static/plugins/highlightjs-copy.min.js"></script>
</th:block>

<link href="/plugins/PluginHighlightJS/assets/static/plugins/override.css" rel="stylesheet"/>

<script th:inline="javascript">
document.addEventListener("DOMContentLoaded", function () {
[# th:if="${config.showCopyButton}"]
hljs.addPlugin(new CopyButtonPlugin({ lang: "zh"}));
[/]
document.querySelectorAll("pre code").forEach((el) => {
hljs.highlightElement(el);
});
});
</script>
<!-- PluginHighlightJS end -->
""",
context);
return code;
}

public boolean notContentTemplate(ITemplateContext context) {
Expand Down Expand Up @@ -159,7 +138,7 @@ public static class PathMatchRule {

@Data
public static class BasicConfig {
String extra_languages;
boolean enableThemeSideRender;
String style;
List<PathMatchRule> rules;
boolean showCopyButton;
Expand All @@ -168,4 +147,4 @@ public List<PathMatchRule> nullSafeRules() {
return ObjectUtils.defaultIfNull(rules, List.of());
}
}
}
}
18 changes: 12 additions & 6 deletions src/main/resources/extensions/settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,28 @@ spec:
- group: basic
label: 基本设置
formSchema:
- $formkit: checkbox
name: enableThemeSideRender
id: enableThemeSideRender
label: 启用主题侧渲染
help: 启用之后会自动渲染文章中的代码块,以及根据下面的页面匹配规则渲染代码块。取消勾选之后,此插件仅会在编辑器中渲染代码块。
value: true
- $formkit: repeater
if: "$get(enableThemeSideRender).value"
name: rules
id: rules
key: rules
label: 页面匹配规则
value: [ ]
value: []
children:
- $formkit: text
name: pathPattern
label: 路径匹配
value: ""
validation: required
help: 用于匹配页面路径的符合 Ant-style 的表达式 ,如:/archives/**,被匹配的页面将会被添加代码高亮功能
- $formkit: textarea
name: extra_languages
label: 代码块额外高亮语言
help: "默认已经包含:php,diff,java,css,bash,json,perl,swift,plaintext,ruby,yaml,sql,vbnet,ini,scss,less,cpp,typescript,csharp,rust,python,python-repl,objectivec,r,shell,makefile,go,xml,markdown,lua,javascript,php-template,kotlin,c 如果需要添加其他语言,请按照相同格式添加。"
- $formkit: select
if: "$get(enableThemeSideRender).value"
name: style
label: 代码块高亮主题
value: "default.min.css"
Expand Down Expand Up @@ -170,7 +176,7 @@ spec:
- value: "xt256.min.css"
label: "xt256.min.css"
- $formkit: checkbox
if: "$get(enableThemeSideRender).value"
label: 显示复制按钮
value: false
name: showCopyButton

2 changes: 1 addition & 1 deletion src/main/resources/plugin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ metadata:
spec:
enabled: true
version: 1.0.0
requires: ">=2.10.0"
requires: ">=2.19.0"
author:
name: Halo
website: https://github.com/halo-dev
Expand Down
Loading