diff --git a/plugins/controlled-documents-resources/src/components/document/popups/AddCommentPopup.svelte b/plugins/controlled-documents-resources/src/components/document/popups/AddCommentPopup.svelte index f8321ce8a6..f46b22e0ce 100644 --- a/plugins/controlled-documents-resources/src/components/document/popups/AddCommentPopup.svelte +++ b/plugins/controlled-documents-resources/src/components/document/popups/AddCommentPopup.svelte @@ -22,10 +22,20 @@ let popup: HTMLDivElement | undefined + function isClickInsidePopup (target: Node): boolean { + if (popup !== undefined && popup.contains(target)) return true + if (!(target instanceof Element)) return false + + if (target.closest('.tippy-box') !== null) return true + if (target.closest('[data-block-editor-blur="true"]') !== null) return true + + return false + } + function handleClick (event: MouseEvent): void { if (event.target instanceof Node) { const top = $popups.length > 0 && $popups[$popups.length - 1].id === popupId - if (top && popup !== undefined && !popup.contains(event.target)) { + if (top && !isClickInsidePopup(event.target)) { event.preventDefault() event.stopPropagation() dispatch('close', undefined) diff --git a/plugins/converter-resources/src/__tests__/markdown.escape.test.ts b/plugins/converter-resources/src/__tests__/markdown.escape.test.ts index 29926dcf9d..737b943de4 100644 --- a/plugins/converter-resources/src/__tests__/markdown.escape.test.ts +++ b/plugins/converter-resources/src/__tests__/markdown.escape.test.ts @@ -13,7 +13,7 @@ // limitations under the License. // -import { escapeMarkdownLinkText, escapeMarkdownLinkUrl, escapeTableCell } from '../markdown/escape' +import { escapeMarkdownLinkText, escapeMarkdownLinkUrl } from '../markdown/escape' describe('markdown/escape', () => { describe('escapeMarkdownLinkText', () => { @@ -56,30 +56,4 @@ describe('markdown/escape', () => { expect(escapeMarkdownLinkUrl('https://example.com')).toBe('https://example.com') }) }) - - describe('escapeTableCellPreservingLink', () => { - it('escapes plain text pipe for table safety', () => { - expect(escapeTableCell('a|b')).toBe('a\\|b') - }) - - it('preserves markdown link and escapes pipes inside text and URL', () => { - const input = '[a|b](http://example.com/x|y)' - expect(escapeTableCell(input)).toBe('[a\\|b](http://example.com/x\\|y)') - }) - - it('escapes pipes even when value contains escaped characters', () => { - const input = '[a|b](http://example.com/x|y\\z)' - expect(escapeTableCell(input)).toBe('[a\\|b](http://example.com/x\\|y\\\\z)') - }) - - it('treats strings that do not end with `)` as plain text', () => { - const input = '[a|b](http://example.com/x|y' - expect(escapeTableCell(input)).toBe('\\[a\\|b\\](http://example.com/x\\|y') - }) - - it('returns empty string for null/undefined', () => { - expect(escapeTableCell(null)).toBe('') - expect(escapeTableCell(undefined)).toBe('') - }) - }) }) diff --git a/plugins/converter-resources/src/markdown/escape.ts b/plugins/converter-resources/src/markdown/escape.ts index 3175bf13eb..0a4b0360c4 100644 --- a/plugins/converter-resources/src/markdown/escape.ts +++ b/plugins/converter-resources/src/markdown/escape.ts @@ -37,23 +37,3 @@ export function escapeMarkdownLinkUrl (url: string): string { .replace(/\|/g, '\\|') ) } - -/** - * Escape a markdown table cell while preserving `[text](url)` links. - */ -export function escapeTableCell (value: unknown): string { - const s = value == null ? '' : String(value) - - const sep = s.indexOf('](') - const looksLikeMarkdownLink = s.startsWith('[') && sep !== -1 && s.endsWith(')') - if (!looksLikeMarkdownLink) { - return escapeMarkdownLinkText(s) - } - - const rawText = s.slice(1, sep) - const rawUrl = s.slice(sep + 2, -1) - - const escapedText = escapeMarkdownLinkText(rawText) - const escapedUrl = escapeMarkdownLinkUrl(rawUrl) - return `[${escapedText}](${escapedUrl})` -} diff --git a/plugins/converter-resources/src/markdown/tableBuilder.ts b/plugins/converter-resources/src/markdown/tableBuilder.ts index 16109337c9..8d2bcdec58 100644 --- a/plugins/converter-resources/src/markdown/tableBuilder.ts +++ b/plugins/converter-resources/src/markdown/tableBuilder.ts @@ -28,7 +28,7 @@ import type { CopyAsMarkdownTableProps, CopyRelationshipTableAsMarkdownProps } f import { formatValue } from '../formatter' import { generateHeaders, loadViewletConfig, buildTableModel } from '../model' import { rebuildRelationshipTableViewModel, isRelationshipTable } from '../data' -import { escapeTableCell } from './escape' +import { escapeMarkdownLinkText } from './escape' import { createMarkdownLink } from './link' async function preloadRefLookups ( @@ -253,7 +253,7 @@ export async function buildMarkdownTableFromDocs ( const linkValue = await createMarkdownLink(hierarchy, card, value) row.push(linkValue) } else { - row.push(escapeTableCell(value)) + row.push(escapeMarkdownLinkText(value == null ? '' : String(value))) } } rows.push(row) @@ -375,7 +375,7 @@ export async function buildRelationshipTableMarkdown ( if (isDocumentTitle) { value = await createMarkdownLink(hierarchy, docToUse, value) } else { - value = escapeTableCell(value) + value = escapeMarkdownLinkText(value == null ? '' : String(value)) } row[attrIndex] = value diff --git a/plugins/text-editor-resources/src/components/extension/shortcuts/smartPaste.ts b/plugins/text-editor-resources/src/components/extension/shortcuts/smartPaste.ts index b568e76ea6..0cd4924a7c 100644 --- a/plugins/text-editor-resources/src/components/extension/shortcuts/smartPaste.ts +++ b/plugins/text-editor-resources/src/components/extension/shortcuts/smartPaste.ts @@ -36,6 +36,7 @@ function PasteTextAsMarkdownPlugin (): Plugin { if (clipboardData === null) return false const pastedText = clipboardData.getData('text/plain') + const pastedMarkdown = clipboardData.getData('text/markdown') // check if we are in code block const { $from } = view.state.selection @@ -47,12 +48,21 @@ function PasteTextAsMarkdownPlugin (): Plugin { } } + // If the clipboard explicitly provides markdown, prefer it even if other types (e.g. html) exist. + const hasMarkdown = pastedMarkdown.trim().length > 0 + const markdownSource = hasMarkdown ? pastedMarkdown : pastedText + + // Table copies include metadata comment; treat as markdown even when clipboard has rich types. + const hasTableMetadata = + pastedText.includes('