From 12cc353964b4d43c162f5f53181a02028e92f636 Mon Sep 17 00:00:00 2001 From: Aisland Date: Tue, 18 Aug 2026 00:50:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20txt/tsx/css=E7=AD=89=E5=85=A8=E9=83=A8?= =?UTF-8?q?=E6=96=87=E6=9C=AC=E5=BC=80=E7=BC=96=E8=BE=91=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=20+=20=E4=BB=A3=E7=A0=81=E8=AF=AD=E6=B3=95=E7=9D=80=E8=89=B2(h?= =?UTF-8?q?ighlight.js=20GitHub=20Dark=E9=85=8D=E8=89=B2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01_content/package-lock.json | 11 +++ 01_content/package.json | 1 + 01_content/src/client/split.tsx | 73 ++++++++++++++----- 01_content/src/client/styles.ts | 14 ++++ .../worklogs/2026-08-16_worktable-v2.md | 23 +++--- 04_test/functional-diag.cjs | 19 ++++- 6 files changed, 106 insertions(+), 35 deletions(-) diff --git a/01_content/package-lock.json b/01_content/package-lock.json index 440d28e..82d4e23 100644 --- a/01_content/package-lock.json +++ b/01_content/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "devDependencies": { "esbuild": "^0.25.0", + "highlight.js": "^11.12.0", "markdown-it": "^15.0.0", "xterm": "^5.3.0" }, @@ -544,6 +545,16 @@ "@esbuild/win32-x64": "0.25.12" } }, + "node_modules/highlight.js": { + "version": "11.12.0", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.12.0.tgz", + "integrity": "sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/linkify-it": { "version": "6.1.0", "resolved": "https://registry.npmmirror.com/linkify-it/-/linkify-it-6.1.0.tgz", diff --git a/01_content/package.json b/01_content/package.json index 17a177d..3032b6f 100644 --- a/01_content/package.json +++ b/01_content/package.json @@ -52,6 +52,7 @@ }, "devDependencies": { "esbuild": "^0.25.0", + "highlight.js": "^11.12.0", "markdown-it": "^15.0.0", "xterm": "^5.3.0" } diff --git a/01_content/src/client/split.tsx b/01_content/src/client/split.tsx index 4be6edd..8fc2864 100644 --- a/01_content/src/client/split.tsx +++ b/01_content/src/client/split.tsx @@ -2,6 +2,16 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { Terminal } from 'xterm' import 'xterm/css/xterm.css' import MarkdownIt from 'markdown-it' +import hljs from 'highlight.js/lib/core' +import hljsTypescript from 'highlight.js/lib/languages/typescript' +import hljsJavascript from 'highlight.js/lib/languages/javascript' +import hljsCss from 'highlight.js/lib/languages/css' +import hljsJson from 'highlight.js/lib/languages/json' + +hljs.registerLanguage('typescript', hljsTypescript) +hljs.registerLanguage('javascript', hljsJavascript) +hljs.registerLanguage('css', hljsCss) +hljs.registerLanguage('json', hljsJson) /** * dsh-worktable 乐高式工作区 M1:通用分栏引擎(PRD §13)。 @@ -1121,7 +1131,23 @@ function FileViewer(props: { path: string }) { return } -/** MD/TXT 文本预览(fetch 原文 → MD 渲染或
 等宽展示);MD 支持编辑/预览自由切换并可保存回磁盘 */
+/** 代码文件语言映射(预览语法着色) */
+const CODE_LANGS: Record = { ts: 'typescript', tsx: 'typescript', js: 'javascript', jsx: 'javascript', css: 'css', json: 'json' }
+const CODE_EXTS = /[.](tsx|ts|jsx|js|css|json)$/i
+
+function escapeHtml(s: string): string {
+  return s.replace(/&/g, '&').replace(//g, '>')
+}
+
+function codeHtml(text: string, ext: string): string {
+  const lang = CODE_LANGS[ext] ?? ''
+  if (lang) {
+    try { return hljs.highlight(text, { language: lang }).value } catch { return escapeHtml(text) }
+  }
+  return escapeHtml(text)
+}
+
+/** 文本预览(fetch 原文 → MD 渲染 / 代码高亮 / 
 等宽展示);全部文本类型支持编辑/预览切换并可保存回磁盘 */
 function TextViewer(props: { path: string; fileUrl: string; isMd: boolean }) {
   const [text, setText] = useState(null)
   const [error, setError] = useState('')
@@ -1167,20 +1193,22 @@ function TextViewer(props: { path: string; fileUrl: string; isMd: boolean }) {
   if (text == null) {
     return 
{T('file.loading')}
} - if (props.isMd) { - return ( - <> -
- - - {mode === 'edit' && ( - - )} - {saveFail && {T('file.saveFail')}} -
- {mode === 'edit' - ?