mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
fix(editor): honor edited markdown destinations
This commit is contained in:
@@ -240,6 +240,60 @@ function markdownAfterTextReplace(content: string, search: string, replacement:
|
||||
}
|
||||
}
|
||||
|
||||
function markdownAfterDestinationEdit(
|
||||
content: string,
|
||||
kind: 'link' | 'image',
|
||||
destination: string
|
||||
): string {
|
||||
const codec = createRichMarkdownEditorCodec()
|
||||
const editor = new Editor({
|
||||
element: null,
|
||||
extensions: createRichMarkdownExtensions({ codec }),
|
||||
content: encodeRawMarkdownHtmlForRichEditor(content, codec),
|
||||
contentType: 'markdown'
|
||||
})
|
||||
try {
|
||||
if (kind === 'link') {
|
||||
let from: number | undefined
|
||||
let to: number | undefined
|
||||
editor.state.doc.descendants((node, pos) => {
|
||||
if (
|
||||
from === undefined &&
|
||||
node.isText &&
|
||||
node.marks.some((mark) => mark.type.name === 'link')
|
||||
) {
|
||||
from = pos
|
||||
to = pos + node.nodeSize
|
||||
}
|
||||
})
|
||||
if (from === undefined || to === undefined) {
|
||||
throw new Error(`Missing ${kind}`)
|
||||
}
|
||||
editor.chain().setTextSelection({ from, to }).setLink({ href: destination }).run()
|
||||
} else {
|
||||
let position: number | undefined
|
||||
editor.state.doc.descendants((node, pos) => {
|
||||
if (position === undefined && node.type.name === 'image') {
|
||||
position = pos
|
||||
}
|
||||
})
|
||||
if (position === undefined) {
|
||||
throw new Error(`Missing ${kind}`)
|
||||
}
|
||||
const node = editor.state.doc.nodeAt(position)
|
||||
if (!node) {
|
||||
throw new Error(`Missing ${kind}`)
|
||||
}
|
||||
editor.view.dispatch(
|
||||
editor.state.tr.setNodeMarkup(position, undefined, { ...node.attrs, src: destination })
|
||||
)
|
||||
}
|
||||
return editor.getMarkdown().trimEnd()
|
||||
} finally {
|
||||
editor.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
function markdownAfterTypingBesideImage(content: string, typed: string): string {
|
||||
const codec = createRichMarkdownEditorCodec()
|
||||
const editor = new Editor({
|
||||
@@ -729,6 +783,15 @@ describe('rich markdown round trip', () => {
|
||||
expect(roundTripMarkdown('![a \\] b](x.png)\n')).toBe('![a \\] b](x.png)')
|
||||
})
|
||||
|
||||
it('uses the edited link or image destination instead of stale source bytes', () => {
|
||||
expect(markdownAfterDestinationEdit('[a](old\\)path)\n', 'link', 'new)path')).toBe(
|
||||
'[a](new\\)path)'
|
||||
)
|
||||
expect(markdownAfterDestinationEdit('\n', 'image', 'new(path.png')).toBe(
|
||||
''
|
||||
)
|
||||
})
|
||||
|
||||
it('does not split a paragraph at a mid-line $$', () => {
|
||||
const content = 'costs $$ big money $$ here, honestly'
|
||||
expect(roundTripMarkdown(`${content}\n`)).toBe(content)
|
||||
|
||||
@@ -53,17 +53,22 @@ const RichMarkdownLink = Link.extend({
|
||||
addAttributes() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
rawHref: { default: null, rendered: false }
|
||||
rawHref: { default: null, rendered: false },
|
||||
originalHref: { default: null, rendered: false }
|
||||
}
|
||||
},
|
||||
parseMarkdown: (token, helpers) =>
|
||||
helpers.applyMark('link', helpers.parseInline(token.tokens || []), {
|
||||
href: token.href,
|
||||
title: token.title || null,
|
||||
rawHref: extractRawDestination(token.raw)
|
||||
rawHref: extractRawDestination(token.raw),
|
||||
originalHref: token.href
|
||||
}),
|
||||
renderMarkdown: (node, helpers) => {
|
||||
const href = node.attrs?.rawHref ?? node.attrs?.href ?? ''
|
||||
const href =
|
||||
node.attrs?.rawHref && node.attrs?.href === node.attrs?.originalHref
|
||||
? node.attrs.rawHref
|
||||
: (node.attrs?.href ?? '')
|
||||
const title = node.attrs?.title ?? ''
|
||||
const text = helpers.renderChildren(node)
|
||||
return title ? `[${text}](${href} "${title}")` : `[${text}](${href})`
|
||||
@@ -179,7 +184,8 @@ export function createRichMarkdownExtensions({
|
||||
addAttributes() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
rawSrc: { default: null, rendered: false }
|
||||
rawSrc: { default: null, rendered: false },
|
||||
originalSrc: { default: null, rendered: false }
|
||||
}
|
||||
},
|
||||
parseMarkdown: (token, helpers) =>
|
||||
@@ -187,10 +193,14 @@ export function createRichMarkdownExtensions({
|
||||
src: token.href,
|
||||
alt: token.text || '',
|
||||
title: token.title,
|
||||
rawSrc: extractRawDestination(token.raw)
|
||||
rawSrc: extractRawDestination(token.raw),
|
||||
originalSrc: token.href
|
||||
}),
|
||||
renderMarkdown: (node) => {
|
||||
const src = node.attrs?.rawSrc ?? node.attrs?.src ?? ''
|
||||
const src =
|
||||
node.attrs?.rawSrc && node.attrs?.src === node.attrs?.originalSrc
|
||||
? node.attrs.rawSrc
|
||||
: (node.attrs?.src ?? '')
|
||||
const alt = node.attrs?.alt ?? ''
|
||||
const title = node.attrs?.title ?? ''
|
||||
return title ? `` : ``
|
||||
|
||||
@@ -171,7 +171,8 @@ function escapeLinkAndImageAttributes(node: JSONContent): void {
|
||||
const destKey = kind === 'image' ? 'src' : 'href'
|
||||
const dest = attrs[destKey]
|
||||
const rawDest = kind === 'link' ? attrs.rawHref : attrs.rawSrc
|
||||
if (typeof rawDest === 'string') {
|
||||
const originalDest = kind === 'link' ? attrs.originalHref : attrs.originalSrc
|
||||
if (typeof rawDest === 'string' && dest === originalDest) {
|
||||
attrs[destKey] = rawDest
|
||||
}
|
||||
if (typeof dest === 'string' && destNeedsEscape(dest)) {
|
||||
|
||||
Reference in New Issue
Block a user