Files
fluxer/fluxer_app/src/features/lexical/composer/ComposerLinkPaste.ts
T

54 lines
1.4 KiB
TypeScript

// SPDX-License-Identifier: AGPL-3.0-or-later
const MAX_PASTED_URL_LENGTH = 2048;
const MAX_WRAPPED_SELECTION_LENGTH = 1024;
const ALLOWED_PROTOCOLS = new Set(['http:', 'https:']);
export function parsePastedUrl(clipboardText: string | null | undefined): string | null {
if (clipboardText == null) {
return null;
}
const candidate = clipboardText.trim();
if (candidate.length === 0 || candidate.length > MAX_PASTED_URL_LENGTH) {
return null;
}
if (/[\s<>]/.test(candidate)) {
return null;
}
let parsed: URL;
try {
parsed = new URL(candidate);
} catch {
return null;
}
if (!ALLOWED_PROTOCOLS.has(parsed.protocol)) {
return null;
}
return candidate;
}
export function canWrapSelectionAsLink(selectedText: string): boolean {
if (selectedText.trim().length === 0 || selectedText.length > MAX_WRAPPED_SELECTION_LENGTH) {
return false;
}
if (/[\r\n]/.test(selectedText)) {
return false;
}
if (/[[\]]/.test(selectedText)) {
return false;
}
return parsePastedUrl(selectedText) === null;
}
export function buildMaskedLink(selectedText: string, url: string): string {
return `[${selectedText}](<${url}>)`;
}
export function resolvePastedLinkInsertion(pastedText: string, selectedText: string): string | null {
const url = parsePastedUrl(pastedText);
if (url === null || !canWrapSelectionAsLink(selectedText)) {
return null;
}
return buildMaskedLink(selectedText, url);
}