0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-10-18 01:34:23 -04:00

Enable more markdown paste features in textarea editor (#35494)

Enable the [same paste
features](https://github.com/github/paste-markdown#paste-markdown-objects)
that GitHub has, notably the ability to paste text containing HTML links
and have them automatically turn into Markdown links. As far as I can
tell, previous paste features all work as expected.

---------

Signed-off-by: silverwind <me@silverwind.io>
This commit is contained in:
silverwind
2025-09-17 01:55:57 +02:00
committed by GitHub
parent 9332ff291b
commit 6033c47f90
7 changed files with 18 additions and 95 deletions

View File

@@ -286,28 +286,6 @@ export function isElemVisible(el: HTMLElement): boolean {
return !el.classList.contains('tw-hidden') && (el.offsetWidth || el.offsetHeight || el.getClientRects().length) && el.style.display !== 'none';
}
/** replace selected text in a textarea while preserving editor history, e.g. CTRL-Z works after this */
export function replaceTextareaSelection(textarea: HTMLTextAreaElement, text: string) {
const before = textarea.value.slice(0, textarea.selectionStart ?? undefined);
const after = textarea.value.slice(textarea.selectionEnd ?? undefined);
let success = false;
textarea.contentEditable = 'true';
try {
success = document.execCommand('insertText', false, text); // eslint-disable-line @typescript-eslint/no-deprecated
} catch {} // ignore the error if execCommand is not supported or failed
textarea.contentEditable = 'false';
if (success && !textarea.value.slice(0, textarea.selectionStart ?? undefined).endsWith(text)) {
success = false;
}
if (!success) {
textarea.value = `${before}${text}${after}`;
textarea.dispatchEvent(new CustomEvent('change', {bubbles: true, cancelable: true}));
}
}
export function createElementFromHTML<T extends HTMLElement>(htmlString: string): T {
htmlString = htmlString.trim();
// There is no way to create some elements without a proper parent, jQuery's approach: https://github.com/jquery/jquery/blob/main/src/manipulation/wrapMap.js

View File

@@ -1,17 +1,10 @@
import {pathEscapeSegments, isUrl, toOriginUrl} from './url.ts';
import {pathEscapeSegments, toOriginUrl} from './url.ts';
test('pathEscapeSegments', () => {
expect(pathEscapeSegments('a/b/c')).toEqual('a/b/c');
expect(pathEscapeSegments('a/b/ c')).toEqual('a/b/%20c');
});
test('isUrl', () => {
expect(isUrl('https://example.com')).toEqual(true);
expect(isUrl('https://example.com/')).toEqual(true);
expect(isUrl('https://example.com/index.html')).toEqual(true);
expect(isUrl('/index.html')).toEqual(false);
});
test('toOriginUrl', () => {
const oldLocation = String(window.location);
for (const origin of ['https://example.com', 'https://example.com:3000']) {

View File

@@ -2,18 +2,6 @@ export function pathEscapeSegments(s: string): string {
return s.split('/').map(encodeURIComponent).join('/');
}
function stripSlash(url: string): string {
return url.endsWith('/') ? url.slice(0, -1) : url;
}
export function isUrl(url: string): boolean {
try {
return stripSlash((new URL(url).href)).trim() === stripSlash(url).trim();
} catch {
return false;
}
}
/** Convert an absolute or relative URL to an absolute URL with the current origin. It only
* processes absolute HTTP/HTTPS URLs or relative URLs like '/xxx' or '//host/xxx'. */
export function toOriginUrl(urlStr: string) {