You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VS Code Markdown预览脚本能否触发扩展操作并保存复选框状态?

解决VS Code Markdown预览中添加可持久化交互复选框的问题

核心思路

复用VS Code内置Markdown扩展能力,通过注入自定义资源、扩展渲染规则、利用Webview消息通道实现需求,全程符合默认安全策略:

  • 注入脚本样式到预览环境,复用已存在的Webview API与扩展通信
  • 自定义Markdown渲染逻辑,给复选框添加唯一标识并预加载保存状态
  • 事件委托处理复选框点击,避免inline脚本违反安全规则
  • 用VS Code内置状态管理器存储状态,实现跨会话持久化

步骤1:配置扩展贡献点

在package.json中注册预览脚本、样式及Markdown-it插件权限:

{
  "contributes": {
    "markdown.previewScripts": [
      "./out/preview-script.js"
    ],
    "markdown.previewStyles": [
      "./out/preview-style.css"
    ],
    "markdownItPlugins": true
  }
}

步骤2:实现Markdown-it插件处理复选框渲染

扩展激活时注册Markdown-it插件,为复选框生成唯一标识,并从存储读取状态设置选中状态:

import * as vscode from 'vscode';
import type MarkdownIt from 'markdown-it';

export function activate(context: vscode.ExtensionContext) {
  // 注册Markdown-it插件
  const pluginDisposable = vscode.extensions.getExtension('vscode.markdown-language-features')?.exports?.registerMarkdownItPlugin(
    'persistent-checkboxes',
    (md: MarkdownIt) => {
      const defaultCheckboxRenderer = md.renderer.rules.checkbox;
      md.renderer.rules.checkbox = (tokens, idx, options, env, self) => {
        const token = tokens[idx];
        const docPath = env?.uri?.fsPath;
        if (!docPath) return defaultCheckboxRenderer!(tokens, idx, options, env, self);
        
        // 生成基于文档路径和复选框文本的唯一ID
        const checkboxText = tokens[idx + 1].content.trim();
        const checkboxId = `${docPath}-${checkboxText}`;
        
        // 从全局存储读取状态并设置复选框选中状态
        const isChecked = context.globalState.get<boolean>(checkboxId, false);
        if (isChecked) token.attrSet('checked', 'checked');
        
        // 添加自定义属性用于交互识别
        token.attrSet('data-checkbox-id', checkboxId);
        
        return defaultCheckboxRenderer!(tokens, idx, options, env, self);
      };
    }
  );

  // 监听预览Webview发来的消息
  const messageDisposable = vscode.window.registerWebviewPanelSerializer('markdown.preview', {
    async deserializeWebviewPanel(panel: vscode.WebviewPanel) {
      panel.webview.onDidReceiveMessage(async (message) => {
        if (message.type === 'checkboxToggle') {
          const { checkboxId, checked } = message;
          // 将状态保存到全局存储
          await context.globalState.update(checkboxId, checked);
        }
      });
    }
  });

  context.subscriptions.push(pluginDisposable!, messageDisposable);
}

步骤3:编写预览脚本处理复选框交互

在preview-script.js中,通过事件委托监听复选框点击,利用Webview API向扩展发送状态变更消息:

// 重复调用acquireVsCodeApi会返回已存在的实例,无需担心冲突
const vscode = acquireVsCodeApi();

// 用事件委托处理所有复选框点击,避免动态元素绑定问题
document.addEventListener('click', (e) => {
  const target = e.target;
  if (target.type === 'checkbox' && target.hasAttribute('data-checkbox-id')) {
    const checkboxId = target.getAttribute('data-checkbox-id');
    vscode.postMessage({
      type: 'checkboxToggle',
      checkboxId,
      checked: target.checked
    });
  }
});

步骤4:样式优化(可选)

在preview-style.css中调整复选框样式,明确标识可交互复选框:

input[type="checkbox"][data-checkbox-id] {
  cursor: pointer;
  accent-color: var(--vscode-button-background);
}

关键注意事项

  • 安全合规:所有交互逻辑通过注入脚本实现,无inline代码,完全符合默认严格安全设置
  • 状态隔离:基于文档路径和复选框文本生成唯一ID,避免不同文档间状态冲突
  • 存储选择:使用globalState实现全局持久化,若需工作区隔离可替换为workspaceState

内容的提问来源于stack exchange,提问作者briank

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 12:25:14