mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
54 lines
1.4 KiB
TypeScript
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);
|
|
}
|