/** * 用户手册只读预览面板 */ import * as vscode from "vscode"; export class UserManualPanel { public static currentPanel: UserManualPanel | undefined; private readonly _panel: vscode.WebviewPanel; private _disposables: vscode.Disposable[] = []; private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) { this._panel = panel; this._panel.onDidDispose(() => this.dispose(), null, this._disposables); this._update(extensionUri); } public static render(extensionUri: vscode.Uri) { if (UserManualPanel.currentPanel) { UserManualPanel.currentPanel._panel.reveal(vscode.ViewColumn.One); } else { const panel = vscode.window.createWebviewPanel( "userManual", "IC Coder 用户手册", vscode.ViewColumn.One, { enableScripts: true, localResourceRoots: [vscode.Uri.joinPath(extensionUri, "media")], }, ); UserManualPanel.currentPanel = new UserManualPanel(panel, extensionUri); } } private async _update(extensionUri: vscode.Uri) { const manualPath = vscode.Uri.joinPath( extensionUri, "media", "USER_MANUAL.md", ); const markdown = await vscode.workspace.fs.readFile(manualPath); const content = Buffer.from(markdown).toString("utf-8"); this._panel.webview.html = await this._getHtmlContent( content, extensionUri, ); } private async _getHtmlContent( markdown: string, extensionUri: vscode.Uri, ): Promise { let inCodeBlock = false; let inTable = false; let tableRows: string[] = []; const lines: string[] = []; // 先处理图片 markdown = markdown.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, src) => { const imgUri = this._panel.webview.asWebviewUri( vscode.Uri.joinPath(extensionUri, "media", src), ); return `${alt}`; }); markdown.split("\n").forEach((line) => { // 代码块 if (line.startsWith("```")) { if (inCodeBlock) { lines.push(""); inCodeBlock = false; } else { lines.push("
");
          inCodeBlock = true;
        }
        return;
      }
      if (inCodeBlock) {
        lines.push(line);
        return;
      }

      // 表格
      if (line.startsWith("|")) {
        if (!inTable) inTable = true;
        tableRows.push(line);
        return;
      } else if (inTable) {
        // 表格结束
        const headers = tableRows[0]
          .split("|")
          .filter((c) => c.trim())
          .map((h) => `${h.trim()}`)
          .join("");
        const body = tableRows
          .slice(2)
          .map(
            (r) =>
              "" +
              r
                .split("|")
                .filter((c) => c.trim())
                .map((c) => `${c.trim()}`)
                .join("") +
              "",
          )
          .join("");
        lines.push(
          `${headers}${body}
`, ); tableRows = []; inTable = false; } // 其他行 if (line === "---") lines.push("
"); else if (line.startsWith("#### ")) lines.push(`

${line.slice(5)}

`); else if (line.startsWith("### ")) lines.push(`

${line.slice(4)}

`); else if (line.startsWith("## ")) lines.push(`

${line.slice(3)}

`); else if (line.startsWith("# ")) lines.push(`

${line.slice(2)}

`); else if (line.startsWith("- ")) lines.push( `
  • ${line.slice(2).replace(/\*\*(.+?)\*\*/g, "$1")}
  • `, ); else if (line.trim() === "") lines.push("

    "); else lines.push( `

    ${line.replace(/\*\*(.+?)\*\*/g, "$1").replace(/\[(.+?)\]\((.+?)\)/g, '$1')}

    `, ); }); const html = lines .join("\n") .replace(/((?:
  • .*<\/li>\n?)+)/g, "
      $1
    "); return ` ${html} `; } public dispose() { UserManualPanel.currentPanel = undefined; this._panel.dispose(); while (this._disposables.length) { this._disposables.pop()?.dispose(); } } }