贴出的Start-SoloMD.bat方案启动脚本:
@echo off rem===========================================================================rem Start-SoloMD.bat - one double-click launcherforSoloMD(English only). rem rem Anti-regression launcher. What it does,inorder: rem0. ELEVATION GUARD: WebView2 ignores the extension flags when thehostrem runs elevated(Microsoft security policy), so an elevated launch rem=extension-less SoloMD. If started as admin: close any SoloMD rem(only an elevated process kills high-integrity leftovers),thenrem relaunch THIS launcher through explorer.exe(standard integrity)rem and exit. The de-elevated copy does the rest below. rem1. closes any SoloMD that is already running,thenVERIFIES none rem survives(a surviving instance would win the single-instance rem handoff and leave youinan extension-less window)rem2. starts SoloMD WITH the extension(callout engine + text-stylize rem panel +exportwindow + live-view HTML rendering)rem3. polls the debug portuntilSoloMD's browser is up rem 4. runs the EXTENSION GATE (zz_ext_gate.py): proves via CDP that the rem main-world bridge is alive AND the FAB is really visible - the rem port answering alone is NOT proof of the extension loading rem 5. if the gate fails: force-kills everything, relaunches and rem re-gates, up to 3 attempts, then gives exact recovery steps rem rem Usage: double-click this file, or drop a .md file onto it. rem No Chinese anywhere on purpose, and every helper is called by full path rem (System32) so it behaves the same from Explorer, a terminal, or a script. rem =========================================================================== setlocal enabledelayedexpansion set "HERE=%~dp0" set "EXT=%HERE%solomd-ext-callouts" set "EXE=C:\Program Files\SoloMD\SoloMD.exe" set "SYS=%SystemRoot%\System32" set "GATE=%~dp0zz_ext_gate.py" set "ARGS=--load-extension=%EXT% --disable-extensions-except=%EXT% --remote-debugging-port=9222 --disable-gpu --disable-gpu-compositing --no-sandbox --disable-dev-shm-usage" if not exist "%EXT%\manifest.json" ( echo [ERROR] extension not found: echo %EXT% echo Keep this launcher inside the solomd-onelick folder. pause & exit /b 1 ) if not exist "%EXE%" ( echo [ERROR] SoloMD not found: echo %EXE% pause & exit /b 1 ) if not exist "%GATE%" ( echo [ERROR] extension gate script not found: echo %GATE% echo Keep zz_ext_gate.py next to this launcher. pause & exit /b 1 ) rem ---- find a working python for the gate (PATH first, then common dirs) ---- set "PY=" for %%P in (python py) do ( if not defined PY ( where %%P >nul 2>&1 && set "PY=%%P" ) ) if not defined PY ( for %%F in ("C:\Python314\python.exe" "C:\Python312\python.exe" "C:\Python311\python.exe" "C:\Python310\python.exe") do ( if not defined PY if exist %%F set "PY=%%~fF" ) ) if defined PY "%PY%" --version >nul 2>&1 || set "PY=" rem ---- 0. ELEVATION GUARD (WebView2 security policy) --------------------- rem WebView2 IGNORES WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS when the host rem process runs elevated (Microsoft, "Develop secure WebView2 apps": rem "For an elevated WebView2 host app, WEBVIEW2_* environment variable rem overrides (flags) are ignored, including rem WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS"). An elevated launch therefore rem starts SoloMD WITHOUT the extension and WITHOUT the debug port - the rem exact regression this launcher exists to prevent. When started as rem administrator (e.g. from an elevated file manager): close any SoloMD rem first (only an elevated process can kill high-integrity leftovers), rem then relaunch THIS launcher through explorer.exe, which runs at rem standard integrity. The de-elevated copy performs the normal flow. whoami /groups | "%SYS%\findstr.exe" /c:"S-1-16-12288" >nul 2>&1 if not errorlevel 1 goto ELEVATED net session >nul 2>&1 if not errorlevel 1 goto ELEVATED goto MAIN :ELEVATED echo [ADMIN] This launcher is running as administrator. echo WebView2 ignores the extension flags when elevated echo ^(Microsoft security policy^), so SoloMD would start WITHOUT echo the extension and the debug port would never come up. echo Closing any SoloMD and relaunching without admin ... if not "%~1"=="" > "%HERE%zz-doc-arg.txt" echo %~1 "%SYS%\taskkill.exe" /F /IM SoloMD.exe >nul 2>&1 "%SYS%\ping.exe" -n 3 127.0.0.1 >nul "%SYS%\taskkill.exe" /F /IM SoloMD.exe >nul 2>&1 "%SYS%\ping.exe" -n 2 127.0.0.1 >nul explorer.exe "%~f0" exit /b 0 :MAIN rem a document path handed over by the elevated copy (explorer.exe does rem not forward command-line arguments reliably) set "DOCARG=" if exist "%HERE%zz-doc-arg.txt" ( set /p DOCARG=<"%HERE%zz-doc-arg.txt" del "%HERE%zz-doc-arg.txt" >nul 2>&1 ) if not defined DOCARG if not "%~1"=="" set "DOCARG=%~1" set /a ATTEMPT=0 :ATTEMPT_LAUNCH set /a ATTEMPT+=1 echo. echo ==================== Launch attempt !ATTEMPT! of 3 ==================== rem ---- 1. close every SoloMD, then VERIFY none survives ---- echo [1/4] Closing any SoloMD that is already running ... "%SYS%\taskkill.exe" /IM SoloMD.exe >nul 2>&1 "%SYS%\ping.exe" -n 3 127.0.0.1 >nul "%SYS%\taskkill.exe" /F /IM SoloMD.exe >nul 2>&1 "%SYS%\ping.exe" -n 2 127.0.0.1 >nul set /a K=0 :KILLCHK "%SYS%\tasklist.exe" /nh /fi "imagename eq SoloMD.exe" 2>nul | "%SYS%\find.exe" /i "SoloMD.exe" >nul if errorlevel 1 goto KILLOK set /a K+=1 if !K! GEQ 8 goto KILLSURVIVORS "%SYS%\taskkill.exe" /F /IM SoloMD.exe >nul 2>&1 "%SYS%\ping.exe" -n 2 127.0.0.1 >nul goto KILLCHK :KILLSURVIVORS echo [WARN] a SoloMD.exe is still alive after 8 force-kills. echo It is probably running elevated ^(as admin^). Close its window echo yourself, or right-click THIS launcher -^> Run as administrator: echo the elevated copy closes the leftover, then relaunches itself echo without admin and the extension loads. :KILLOK rem ---- 2. start SoloMD WITH the extension ---- echo [2/4] Starting SoloMD WITH the extension ... set "WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=%ARGS%" if "%DOCARG%"=="" ( start "" "%EXE%" ) else ( if not exist "%DOCARG%" ( echo [ERROR] document not found: %DOCARG% pause & exit /b 1 ) start "" "%EXE%" "%DOCARG%" ) rem ---- 3. wait for the debug port ---- echo [3/4] Waiting for SoloMD to come up ^(port poll^) ... set /a TRIES=0 :WAITPORT set /a TRIES+=1 "%SYS%\curl.exe" -s --max-time 2 http://127.0.0.1:9222/json/version >nul 2>&1 if not errorlevel 1 goto PORTUP if !TRIES! GEQ 12 goto PORTDOWN "%SYS%\ping.exe" -n 2 127.0.0.1 >nul goto WAITPORT :PORTUP rem ---- 4. extension gate: the real anti-regression proof ---- if not defined PY goto NOPY echo [4/4] Verifying the extension really loaded ^(bridge + FAB visible^) ... "%PY%" "%GATE%" if not errorlevel 1 goto VERIFIED echo. echo [GATE FAIL] attempt !ATTEMPT!: extension NOT verified. if !ATTEMPT! LSS 3 goto ATTEMPT_LAUNCH goto FAILED :NOPY echo [4/4] No working python found - cannot run the extension gate. goto UNVERIFIED :VERIFIED echo. echo RESULT: PASS - extension VERIFIED running. echo ^(main-world bridge alive, FAB visible on screen, callouts rendering - echo not just the port answering^) echo. echo The round/export FAB sits in SoloMD's bottom-right corner;clickingechoit opens the floating panel with two tabs: Export and Text-Stylize.echoDetails: manual-start-solomd-walkthrough-20261015.md goto DONE :FAILED echo.echoRESULT: FAIL - the extension did NOT verify after3attempts. echo.echoMost likely a leftover SoloMD window survived the kills ^(startedechofrom SoloMD.exe / the Start menu / a double-clicked .md file, orechorunning elevated^). Fix,inorder:echo1. Close every SoloMD window ^(all of them^).echo2. Task Manager -^>Details -^>end any leftover SoloMD.exe.echo^(If it is notinthe list but a window is open, it runsechoelevated: right-click THIS launcher -^>Run as administrator.^)echo3. Double-click THIS launcher again - never SoloMD.exe itself. goto DONE :UNVERIFIED echo.echoRESULT: UNVERIFIED - the port answers, but that alone does NOTechoprove the extension loaded ^(python was unavailableforthe gate^).echoDo the visual check yourself:echo- SoloMD bottom-right corner: a small"export"pill button ^(FAB^)echo- callout boxes render with colored bordersechoIf either is missing: close every SoloMD window, end leftoverechoSoloMD.exeinTask Manager,thenrun this launcher again. goto DONE :PORTDOWN echo.echoRESULT: FAIL - the debug port never answeredin~25 seconds.echoSoloMD may have crashed at startup. Close every window, end anyecholeftover SoloMD.exe,thendouble-click this launcher again. goto DONE :DONE echo. pauseexit/b0不是仅使用 SoloMD 的自定义 CSS,也不是通过 REST API 添加扩展,而是通过启动器,把一个外部 Chromium 扩展注入 SoloMD 的 Windows WebView2 运行环境。
而且不只是把扩展加载进去,还建立了启动验证机制,确认扩展的通信桥接和浮动按钮真正生效。这比仅判断 SoloMD 有没有启动、调试端口能不能访问更加可靠。
一、该外挂式架构究竟是什么?
根据脚本,可以归纳出下面这套工作机制。
Start-SoloMD.bat
配置浏览器启动参数、清理旧进程、启动应用
SoloMD.exe + WebView2
通过 WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS 接收启动参数
外部扩展
solomd-ext-callouts
CDP 调试接口
127.0.0.1:9222
zz_ext_gate.py
验证主世界桥接、FAB 可见性等运行结果
其中最关键的是这三项参数:
--load-extension=%EXT% --disable-extensions-except=%EXT% --remote-debugging-port=9222它们分别承担以下职责:
| 参数 | 在你这套架构中的作用 |
|---|---|
--load-extension | 要求浏览器加载指定目录中的扩展 |
--disable-extensions-except | 除指定扩展外,不加载其他扩展 |
--remote-debugging-port | 开放 Chrome DevTools Protocol(CDP)调试接口,供验证脚本连接 |
而这句:
set "WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=%ARGS%"是在启动 SoloMD 之前设置进程环境变量,使后续启动的应用能够取得这组浏览器参数。它不是注册 SoloMD 官方插件,而是从启动阶段改变 WebView2 浏览器环境的行为。
微软确实提供了 WebView2 附加浏览器参数机制,并说明以管理员权限运行的宿主程序会忽略通过本地环境变量设置的相关参数。
因此,在脚本中专门处理管理员权限、杀掉旧进程,再重新启动并验证扩展,是针对这条启动链设计的,而不是多余的步骤。
二、这套外挂机制和 5.0.3 新开放的开发者工具有什么关系?
这里需要把四种能力彻底分开。
| 能力 | 你怎样使用 | 实际用途 |
|---|---|---|
| SoloMD 自定义 CSS | 设置中加载或编辑 CSS | 改颜色、字体、间距、布局 |
| v5.0.3 DevTools | F12打开 Inspector | 检查 DOM、CSS 变量、运行时问题 |
| 你的外挂扩展 | BAT 指定扩展目录 | 添加浮动面板、导出功能、文本样式处理、特殊渲染等 |
| REST API / MCP | HTTP 请求或 MCP 工具调用 | 让其他程序、脚本和 AI 操作笔记 |
官方对 Issue #423 的回复明确说明,新增 DevTools 是为了让用户查看 DOM 结构及 CSS 变量,方便编写自定义主题。它没有宣称增加了一个正式的 SoloMD 插件 SDK。
对外挂而言,新功能的价值在于:仍可以继续通过 BAT 加载扩展,再利用 SoloMD 自带的 F12 Inspector 辅助开发,而不必把全部调试工作都交给自己的 CDP 验证脚本。
举例说,可以在 DevTools 中检查:
- 外挂的浮动按钮与 SoloMD 原有界面是否重叠。
- 主界面的主题变量发生变化后,扩展面板是否仍能正确显示。
- 编辑器切换源码、分栏、预览模式后,扩展插入的元素是否仍然存在。
- SoloMD 升级后,旧的 CSS 选择器或 DOM 依赖是否失效。
不过,DevTools 检查的是运行时状态,不会自动把外挂扩展安装成 SoloMD 官方支持的插件。
三、AI对的 BAT 方案的具体评价
外挂启动器设计有一个很好的地方:没有把“调试端口可以访问”误当成“扩展已经加载成功”。
还用zz_ext_gate.py检查桥接状态和 FAB 可见性,并在失败时重试。这种设计能有效避免只启动了 WebView2、却没有真正加载扩展的假阳性。
但从长期维护角度,建议特别注意三个问题。
1. 优先评估--no-sandbox是否真的需要
脚本的参数中包含:
--no-sandbox微软文档明确将它描述为仅用于测试的浏览器级参数,因为它会关闭原本受沙盒保护的进程的沙盒隔离。建议先尝试删除这一项,然后重新运行扩展验证,确认是否仍能正常工作。
2. 不要把 CDP 调试端口当成普通服务端口
--remote-debugging-port=9222这个端口允许调试客户端通过 Chrome DevTools Protocol 检查和控制 WebView 内容。应当只在可信的本机环境使用,不要将它转发到公网或开放给局域网。微软也将 CDP 定位为用于检查、调试和分析 Chromium 内容的协议。
如果日常使用不需要 CDP 验证,可以考虑将调试启动和普通启动分开,减少不必要的调试暴露面。
3. 留意扩展路径中的空格
目前的参数是:
--load-extension=%EXT% --disable-extensions-except=%EXT%如果整个目录路径包含空格,浏览器参数解析可能出现问题。可以在确保参数格式正确的前提下,将路径值加引号,并用实际启动结果验证。
另外,--disable-extensions-except会限制可加载的扩展范围。如果未来还要加载第二个扩展,必须重新设计这一参数,而不是只增加一个目录。
还需要明确一个维护边界:你的 BAT 可以控制 SoloMD 的启动参数,但不能保证未来每个版本的 WebView2、SoloMD DOM 结构或扩展运行机制都保持不变。当前的 Gate 应继续保留;仅凭这份 BAT,我还不能确认manifest.json、主世界桥接脚本和完整扩展代码是否具有版本兼容性。
四、针对外挂的实际使用,推荐怎样工作?
我建议保留现有架构,并用不同工具处理不同层次的问题:
主题开发: 使用F12找到实际的 CSS 变量和 DOM 元素,然后把稳定的外观修改沉淀到 SoloMD 的自定义 CSS 中。
扩展开发: 继续使用Start-SoloMD.bat加载solomd-ext-callouts,通过 Console、Inspector 和zz_ext_gate.py联合排查问题。需要注意,F12 不一定能直接展示扩展所有隔离上下文中的状态,因此仍应以你自己的桥接验证为准。
笔记自动化: 如果以后希望 Python、n8n 或 AI 编程助手批量搜索、读取或修改笔记,应优先考虑 SoloMD 提供的 REST API、MCP 或 CLI,而不要仅仅为了操作笔记去模拟界面行为。
最后,我认为当前的脚本启动外挂的方案已经形成了一套可理解的工程架构:启动器负责环境,浏览器扩展负责附加功能,CDP 负责调试与检查,Gate 负责运行时验证。v5.0.3 增加的 DevTools 是对这套架构的补充,而不是对它的替代。