From 8db2bb7a2f929680f591ab1d7d4515419c29712f Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sat, 6 Jun 2026 01:20:04 +0800 Subject: [PATCH 01/19] =?UTF-8?q?feat:=20JSON/XML=20=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E6=A0=BC=E5=BC=8F=E5=8C=96+=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E8=80=8C=E9=9D=9E=20cat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - JSON 运行: python3 -m json.tool(美化输出,非法 JSON 报错) - XML 运行: xmllint --format(格式化,非法 XML 报错),xmllint 为 macOS 自带 --- src-tauri/src/plugins/json.rs | 5 +++-- src-tauri/src/plugins/xml.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src-tauri/src/plugins/json.rs b/src-tauri/src/plugins/json.rs index e228d3a..3e0e4c8 100644 --- a/src-tauri/src/plugins/json.rs +++ b/src-tauri/src/plugins/json.rs @@ -37,7 +37,8 @@ impl LanguagePlugin for JsonPlugin { before_compile: None, extension: String::from("json"), execute_home: None, - run_command: Some(String::from("cat $filename")), + // 运行=格式化+校验:python3 -m json.tool 会美化输出并对非法 JSON 报错 + run_command: Some(String::from("python3 -m json.tool $filename")), after_compile: None, template: Some(String::from("{\n \n}")), timeout: Some(30), @@ -49,6 +50,6 @@ impl LanguagePlugin for JsonPlugin { fn get_default_command(&self) -> String { self.get_config() .and_then(|config| config.run_command.clone()) - .unwrap_or_else(|| "cat".to_string()) + .unwrap_or_else(|| "python3".to_string()) } } diff --git a/src-tauri/src/plugins/xml.rs b/src-tauri/src/plugins/xml.rs index 31e4eeb..a1bd8c5 100644 --- a/src-tauri/src/plugins/xml.rs +++ b/src-tauri/src/plugins/xml.rs @@ -37,7 +37,8 @@ impl LanguagePlugin for XmlPlugin { before_compile: None, extension: String::from("xml"), execute_home: None, - run_command: Some(String::from("cat $filename")), + // 运行=格式化+校验:xmllint(macOS 自带)格式化输出并对非法 XML 报错 + run_command: Some(String::from("xmllint --format $filename")), after_compile: None, template: Some(String::from("\n")), timeout: Some(30), @@ -49,6 +50,6 @@ impl LanguagePlugin for XmlPlugin { fn get_default_command(&self) -> String { self.get_config() .and_then(|config| config.run_command.clone()) - .unwrap_or_else(|| "cat".to_string()) + .unwrap_or_else(|| "xmllint".to_string()) } } From 6b77f91aec7973668011b0830956b329e81f4128 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sat, 6 Jun 2026 01:24:22 +0800 Subject: [PATCH 02/19] =?UTF-8?q?feat:=20JSON=20=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E6=94=B9=E7=94=A8=E5=AE=9A=E5=88=B6=20JSON=20=E6=A0=91?= =?UTF-8?q?=E8=A7=86=E5=9B=BE=EF=BC=8C=E6=92=A4=E9=94=80=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - JSON 插件 console_type 改为 json,运行输出原文交前端渲染 - 新增 JsonView/JsonNode:可折叠 JSON 树(键值着色、顶部两层默认展开、折叠摘要、复制格式化、解析失败回退原文+错误) - App 输出区按 consoleType==='json' 渲染;撤销 XML 的 xmllint 改回 cat --- src-tauri/src/plugins/json.rs | 8 ++-- src-tauri/src/plugins/xml.rs | 5 +- src/App.vue | 9 ++++ src/components/JsonNode.vue | 88 +++++++++++++++++++++++++++++++++++ src/components/JsonView.vue | 78 +++++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+), 7 deletions(-) create mode 100644 src/components/JsonNode.vue create mode 100644 src/components/JsonView.vue diff --git a/src-tauri/src/plugins/json.rs b/src-tauri/src/plugins/json.rs index 3e0e4c8..37ff321 100644 --- a/src-tauri/src/plugins/json.rs +++ b/src-tauri/src/plugins/json.rs @@ -37,12 +37,12 @@ impl LanguagePlugin for JsonPlugin { before_compile: None, extension: String::from("json"), execute_home: None, - // 运行=格式化+校验:python3 -m json.tool 会美化输出并对非法 JSON 报错 - run_command: Some(String::from("python3 -m json.tool $filename")), + // 输出原始 JSON 内容,由前端 JSON 视图(可折叠树)渲染 + run_command: Some(String::from("cat $filename")), after_compile: None, template: Some(String::from("{\n \n}")), timeout: Some(30), - console_type: Some(String::from("console")), + console_type: Some(String::from("json")), icon_path: None, } } @@ -50,6 +50,6 @@ impl LanguagePlugin for JsonPlugin { fn get_default_command(&self) -> String { self.get_config() .and_then(|config| config.run_command.clone()) - .unwrap_or_else(|| "python3".to_string()) + .unwrap_or_else(|| "cat".to_string()) } } diff --git a/src-tauri/src/plugins/xml.rs b/src-tauri/src/plugins/xml.rs index a1bd8c5..31e4eeb 100644 --- a/src-tauri/src/plugins/xml.rs +++ b/src-tauri/src/plugins/xml.rs @@ -37,8 +37,7 @@ impl LanguagePlugin for XmlPlugin { before_compile: None, extension: String::from("xml"), execute_home: None, - // 运行=格式化+校验:xmllint(macOS 自带)格式化输出并对非法 XML 报错 - run_command: Some(String::from("xmllint --format $filename")), + run_command: Some(String::from("cat $filename")), after_compile: None, template: Some(String::from("\n")), timeout: Some(30), @@ -50,6 +49,6 @@ impl LanguagePlugin for XmlPlugin { fn get_default_command(&self) -> String { self.get_config() .and_then(|config| config.run_command.clone()) - .unwrap_or_else(|| "xmllint".to_string()) + .unwrap_or_else(|| "cat".to_string()) } } diff --git a/src/App.vue b/src/App.vue index d60189a..d724b98 100644 --- a/src/App.vue +++ b/src/App.vue @@ -142,6 +142,14 @@ :execution-time="lastExecutionTime" @clear="clearOutput"> + + + @@ -311,6 +319,7 @@ import AppHeader from './components/AppHeader.vue' import CodeEditor from './components/CodeEditor.vue' import ConsoleOutput from './components/ConsoleOutput.vue' import WebOutput from "./components/WebOutput.vue"; +import JsonView from "./components/JsonView.vue"; import StatusBar from './components/StatusBar.vue' import About from './components/About.vue' import Settings from './components/Settings.vue' diff --git a/src/components/JsonNode.vue b/src/components/JsonNode.vue new file mode 100644 index 0000000..a1394c3 --- /dev/null +++ b/src/components/JsonNode.vue @@ -0,0 +1,88 @@ + + + diff --git a/src/components/JsonView.vue b/src/components/JsonView.vue new file mode 100644 index 0000000..f78769e --- /dev/null +++ b/src/components/JsonView.vue @@ -0,0 +1,78 @@ + + + From fe7dafa3f37c6a244fa842e0a091116ae6e73b58 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sat, 6 Jun 2026 07:46:14 +0800 Subject: [PATCH 03/19] =?UTF-8?q?feat:=20=E9=85=8D=E7=BD=AE=E9=A1=B5?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E7=B1=BB=E5=9E=8B=E5=A2=9E=E5=8A=A0=20JSON?= =?UTF-8?q?=20=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit consoleTypes 增加 {label: 'JSON', value: 'json'},可在语言设置里把输出类型选为 JSON 视图 --- src/composables/useLanguageSettings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/composables/useLanguageSettings.ts b/src/composables/useLanguageSettings.ts index dc7514a..1d1b1ba 100644 --- a/src/composables/useLanguageSettings.ts +++ b/src/composables/useLanguageSettings.ts @@ -33,7 +33,7 @@ export function useLanguageSettings(emit: any) } ] - const consoleTypes = [{label: '控制台', value: 'console'}, {label: 'Web', value: 'web'}] + const consoleTypes = [{label: '控制台', value: 'console'}, {label: 'Web', value: 'web'}, {label: 'JSON', value: 'json'}] const { activePlugin, From 38d40732b02d9d93bca37a27e42556f666e5f3cd Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sat, 6 Jun 2026 07:48:48 +0800 Subject: [PATCH 04/19] =?UTF-8?q?feat:=20Markdown=20=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E7=B1=BB=E5=9E=8B=EF=BC=88=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E5=90=8E=E6=B8=B2=E6=9F=93=E9=A2=84=E8=A7=88=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Markdown 插件 console_type 改为 markdown,输出原文交前端渲染 - 新增 MarkdownView:markdown-it 渲染预览(标题/列表/代码/表格/引用等样式) - 配置页输出类型增加 Markdown 选项;App 按 consoleType==='markdown' 渲染 --- src-tauri/src/plugins/markdown.rs | 3 +- src/App.vue | 9 +++++ src/components/MarkdownView.vue | 54 ++++++++++++++++++++++++++ src/composables/useLanguageSettings.ts | 2 +- 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/components/MarkdownView.vue diff --git a/src-tauri/src/plugins/markdown.rs b/src-tauri/src/plugins/markdown.rs index 6336c8c..89e6fa6 100644 --- a/src-tauri/src/plugins/markdown.rs +++ b/src-tauri/src/plugins/markdown.rs @@ -37,11 +37,12 @@ impl LanguagePlugin for MarkdownPlugin { before_compile: None, extension: String::from("md"), execute_home: None, + // 输出原始 Markdown,由前端渲染为预览 run_command: Some(String::from("cat $filename")), after_compile: None, template: Some(String::from("# 标题\n\n在这里输入 Markdown 内容\n")), timeout: Some(30), - console_type: Some(String::from("console")), + console_type: Some(String::from("markdown")), icon_path: None, } } diff --git a/src/App.vue b/src/App.vue index d724b98..f32ac8e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -150,6 +150,14 @@ :is-running="isRunning" :execution-time="lastExecutionTime" @clear="clearOutput"/> + + + @@ -320,6 +328,7 @@ import CodeEditor from './components/CodeEditor.vue' import ConsoleOutput from './components/ConsoleOutput.vue' import WebOutput from "./components/WebOutput.vue"; import JsonView from "./components/JsonView.vue"; +import MarkdownView from "./components/MarkdownView.vue"; import StatusBar from './components/StatusBar.vue' import About from './components/About.vue' import Settings from './components/Settings.vue' diff --git a/src/components/MarkdownView.vue b/src/components/MarkdownView.vue new file mode 100644 index 0000000..a7b47f8 --- /dev/null +++ b/src/components/MarkdownView.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/src/composables/useLanguageSettings.ts b/src/composables/useLanguageSettings.ts index 1d1b1ba..b1192ae 100644 --- a/src/composables/useLanguageSettings.ts +++ b/src/composables/useLanguageSettings.ts @@ -33,7 +33,7 @@ export function useLanguageSettings(emit: any) } ] - const consoleTypes = [{label: '控制台', value: 'console'}, {label: 'Web', value: 'web'}, {label: 'JSON', value: 'json'}] + const consoleTypes = [{label: '控制台', value: 'console'}, {label: 'Web', value: 'web'}, {label: 'JSON', value: 'json'}, {label: 'Markdown', value: 'markdown'}] const { activePlugin, From fedce4f7b41e3f012262e34a160fecea65931f7c Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sat, 6 Jun 2026 07:51:44 +0800 Subject: [PATCH 05/19] =?UTF-8?q?fix:=20Markdown=20=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=86=85=E5=B5=8C=20HTML=EF=BC=88DOMPurify?= =?UTF-8?q?=20=E5=87=80=E5=8C=96=E9=98=B2=20XSS=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MarkdownView 与 PreviewPanel 的 markdown-it 开启 html:true,渲染结果经 DOMPurify 净化后再 v-html,既能渲染 md 中的 HTML 又防止脚本注入 --- package.json | 1 + src/components/MarkdownView.vue | 6 ++++-- src/components/PreviewPanel.vue | 6 ++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 1f6fa75..8056559 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", "codemirror": "^6.0.2", + "dompurify": "^3.4.8", "lodash-es": "^4.17.21", "lucide-vue-next": "^0.539.0", "markdown-it": "^14.2.0", diff --git a/src/components/MarkdownView.vue b/src/components/MarkdownView.vue index a7b47f8..d7239fe 100644 --- a/src/components/MarkdownView.vue +++ b/src/components/MarkdownView.vue @@ -23,6 +23,7 @@ import {computed} from 'vue' import {FileText} from 'lucide-vue-next' import MarkdownIt from 'markdown-it' +import DOMPurify from 'dompurify' const props = defineProps<{ output: string @@ -31,8 +32,9 @@ const props = defineProps<{ }>() const emit = defineEmits<{ clear: [] }>() -const md = new MarkdownIt({html: false, linkify: true, breaks: true}) -const rendered = computed(() => md.render(props.output || '')) +// 允许 Markdown 中的 HTML,渲染后用 DOMPurify 净化(去除 script 等危险内容)防 XSS +const md = new MarkdownIt({html: true, linkify: true, breaks: true}) +const rendered = computed(() => DOMPurify.sanitize(md.render(props.output || '')))