Skip to content

Releases: sevenhsu7/dsh-codex-like-markdown

v0.1.1 — 就地编辑 · 表格按格编辑 · 选中引用到会话

Choose a tag to compare

@sevenhsu7 sevenhsu7 released this 13 Oct 07:56

中文

就地编辑

  • 打开 .md 即用本插件渲染,效果与内置 Markdown 视图完全一致(同一个 MarkdownText 组件),可随时从渲染器下拉切回内置只读视图
  • 点段落、标题、列表、引用、代码围栏 → 就地变成可编辑:没有边框盒子,用文档自己的字体,屏幕上不出现 Markdown 标记
  • 光标落在你点击的那个字符上,用的是浏览器自己的命中测试(caretRangeFromPoint)
  • Markdown 本身就是文本模型:语法标记留在 DOM 里但被隐藏,绝不从样式反推语法,所以一次编辑只可能改变字符。这条有测试兜底——把编辑面的标记剥掉后必须逐字节等于原文件
  • B / I / </> / 🔗 给选区套 Markdown;+ 新增段落

表格

  • 表格保持渲染器自己的表格(列宽、分隔线、对齐都不变)
  • 点某一格只在那一格上浮出单行编辑面,并且只回写该格在源码行里的字节区间——行内其它格、分隔行、表外内容一个字节都不动
  • 编辑面每次渲染都从该格重新量位置,行变高时它会跟着走;回车即提交,粘贴的换行变成空格,所以单元格编辑不可能多出一行

选中即上下文

  • 在预览里选中文字 → 浮出操作条 → 添加至会话(英文界面为 Add to chat)
  • 插入内容形如:出处单独一行,下面是被引用的段落,末尾留一个空行(光标落在那里,可以直接接着提问)
  • 走的是输入框自己的 captureInsertion() / insertText(),因此是光标处一次可撤销的编辑,不会覆盖你已经写好的内容
  • 为了让拖拽始终能用于选择:拖拽 = 选择,按下与松开在同一位置 = 进入编辑

保存

  • 停顿约 1.2 秒自动保存;⌘S / Ctrl+S 立即保存;工具栏实时显示就绪 / 未保存 / 保存中 / 已保存
  • 磁盘上的文件被别处改动时不覆盖你的内容,而是给出「仍然覆盖」或「从磁盘重新载入」,并在你再次编辑前暂停自动保存
  • 版本护栏:写入携带读取时的版本做 compare-and-set,FS_STALE_VERSION 会映射成明确的 stale 状态

安全与边界

  • 写入经 ctx.fs.writeText,目标身份、原子性、沙箱围栏都由文件系统后端负责:read-only 会话被拒绝,workspace-write 只能写工作区内
  • 宿主半不 import 任何 @deepseek-ai/*(Typert 绑定手工装配),客户端半是普通 module-loader bundle,零依赖、零安装脚本、无需构建

其他

  • 源码视图;每个标签页的未保存草稿在切换标签 / 切换渲染器 / 来去源码视图时都不丢
  • 中英双语界面,两种字典都有测试校验
  • 渲染器不可用时退回纯源码编辑;单个块渲染出错时退回该块原文,不会让整个面板变空
  • 186 条断言:宿主半经过真实 Gateway 派发与真实文件写入,正文经过真实 React 渲染,交互逻辑逐字校验插入内容

暂不支持

  • 编辑过程中现敲的 Markdown 语法会先显示原文,关闭该块后才渲染(不在持有光标和输入法组合态时重渲染,是刻意的)
  • 表格增删行列属于结构性修改,请用源码视图
  • 草稿存在浏览器内存里,刷新页面不保留

English

Editing in place

  • A .md file opens in this plugin's renderer, which uses the same MarkdownText component as the built-in viewer, so it looks identical; switch back to the read-only viewer from the renderer dropdown at any time
  • Click a paragraph, heading, list, quote, or code fence and it becomes editable in place: no border box, the document's own font, no Markdown syntax on screen
  • The caret lands on the character you clicked, placed by the browser's own hit test (caretRangeFromPoint)
  • The Markdown is the text model: markers stay in the DOM but hidden, and styling is never serialized back into syntax, so an edit can only change characters. A test pins it — stripping the surface's markup must return the file byte for byte
  • B / I / </> / 🔗 wrap the selection in Markdown; + Add paragraph

Tables

  • A table keeps the renderer's own table: column widths, rules, and alignment are untouched
  • Clicking a cell floats a single-line surface over just that cell and writes back only that cell's byte range in the line. Every other cell, the delimiter row, and the prose around the table are not touched
  • The surface re-measures itself from the cell on every render, so it follows the row as it grows; Enter commits and a pasted newline becomes a space, so a cell edit can never add a row

A selection is context

  • Select text in the preview and the bar offers Add to chat (添加至会话 in a Chinese UI)
  • What arrives is the file on its own line, the quoted passage under it, and a blank line at the end — where the caret lands, so a question can be typed straight away
  • It goes through the composer's own captureInsertion() / insertText(), so it joins the draft as one undoable edit at the caret and never replaces what you already wrote
  • A drag stays available for selecting: a drag selects, a press and release in the same place opens the editor

Saving

  • Auto-save after ~1.2 s idle; ⌘S / Ctrl+S saves immediately; the toolbar shows ready / unsaved / saving / saved
  • When the file changes elsewhere your buffer is kept, and you choose Overwrite or Load from disk; auto-save pauses until you edit again
  • Version guard: writes carry the version they read as a compare-and-set, and FS_STALE_VERSION maps to an explicit stale state

Safety and boundaries

  • Writes go through ctx.fs.writeText, so target identity, atomicity, and the sandbox fence belong to the filesystem backend: a read-only session is denied, and workspace-write may only write inside its workspace root
  • The host half imports nothing from @deepseek-ai/* (the Typert binding is assembled by hand) and the client half is a plain module-loader bundle: no dependencies, no install scripts, no build step

Also

  • Source view; a tab's unsaved draft survives switching tabs, switching renderers, and going to the source view and back
  • Chinese and English copy, with both dictionaries checked by tests
  • Without the renderer it falls back to source-only editing; a block that fails to render falls back to its own raw text instead of blanking the pane
  • 186 checks: the host half through a real Gateway dispatch and real filesystem writes, the body through a real React render, and the interaction logic down to the exact inserted text

Not yet supported

  • Markdown syntax typed during an edit shows literally until the block is closed — the surface is deliberately not re-rendered while it holds your caret and an in-flight IME composition
  • Adding or removing table rows and columns is a structural change: use the Source view
  • Drafts live in browser memory and do not survive a page reload