Releases: fwzheng/mdex
Release list
MDeX-2.2.1
v2.2.1
AI 辅助弹窗升级为独立 OS 窗口:现在 AI 助手面板是一个可拖出主界面的独立操作系统窗口,支持跨屏使用。对话框 B 区高度由 JS 内容动态驱动,并实现了窗口自动贴合功能,以补偿 Tauri
setSize方法在计算外尺寸时的偏移。此外,新增了光标位置可视化功能与多窗口生命周期管理。
The AI assistant pane is now a separate OS window that can be dragged outside the main interface, supporting cross-screen use. The height of dialog B-zone is dynamically driven by JS content, and the window automatically fits to compensate for the outer-size offset in Tauri'ssetSizemethod. Additionally, cursor-position visualization and multi-window lifecycle management have been added.
新增 / Features
- AI 辅助弹窗升级为独立 OS 窗口:选中文字或光标处按 ⌘J(或右键)即可弹出独立的 AI 辅助窗口。该窗口可被拖出 MDeX 主窗口至桌面或第二屏幕,不再受主窗口边框限制。窗口内的所有功能(改写 / diff / 应用 / 多轮对话)均完整保留;执行
apply操作后,窗口将保持开启状态(编辑区会自动清空)。AI pane is now a separate OS window — Invoked by ⌘J / right-click, it can be dragged outside the main MDeX window onto the desktop or second screen, free from the main-window border. All functionalities (rewrite / diff / apply / multi-turn chat) are fully preserved; the window remains open afterapply(with the edit area cleared). - 光标位置可视化:在无选区时按 ⌘J,主窗口编辑器会在光标位置显示一条闪烁的蓝色竖线;有选区时则高亮显示选区。此功能旨在解决独立窗口获取焦点后主编辑器原生光标消失的问题,帮助用户清晰定位 AI 将要改写或插入内容的位置。Cursor-position visualization: Pressing ⌘J at the cursor (without selection) displays a blinking blue caret line in the main editor; with a selection, the selection is highlighted. This compensates for the disappearance of the native cursor in the main editor when the separate window gains focus, helping users clearly identify where the AI will edit.
- 关闭主窗口即退出程序:现在关闭主窗口会自动关闭所有 AI、文件及 mermaid 子窗口,并退出整个程序(Tauri 默认行为是需关闭所有窗口才会退出)。Closing the main window now quits the app — It automatically closes all AI, file, and mermaid child windows, exiting the application (Tauri's default behavior requires all windows to be closed to exit).
重构 / Refactor
- AI 窗口 B 区高度改为 JS 内容驱动 + 窗口自动贴合:A 区与 B2(输入行)采用
flex:none,其高度由内容决定且互不影响;B 区(对话框)使用flex:1占据窗口剩余空间,B1(聊天区)同样使用flex:1占据对话框剩余空间。窗口通过 JS 的maybeFitWindow方法实现自动贴合,该方法的逻辑是:窗口始终会根据内容撑大,但仅在未手动调整大小时才会自动缩小。补偿 Tauri setSize 外尺寸偏移(标题栏高度约 32px;由于outerSize/innerSize在 webview 中测量不准确,需通过经验值进行补偿)。拖动 A-B 分隔条改变 A 区高度时,窗口会随之调整,而 B 区高度保持不变;拖动窗口下缘时,窗口会在 B2 区域仅剩一行时停止缩小(通过set_min_size实现)。当聊天区 B1 为空时,其高度为 0;B2 区域增加行数时,窗口会相应撑大;B1 区域进行流式输出时,窗口会随之增大(通过rAF紧跟内容变化,最多显示 10 行后出现滚动条)。Rebuilt B-zone with JS content-driven sizing + auto window-fit: A and B2 zones useflex:none(independent, content-sized); the dialog (B) usesflex:1; the chat (B1) also usesflex:1. The window auto-fits viamaybeFitWindow, which always expands with content but only shrinks if not manually resized. This compensates for the TaurisetSizeouter-size offset (approximately 32px for the titlebar, asouterSize/innerSizeare unreliable in webview and require empirical compensation). Dragging the A-B separator resizes A and the window (B remains constant); dragging the window bottom stops resizing when B2 is reduced to one line (viaset_min_size). When B1 is empty, its height is 0; B2 growth expands the window; B1 streaming output causes the window to grow (tracked byrAF, capped at 10 lines before scrolling appears).
修复 / Fixes
- B2 输入区显示行数受限:旧 CSS 规则
#ai-input{max-height:120px}限制了 JS 设置的高度,现已移除该上限。B2 input was capped at ~7 lines due to a stalemax-height:120pxrule that truncated the JS-set height. - B2 空输入显示异常:空 textarea 的
scrollHeight不包含一行的高度,导致自动调整大小时出现问题。现已通过在 JS 调整大小时添加max(1行)兜底,并结合 CSSmin-height属性解决。B2 collapsed to a sliver when empty because thescrollHeightof an empty textarea excludes one line of height; this is fixed by adding amax(1 line)fallback in JS resizing and using CSSmin-height. - AI 窗口关闭后选区标记残留:子窗口关闭时会发出通知,主窗口据此清除
#editor-hl标记。Selection mark lingered after the AI window closed — The child window now emits a notification on close, allowing the main window to clear the#editor-hlmark. - 选区首尾空白导致行内格式失效:
wrapSelection函数(用于 Ctrl+I 斜体、Ctrl+B 加粗、删除线、行内代码)原先将标记放置在选区的最边缘。当选区首尾包含空格或换行符时,会生成* 文本 *这样的格式,而 Markdown 行内标记要求必须紧贴文字,因此斜体/加粗无法正确渲染。现已修改逻辑,将首尾空白移至标记外侧(如*text*),确保空白留在标记之外。Inline-format markers weren't adjacent to text when the selection had surrounding whitespace/newlines, producing* text *which Markdown doesn't render; the markers now hug the text with whitespace kept outside. This affects Ctrl+I / Ctrl+B / strike / inline-code (all share thewrapSelectionfunction). - 版本号升至 2.2.1。Version bumped to 2.2.1.
MDeX-2.2.0
v2.2.0
本次更新重点: 安全加固、性能优化、AI 辅助弹窗改版,以及多项 Bug 修复(重点解决了一项可能导致“撤销清空文档”的数据丢失隐患)。
Highlights: Security hardening, performance optimizations, an AI-pane redesign, and numerous bug fixes (including a critical data-loss bug where undo could clear the document).
安全 / Security
- 自定义文件系统命令增加路径校验:
先前有 7 个自定义 Tauri 命令(如write_bytes_at、move_dir等)直接将前端传入的路径传递给std::fs,缺乏作用域校验。本次更新新增了assert_safe_path校验逻辑(包含路径规范化、..折叠处理及敏感目录黑名单机制,如~/.ssh、~/.aws、/etc等)。此举将潜在的 Webview 脚本注入攻击半径从“全用户文件系统”有效收窄至“非敏感路径”。
Path-scope guard on custom FS commands: Previously, 7 custom Tauri commands passed front-end paths directly tostd::fswithout scope validation. Addedassert_safe_path(canonicalization +..collapse + sensitive-directory denylist) to restrict hypothetical webview script-injection attacks from accessing secrets/persistence/system directories. - SVG 注入点净化:
针对 Mermaid 及图片查看器的innerHTML注入点,统一接入 DOMPurify 进行净化(采用svg+html配置并放行foreignObject),有效剥离<script>标签、on*事件属性及javascript:协议,防止 XSS 攻击。
SVG sanitization at injection points: Unified sanitization for viewerinnerHTMLsinks using DOMPurify (svg+htmlprofile +foreignObjectallowance), stripping<script>,on*handlers, andjavascript:protocols. - 收紧 Opener 权限:
Capabilities 配置项opener:default收缩为显式的allow-open-url+allow-default-urls权限,并移除了未使用的allow-reveal-item-in-dir权限,遵循最小权限原则。
Tightened opener capability: Replacedopener:defaultwith explicitallow-open-url+allow-default-urlsscopes and removed the unusedallow-reveal-item-in-dirpermission.
性能 / Performance
- 渲染优化:
renderIntoPreview针对非虚拟分支复用已解析的模板(cloneNode),避免了二次innerHTML解析带来的性能开销。 - 网络与内存优化:实现
reqwest::Client进程级复用,节省了每次 AI 请求的 DNS/TCP/TLS 握手开销;优化drain_complete_lines算法,改用游标实现,消除 O(n²) 复杂度的 memmove 操作;read_image_data_url采用 String 预分配策略,减少内存拷贝次数。 - 构建与交互优化:
build-html.mjs改为并行读取 vendor 文件;搜索输入增加 debounce(防抖)处理;优化撤销快照缓存策略,确保editor.value仅缓存一次。
新增 / Features
- AI 辅助弹窗改版:
移除顶部冗余头部行;编辑区标签现显示当前 AI 名称;将用户输入与 AI 回答合并为单一对话框,交互更连贯;发送按钮移入输入行;关闭按钮(×)置于编辑区标签行(纯问答模式亦显示该行);弹窗右下角新增拖拽调整大小功能。
AI pane redesign: Removed the redundant header row. The edit-zone label now displays the active AI name. User input and AI replies are merged into a unified dialog flow. The send button moved into the input row, and the close button (×) sits in the edit-zone label row (visible in chat-only mode as well). The pane is now resizable via a bottom-right handle. - LaTeX 导出 Golden 测试:
tools/test-pure.mjs升级为具备字符串/正则/注释感知能力的函数提取器,新增texInline/texColor/detectTexLangs等 16 条断言(测试覆盖从 20 提升至 36 条)。
LaTeX export golden tests: Upgraded the extractor with string/regex/comment awareness, adding 16 new assertions (total coverage increased from 20 to 36 cases). - CI 流程完善:
新增.github/workflows/ci.yml,包含 Web 护栏检查、跨平台cargo test及 e2e 对齐测试;修复test-align-e2e.mjs退出码逻辑(此前断言失败错误地返回了 exit 0)。
CI workflow: Added.github/workflows/ci.yml(web lint + cross-platformcargo test+ e2e alignment). Fixedtest-align-e2e.mjsexit code logic (previously returned exit 0 on assertion failures).
修复 / Fixes
- 撤销操作导致文档清空(数据丢失隐患):
修复init/loadTab加载文档时未重置undoLast基线的问题。此前,程序化编辑入栈后执行 Ctrl+Z 会错误地将文档恢复为空内容。现已新增commitUndo(prev)函数,利用“编辑前快照”入栈(不再依赖可能过时的全局undoLast),并在文档加载后调用resetUndo。同时修复了 Ctrl+K 插链接等工具栏格式化操作不可撤销的问题。
Undo could clear the document (data loss): Fixed a bug whereinit/loadTabdid not reset the undo baseline to the loaded content, causing Ctrl+Z to restore an empty state. AddedcommitUndo(prev)to capture the real pre-edit snapshot inline andresetUndoafter load. Also made toolbar formatting actions (Ctrl+K link, bold, etc.) properly undoable. - 搜索功能体验优化:
修复搜索“下一个”匹配项时,非首条匹配项未能居中或高亮的问题。selectMatch改用offToEditorY(基于实测视觉行 Y 坐标),替代原先逻辑行乘以硬编码行高的计算方式。
Search "next" match centering/highlighting: Fixed an issue where non-first matches were not centered/highlighted.selectMatchnow uses measured visual-line Y coordinates instead of logical-line calculations. - 界面显示异常:
- 替换栏显示问题:修复仅查找模式下替换栏不隐藏的问题(原因:
.search-row的display:flex覆盖了[hidden]属性),补充了高特异性 CSS 规则。
Replace row visibility: Fixed an issue where the replace row remained visible in find-only mode (display:flexwas overriding[hidden]). - AI 弹窗状态异常:修复 AI 弹窗
display:flex覆盖[hidden]导致无法关闭及启动时残留弹窗的问题,增加#ai-pop[hidden]规则。
AI pane visibility: Fixed a bug wheredisplay:flexprevented the AI pane from closing or caused stray popups on startup. - 菜单响应失效:修复点击「MDeX简介」无反应的问题(原因:
HELP_STRINGS声明代码意外粘合至注释行)。
"MDeX intro" menu item: Fixed unresponsiveness caused by code being incorrectly concatenated into a comment line.
- 替换栏显示问题:修复仅查找模式下替换栏不隐藏的问题(原因:
- 文案优化:
AI 设置标题由「AI 辅助设置」精简为「AI 设置」(17 种语言统一更新);精简输入框 placeholder 中冗长的括号说明。
UI Text: Shortened AI-settings title across 17 languages and simplified input placeholder text. - 版本号升级:
版本号更新至 2.2.0。
Version bumped to 2.2.0.
MDeX-2.1.1
v2.1.1
热修: 修复了部分文档编辑区与预览区滚动不同步的问题——编辑器滞后于预览区若干段落,且越往文档后部越明显。
Hotfix: Fixed out-of-sync scrolling between the editor and preview in some documents — the editor lagged behind the preview by several paragraphs, with the issue worsening toward the end of the document.
修复 / Fixes
-
修复编辑↔预览块级对齐错位
问题根因:srcBlockOffsets(预览区每个块对应的源码偏移量)原先是通过按空行\n\n切分源码来定义「块」的,这与marked解析器的实际顶层块划分逻辑不一致。
具体而言,marked会将「标题紧跟列表/段落(中间无空行)」的情况拆分为heading+list两个顶层元素,或将loose list(松散列表)及其续文段落合并为一个list token。这种逻辑差异导致按空行切分得到的块数与marked解析出的实际块数不匹配。错位后果:
renderIntoPreview随后使用children[i] ↔ srcBlockOffsets[i]的方式按下标一一对应分配data-src-offset。一旦出现块数分歧,后续所有偏移量即开始产生单调错位。在一份包含大量「标题紧跟列表」与「列表续文段落」的测试文档中,66 个预览块里有 58 个标注了错误的偏移量。这导致syncAnchors的中线映射将编辑器和预览锚定到了不同的段落,表现为编辑器持续滞后于预览,且越往后偏移越严重。解决方案: 改用
marked.lexer的顶层 token 在源码中按顺序定位每个块的起始偏移。这使得源码映射与预览的顶层元素严格同源、一一对应,从而彻底消除了错位。测试: 附带了
tools/check-align.js(块切分对比工具)与tools/test-align-e2e.mjs(真实浏览器端到端对齐回归测试)。
Fixed Edit↔Preview block-alignment drift
Root Cause:srcBlockOffsets(each preview block's corresponding source offset) was originally derived by splitting the source on blank lines (\n\n). This method did not align withmarked's actual top-level block parsing logic. Specifically,markedsplits "a heading immediately followed by a list/paragraph (no blank line)" into two separate top-level elements (heading+list), while merging loose lists and their continuation paragraphs into a singlelist token. Consequently, the block count obtained by splitting on blank lines differed frommarked's parsed output.Impact:
renderIntoPreviewthen assigneddata-src-offsetby pairingchildren[i]withsrcBlockOffsets[i]based on index. From the first point of divergence, all subsequent offsets began to drift monotonically. In a test document full of "heading-then-list" and "list-continuation" patterns, 58 out of 66 preview blocks were assigned incorrect offsets. This causedsyncAnchorsto map the editor and preview to different paragraphs, manifesting as the editor consistently lagging behind the preview, with the error worsening toward the end.Fix: The offset of each block is now located sequentially using
marked.lexer's top-level tokens. This ensures the source mapping is strictly co-sourced with the preview's top-level elements in a one-to-one correspondence, eliminating the drift.Tests: Added
tools/check-align.js(block-split diff utility) andtools/test-align-e2e.mjs(real-browser end-to-end alignment regression test). -
版本号升级至 2.1.1。 / Version bumped to 2.1.1.
重构 / Refactor
-
app.js 拆分(内部重构,无功能变化)
改动内容: 将 17 种语言的界面文案(I18N,共 491 行)抽离至src/i18n.js;将帮助文档数据(HELP_STRINGS及其依赖项SK/sc/CITE_HELP_*,共 1090 行)抽离至src/help.js。此举使src/app.js从 6580 行缩减至 5006 行(−24%)。实现细节:
app.js内部通过const X = window.X重新引用这些模块(下游 6 处引用零改动)。build-html.mjs现改为拼接i18n + help + app并内联至同一个<script>标签中,保持了单文件离线产物的特性,且无需引入打包器。验证: 经过四层验证确保功能正常:
- 构建成功;
npm test通过 (20/20);- 真实浏览器对齐端到端测试通过(66 个块,0 错位);
- 运行时确认
window.I18N/window.HELP_DATA数据完整,UI 文案与帮助文档显示正常。
**app.js split (internal refactor, no behavior change)** **Changes:** Extracted the 17-language UI strings (`I18N`, 491 lines) into `src/i18n.js` and the help-document data (`HELP_STRINGS` plus dependencies `SK`/`sc`/`CITE_HELP_*`, 1090 lines) into `src/help.js`. This reduced `src/app.js` from 6580 to 5006 lines (−24%).Implementation:
app.jsreimports these modules viaconst X = window.X(zero downstream edits required).build-html.mjsnow concatenatesi18n + help + appinto a single inlined<script>tag, preserving the self-contained offline build without a bundler.Verification: Verified through four layers:
- Build succeeds;
npm testpasses (20/20);- Real-browser alignment e2e test passes (66 blocks, 0 drift);
- Runtime confirmation that
window.I18N/window.HELP_DATAare fully populated, and UI labels/help docs render correctly.
MDeX-2.1.0
v2.1.0
AI 配置预设:支持保存多套命名的 API 配置(如 OpenAI / Anthropic / 本地 Ollama),并可在它们之间一键切换。
AI configuration profiles: save multiple named API configs and switch between them with one click (e.g. OpenAI / Anthropic / local Ollama).
新增 / Features
- AI 配置预设管理:AI 设置弹窗现已升级为配置管理器风格,左侧为预设列表,右侧为详细表单。您现在可以创建命名配置(包含配置名、提供商、端点、模型、温度及 Key),支持保存及删除(含二次确认),并直接从列表切换当前使用的 API。老用户原有的单一配置将自动迁移为「默认」预设。Key 的存储方式保持不变,仅存于本机 localStorage,绝不会随会话备份外泄。
AI configuration profiles: the AI settings dialog is now a two-pane profile manager (preset list on the left, form on the right). Create named profiles (name + provider/endpoint/model/temperature/key), save, delete (with confirm), and pick from the list to switch the active API; existing single-config users are auto-migrated to a "Default" profile. As before, keys live only in this machine's localStorage and are never included in session backups. - 编辑↔预览同步定位优化:大幅提升双向同步的精确度。点击编辑区定位预览时,采用 DOM 实测实现精确居中;滚动同步机制升级为“渲染时测量、滚动时读取缓存”模式:预览侧采用细粒度 Y 轴缓存,编辑侧新增
#editor-yprobe镜像层,利用 Range 实测每行的真实 Y 值,从而使滚动精度接近点击级别。此外,修复了图片加载后导致的预览偏移(图片加载时主动重测缓存),以及镜像层字号未随缩放同步导致的发布版偏移问题。
Edit↔Preview sync accuracy: editor→preview click now DOM-measures the exact target; scroll sync measures Y at render time and only reads cache while scrolling (fine-grained Y cache on the preview side; a new#editor-yprobemirror layer range-measures each editor line's real Y), reaching near-click precision; also fixed preview drift after images load (via img-load re-measure) and a release-only drift caused by the mirror layer not syncing its font-size on zoom. - AI 浮层显示当前 API:浮层头部的 Chip 标签现在会显示当前激活的 API 预设名称,并在切换预设后实时更新。
AI pane active-API chip: the pane header shows the active profile name and updates on switch. - 版本号升级至 2.1.0。Version bumped to 2.1.0.
修复 / Fixes
- 修复长文档末尾 AI 选区阴影跳行问题:此前的版本中,
#editor(textarea)在内容溢出时会出现原生垂直滚动条,而镜像层#editor-hl设置为overflow:hidden(无滚动条)。这导致两层的可用文本宽度相差一个滚动条宽度,进而使得软换行的断行点不一致,造成行数与scrollHeight的差异。在长文档末尾,这种差异会累积,导致像素级同步(hl.scrollTop = editor.scrollTop)失效,蓝色选区阴影因此跳到下一行(文档开头因scrollTop≈0无累积,故显示正常)。本次更新将两处统一设置为overflow-y: scroll,确保两层的可用文本宽度逐像素相等,彻底恢复对齐。此修复也同步解决了搜索高亮在长文档末尾可能出现的同类偏移隐患。
AI selection shadow skipping a line at the end of long documents: the textarea's native scrollbar and the#editor-hlmirror'soverflow:hiddenleft the two layers with different usable text widths, so soft-wrapped lines broke at different points and the pixel-levelscrollTopsync drifted near the end of long docs. Unified both tooverflow-y: scroll; also fixes the same latent drift in search highlighting.
MDeX-2.0.1
v2.0.1
AI 功能更名 + 全量本地化:README/UI 的「AI 改写 / AI rewrite」统一改为「AI 辅助编辑 / AI-assisted editing」(界面按钮 zh「AI 辅助」、en 及其余 15 种「AI Copilot」);AI 相关界面文案、
帮助对话框、原生「AI 设置」菜单项此前在 15 种语言里回退中文,现已补齐全部 17 种语言。
AI feature rename + full localization: "AI rewrite" is now "AI-assisted editing" in the docs and "AI Copilot" in the UI (zh shows「AI 辅助」); all AI UI strings, the help dialog's AI section, and the native "AI Settings" menu item are now translated into all 17 languages (the 15 non-zh/en locales previously fell back to Chinese).
变更 / Changes
- AI 功能更名:17 种 README 的核心功能/简介/快捷键表「AI 改写 · AI rewrite」→「AI 辅助编
辑 · AI-assisted editing」;应用内按钮/右键菜单/设置浮层/模式切换 zh「AI 改写」→「AI 辅助」、en 及其余语种「AI Rewrite」→「AI Copilot」;⌘/Ctrl+J标签同步。AI feature rename across all 17 READMEs and the in-app UI (toolbar button, context menu, settings modal, mode toggle, shortcut row). - AI 文案全量本地化:44 个 AI 相关 i18n key 此前仅 zh+en(其余 15 种回退中文),现已为 15 种语言全部翻译。Full AI i18n: the 44 AI i18n keys now exist in all 17 language blocks (previously zh+en only).
- 帮助对话框:15 种语言的 AI 段落跟随更名(pIntro「optional AI Copilot」、features 首条 bullet、shortcuts 末项)。Help dialog: AI section rebranded in all 15 non-zh/en locales.
- 原生菜单:Rust
ai_settings_label此前仅 en+zh(其余 15 种显示中文「AI 设置」),现已
补齐 15 种语言,文案与前端aiSettings一致。Native menu: "AI Settings" menu item localized into all 15 non-zh/en languages. - 内部代码标识(Rust 命令
ai_rewrite、i18n key 名aiRewrite/aiModeRewrite、localStoragekey、CSS/#ai-*、系统 prompt)未改。Internal identifiers (Rustai_rewrite, i18n key names, localStorage keys, CSS, system prompt) unchanged.
MDeX-2.0.0
v2.0.0
首个 AI 辅助大版本:采用「编辑区」与「对话区」双栏浮层,支持多轮对话。改写类指令实时流至编辑区(带 diff 高亮),问答类内容进对话区;支持应用到选区或在光标处插入。AI 为可选功能,应用默认保持离线。
First AI-assisted release: Introduces a two-pane overlay (Edit zone + Chat zone) with multi-turn conversation. Rewrite instructions stream into the Edit zone (with diff highlighting), while Q&A goes to the Chat zone; apply to selections or insert at the cursor. AI is strictly opt-in; the app remains offline by default.
新增 / Features
- 统一多轮 AI 对话(编辑区 + 对话区):选中文本或定位光标后,可通过
⌘/Ctrl+J、工具栏 ✨ 图标或右键菜单唤出 AI 浮层。浮层分为双栏:编辑区用于展示改写版本(与原文 diff 对比高亮,支持「应用到文档」「在光标处插入」及 ↶↷ 撤销/重做);对话区用于展示多轮对话流。改写类指令实时流至编辑区,问答类指令进入对话区——AI 会通过EDIT:前缀自动判断并进行路由。
Unified multi-turn AI dialog (Edit + Chat panes): Select text or place the cursor, then open via⌘/Ctrl+J, the ✨ toolbar icon, or right-click. The overlay is split into an Edit zone (showing the current rewrite with diff highlighting; includes Apply to document, Insert at cursor, and undo/redo) and a Chat zone (for multi-turn threads). Rewrite instructions stream into the Edit zone, while Q&A goes to the Chat zone — the AI self-routes via anEDIT:prefix. - 无选区模式与路由兜底:未选中文本直接唤出时,默认为「在光标处生成并插入」,并以蓝色光标竖条标示插入位置。若 AI 路由误判,可一键点击「→用作编辑区内容」,将对话区的任意回复快速转移至编辑区。
No-selection mode & routing fallback: Opening without a selection defaults to "generate and insert at cursor," marked by a blue caret bar. If the AI misroutes, any chat reply can be moved into the Edit zone with a single click as a fallback. - 兼容 OpenAI 与 Anthropic 双协议:端点、模型、温度及 API Key 均保存在本地(
localStorage,不纳入会话备份)。网络请求仅通过 Rust 的reqwest库进行,前端 CSP 维持禁网状态。支持流式 SSE 响应、Esc 键随时取消以及清晰易读的错误提示。
OpenAI-compatible + Anthropic dual protocols: Endpoint, model, temperature, and API key are stored locally (localStorage, excluded from session backups). Networking is handled exclusively via Rust'sreqwest, keeping the frontend CSP network-blocking. Supports streaming SSE, Esc to cancel, and human-readable error messages. - AI 浮层渲染增强:对话区的 AI 回复支持完整 Markdown 渲染(包含 KaTeX 公式与 Mermaid 图表)。编辑区支持「源码/渲染」视图切换及 ↶↷ 撤销/重做。当选中部分公式时,渲染时会自动补齐缺失的
$或$$(仅作用于渲染过程,不修改源文本)。
Enhanced AI pane rendering: Chat replies render full Markdown (including KaTeX math and Mermaid diagrams). The Edit zone toggles between source-diff and rendered preview with ↶/↷ undo/redo. Partial formula selections auto-balance missing$or$$during rendering only (source text remains untouched). - 文档与快捷键:帮助说明已结构化(分为「隐私与数据安全」和「AI」两部分,支持 17 种语言);
⌘/Ctrl + J快捷键已收录至帮助文档及 README 的快捷键表格中。
Docs & shortcuts: Restructured help intro (Privacy & Data Safety / AI sections, 17 languages);⌘/Ctrl + Jadded to the help and README shortcut tables.
工程性 / Engineering
- 后端重构
ai_rewrite/ai_cancel:新增history参数以支持多轮对话;采用自适应 system prompt(通过EDIT:前缀实现改写与问答的分流);流式字节缓冲按行切分,解决中文跨 TCP 分块导致的乱码问题;使用reqwest配合 rustls-tls,移除对系统 OpenSSL 的依赖。
Backendai_rewrite/ai_cancel: Now acceptshistoryfor multi-turn context; features an adaptive system prompt (EDIT:prefix routes rewrites vs. Q&A); byte-buffer line-splitting prevents CJK mojibake across TCP chunks; utilizesreqwestwith rustls-tls (no system OpenSSL dependency). - 版本号正式升至 2.0.0。
Version bumped to 2.0.0.
MDeX-1.4.1
v1.4.1
窗口关闭热修(hotfix):点窗口关闭按钮(红×)完全无反应,仅 ⌘Q 能退出。
修复 / Fixes
- 修复点窗口关闭按钮(红× / 关闭)完全无反应:主窗口注册了
onCloseRequested拦截器(用
于关窗前确认未保存标签)后,Tauri 会无条件挡掉原生关闭、改由 JS 默认动作destroy()落实关闭;而destroy()需要core:window:allow-destroy权限,原capabilities/default.json只配了allow-close、漏配allow-destroy,导致"放行关闭"被权限系统拒绝、窗口关不掉(⌘W 关标签、⌘Q 退出不经此路径,故不受影响)。补上core:window:allow-destroy。
Fix: clicking the window close button (red ×) did nothing. With anonCloseRequestedinterceptor registered, Tauri blocks the native close and defers to the JS default actiondestroy(), which requires thecore:window:allow-destroycapability — missing fromdefault.json(onlyallow-closewas granted). Closing via the button (and the post-save-confirm path) now works; ⌘W / ⌘Q were unaffected.
MDeX-1.4.0
v1.4.0
本版为"硬化 + 架构升级"大版本:集中修复代码审查报告中的数据完整性 / 并发 / 安全 / 性能缺陷
,
首次引入自动化测试(CI + 前后端单测),并将前端模块化、加 TypeScript 类型护栏、会话持久化迁移到 IndexedDB 混合。
修复 / Fixes(数据完整性)
- 保存写入改为原子写(崩溃/断电/磁盘满不再留半截文件丢原内容)。Save writes atomically (crash-safe).
- 跨盘另存为不再丢图片:草稿首存到 U 盘/网络盘,复制失败中止并保留源(此前静默跳过后删源
致两处皆失)。 - 两窗口草稿图片不再互相覆盖:草稿目录按窗口隔离。
- 同一文件不再能在两窗口同时打开:已被别的窗口持有时置顶该窗口,杜绝并发存盘互相覆盖。
- 重启后双击同一文件不再开重复窗口:会话恢复后重新登记已打开文件。
- GBK / Latin-1 / UTF-16 等非 UTF-8 文件可正常打开(兜底解码 + 剥 BOM)。
- "已保存"不再掩盖图片断链:图片迁移/引用回写失败时保持未保存并提示。
- 外部修改检测:保存时若文件已被别的程序改过,弹"覆盖/从磁盘重载/取消",不再静默覆盖。
修复 / Fixes(安全 / 并发 / 性能)
- 构造
<img src=敏感文件>不再泄露本地文件:后端按 magic bytes + 扩展名白名单仅放行真实图片(含 AVIF/HEIF)。 - mermaid SVG 输出加纵深消毒(DOMPurify SVG profile,失败安全回退);KaTeX 显式
trust:false。 - 关标签用对象引用重定位(不再 splice 错标签);确认弹窗加重入锁;切换/关闭取消待渲染。
- 撤销栈 / 图片缓存 / 导航历史内存封顶(撤销 48MB 字节预算、imgCache LRU 200、navStack 500)。
- 关闭窗口前确认未保存标签(onCloseRequested 拦截;viewer 窗口排除)。
- 关闭前同步刷盘(pagehide/visibilitychange)挽回 ≤500ms 编辑。
- 会话过大/损坏不再静默失败:IDB 主存突破 5MB 配额 + localStorage 回退 + 一次性提示。
- 另存为找不到源图时不再改写引用为断链路径(保留原引用 + 提示"保存失败 images")。
- 修复 mermaid Viewer 窗口无法关闭(关窗拦截误注册到 viewer 窗口);进一步改为结构性门
控:viewer 窗口根本不注册关窗拦截。 - 修复 mermaid 图节点标签消失(过度消毒剥掉了
foreignObject标签)——回退冗余的 SVG 消毒,安全门回到 mermaidstrict/ KaTeXtrust:false。 - 修复蒙古语"MDeX简介"末尾混入乌尔都语(既有翻译错)。
新增 / Added
- 自动化测试:20 个前端纯函数测试(
npm test)+ 15 个 Rust 单测(cargo test)+ GitHub Actions CI(push/PR 自动跑)。 - 前端模块化:应用逻辑抽到
src/app.js,构建期内联(不引入打包器,保持单文件离线产物)。 - TypeScript 类型护栏:
tsconfig.json+// @ts-check+ JSDoc(@typedef Tab等),tsc --noEmit零错误。
修复 / Fixes(公式渲染,1.4.0 后续 hotfix)
- 超长 LaTeX 公式超宽时按运算符折行,不再出现横向滚动条:单行公式按
=/+/-折行;align/gather等多行环境逐行折行(align保留=对齐、续行\notag不重复编号);cases/matrix等无可折点的回退等比缩小。折行位置随预览栏宽度实时重排(ResizeObserver监听、debounce 重算)。 - 编号不被折行公式覆盖:迭代实测第一行内容右边界 vs 编号左边界(绕过 KaTeX 离屏量宽与实际渲染的偏差);
\begin{env}...\end{env} \tag{x}中环境外的\tag不再被丢弃。
MDeX-1.3.4
调整 / Changed
- 底部状态栏改为显示当前文件的绝对路径:原状态栏左侧的「行 / 词 / 字符」计数改为显示当前打开文件的绝对路径(草稿未存盘时退化为文件名 / 未命名),长路径自动省略、悬停可看完整路径;切换
标签、首存、另存为后即时刷新。
Bottom status bar now shows the current file's absolute path: The "lines / words / characters" counts on the left of the status bar are replaced with the absolute path of the open file (drafts fall back to the filename / "Untitled"); long paths are ellipsized with the full path on hover, and refresh instantly on tab switch / first save / save-as. - 编辑区右上字数单位「字」改为「词」:编辑区右上角的实时计数由「N 字」改为「N 词」,与底部统计口径一致。
Editor word-count unit "字" → "词": The live counter at the editor's top-right now reads "N 词" (words) instead of "N 字" (characters), matching the bottom-bar wording. - 精简预览区状态标识:移除预览区右上角的「已渲染」状态指示、右下角的「UTF-8 · Markdown」编码标识,减少视觉干扰。
Trimmed preview-area status labels: Removed the "Rendered" indicator at the preview's top-right and the "UTF-8 · Markdown" encoding label at its bottom-right for less visual clutter.
修复 / Fixes
- 编辑模式下底部状态栏的文件路径不刷新:仅编辑模式(隐藏预览窗格)下打开/切换文件,底部状
态栏的文件绝对路径不显示(只停留在文件名或旧值)。根因是删除「已渲染」状态指示<span id="rs">时漏删了render()编辑模式分支里的一处$("rs")赋值,$("rs")返回 null 抛 TypeError 中断了渲染流程(BUG-081)。已清理该残留,并在切换标签时立即刷新状态栏。
Status-bar file path didn't refresh in editor-only mode: With the preview pane hidden, opening/switching files left the bottom status bar stuck on the filename or a stale value instead of the absolute path. Root cause: a leftover$("rs")assignment inrender()'s editor-mode branch (the element had been removed) threw a TypeError that aborted rendering (BUG-081). The reference is now removed and the status bar refreshes immediately on tab switch. - 菜单「另存为」后底部状态栏路径不刷新:通过菜单「另存为」把文档(含内置 MDeX 示例)存到某目录(如 Downloads)后,底部状态栏仍显示文件名而非新的完整路径。根因是
saveAs函数设置了文件路径却未刷新状态栏(BUG-082)。已修复。
Status-bar path didn't refresh after "Save As": Saving a document (incl. the built-in MDeX sample) via the menu "Save As" left the status bar showing the filename instead of the new absolute path. The saveAs function set the path but didn't refresh the status bar (BUG-082). Fixed.
MDeX-1.3.3
移除 / Removed
- 移除「高清位图 PDF」导出:「PDF(矢量打印)」已能覆盖 PDF 导出需求(文字/公式任意放大清晰),故移除与之重叠的「PDF(高清位图)」选项,简化导出菜单(现为 Markdown / HTML / PDF(矢量打印)/ LaTeX);顺带消除 macOS WKWebView 下位图路径末尾内容丢失(BUG-078)的根源。
Removed "PDF (high-res raster)" export: "PDF (vector print)" already covers PDF export (text/formulas stay crisp at any zoom), so the overlapping "PDF (high-res raster)" option is removed to simplify the export menu (now Markdown / HTML / PDF (vector print) / LaTeX); this also eliminates the root cause of the trailing-content loss in the WKWebView raster path (BUG-078).
修复 / Fixes
- Windows 下相对路径图片不再无法渲染:Windows 安装版打开/新建含相对路径图片(如
)的文档、或粘贴/拖入图片时,渲染区图片此前一律不显示(macOS/Linux 正常)。根因是前端dirOf/baseName用lastIndexOf("/")、relPath用split("/"),只认 Unix/;而 Rust 在 Windows 返回的是反斜杠绝对路径(C:\Users\…),导致tab.dir被算成空,resolveImages的"无目录则跳过"守卫误杀所有相对路径图,粘贴图片的落盘目录还会拼成/stem_images写到盘符根目录。现让这三个函数同时认/与\,Windows 下相对/绝对路径图片均正常渲染。该问题与编译工具链(MSVC / 交叉编译 GNU)无关,属纯前端逻辑,所有 Windows 运行版皆受影响(BUG-080)。
Relative-path images now render on Windows: On the Windows build, opening/creating a document with relative-path images (e.g.), or pasting/dropping an image, left the render area blank (macOS/Linux were fine). Root cause:dirOf/baseNameusedlastIndexOf("/")andrelPathusedsplit("/")— Unix/only — but Rust returns backslash absolute paths on Windows (C:\Users\…), sotab.dircomputed to empty and the "skip if no dir" guard inresolveImageskilled every relative-path image (pasted images even landed in the drive root via/stem_images). These three helpers now accept both/and\, so relative and absolute images render correctly on Windows. This is a pure-frontend issue independent of the toolchain (MSVC / cross-compiled GNU) and affects every Windows build (BUG-080).
调整 / Changed
- 另存为格式弹窗:底部"取消"键改为右上角 ×:「另存为」时选择导出格式(Markdown / HTML / PDF / LaTeX)的自绘弹窗,原本在底部排一个"取消"按钮,现改为卡片右上角的 × 关闭按钮(与帮助、Mermaid 查看器等弹窗统一)。取消途径仍为三种等价:点右上角 ×、点遮罩空白、按 Esc。× 位置用
inset-inline-end,在阿拉伯/乌尔都等 RTL 语言下自动落到左上角。
Save-as format dialog: bottom "Cancel" → top-right ×: The self-drawn format-picker (Markdown / HTML / PDF / LaTeX) shown on "Save As" previously placed a "Cancel" button in the bottom row; it's now a × close button in the card's top-right corner (consistent with the Help and Mermaid-viewer dialogs). Cancellation still works three equivalent ways: click ×, click the backdrop, or press Esc. The × usesinset-inline-endso it flips to the top-left under RTL languages (Arabic/Urdu/etc.).
修复 / Fixes
- 矢量 PDF 导出图片不再变问号:含相对路径图片的文档(如「MDeX 示例」的图标)在「另存为 PDF(矢量打印)」时,图片在生成的 PDF 里显示为问号(broken);敲空格触发重渲后图标恢复、再导出又变问号,反复横跳。根因是导出前
preview.innerHTML = lastFullHtml把预览重置回原始相对路径后直接打印,未把图片 resolve 成 data URL;而lastFullHtml快照里<img>始终是相对路径(resolveImages只改 DOM 不回写字符串)。现打印前补resolveImagesForExport(与导出同源处理),图片正常渲染。
Vector-PDF images no longer show as broken: A document with relative-path images (e.g. the MDeX sample icon) rendered images as broken (?-icons) when saved via "Save as PDF (vector print)"; typing a space to re-render fixed it temporarily, but re-exporting broke it again. The export reset the preview tolastFullHtml(which always holds raw relative paths —resolveImagesmutates only the DOM, not the snapshot) and printed without resolving images to data URLs. AresolveImagesForExportstep is now run before printing, so images render correctly.