feat(changes): markdown reading mode for .md op previews

The changes tab preview pane gains a Reading/Raw toggle for .md/.markdown/.mdx session ops (read, write, and edit alike). Reading mode renders the resulting document through the shared MarkdownText pass (the same stack as the editor preview): GFM tables with alignment, task lists, strikethrough, footnotes, math, and code fences, with local image destinations rewritten through the session-scoped /sidebar/file media route (rewriteLocalImageUrls). Content sources: read -> parseReadContent (envelope stripped, blank lines preserved — markdown block structure depends on them, which parseReadLines drops); write -> the written content; edit -> the reconstructed resulting document from the in-window prior content when known, else the new snippet. Ported from dsh-file-trace v0.2.0 (PR #471 lineage), re-based onto the unified changes tab (#475) and the repo shared-renderer conventions (markdownTextProps labels contract, skin tokens, 21-dictionary locale parity).
This commit is contained in:
lhh010
2026-09-01 18:23:28 +08:00
parent c1992125d0
commit b7138cf4fd
27 changed files with 195 additions and 6 deletions
+19
View File
@@ -201,6 +201,25 @@ export function knownContentBefore(ops: readonly FileOp[], path: string, before:
return undefined
}
/**
* Strip the DSH read-tool response envelope down to the file's raw content,
* PRESERVING blank lines (unlike {@link parseReadLines}, which drops them):
* drops the <content> wrapper, the "(Showing lines ...)" note, and the
* per-line "<n>: " prefixes. The markdown reading mode needs the blank lines
* because markdown block structure depends on them.
*/
export function parseReadContent(raw: string): string {
const contentMatch = raw.match(/<content>([\s\S]*?)<\/content>/)
const body = contentMatch ? contentMatch[1]! : raw
return body
.split('\n')
.filter((line) => !/^\s*\(Showing lines .*\)\s*$/.test(line))
.map((line) => line.replace(/^\s*\d+:\s/, ''))
.join('\n')
.replace(/^[\n]+/, '')
.replace(/\n+$/, '')
}
/** One parsed read line: the file's own line number and its content text. */
export interface ReadLine { readonly line: number; readonly text: string }