Files
sendou.ink/app/utils/strings.ts
Kalle 0a4518a0e2
Some checks failed
E2E Tests / e2e (push) Has been cancelled
Tests and checks on push / run-checks-and-tests (push) Has been cancelled
Updates translation progress / update-translation-progress-issue (push) Has been cancelled
Tournament multi step + grouped and early end SE/DE (#3449)
2026-10-09 18:38:25 +03:00

135 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { GearType } from "~/modules/in-game-lists/types";
import { assertUnreachable } from "./types";
export function inGameNameWithoutDiscriminator(inGameName: string) {
return inGameName.split("#")[0];
}
export const rawSensToString = (sens: number) =>
`${sens > 0 ? "+" : ""}${sens / 10}`;
export function gearTypeToInitial(gearType: GearType) {
switch (gearType) {
case "HEAD":
return "H";
case "CLOTHES":
return "C";
case "SHOES":
return "S";
default:
assertUnreachable(gearType);
}
}
export function pathnameFromPotentialURL(maybeUrl: string) {
const parsed = safeParseUrl(maybeUrl);
if (parsed) return stripEdgeSlashes(parsed.pathname);
// handle a URL pasted without a protocol, e.g. "discord.gg/FW4dKrY"
const parsedWithProtocol = safeParseUrl(`https://${maybeUrl}`);
const pathname = parsedWithProtocol
? stripEdgeSlashes(parsedWithProtocol.pathname)
: "";
return pathname || maybeUrl;
}
function safeParseUrl(value: string) {
try {
return new URL(value);
} catch {
return null;
}
}
function stripEdgeSlashes(pathname: string) {
return pathname.replace(/^\/+|\/+$/g, "");
}
export function truncateBySentence(value: string, max: number) {
if (value.length <= max) {
return value;
}
// a sentence only ends at a terminator followed by whitespace, so that
// e.g. "18.00" does not split in the middle
const sentences = value.match(/[\s\S]+?(?:[.!?](?=\s|$)|\n|$)/g) || [];
let result = "";
for (const sentence of sentences) {
if ((result + sentence).length > max) {
break;
}
result += sentence;
}
// when cutting at a sentence boundary would leave most of the budget
// unused, a mid-sentence cut that fills it is more informative
if (result.length < max / 2) {
return value.slice(0, max).trim();
}
return result.trim();
}
// based on https://github.com/zuchka/remove-markdown
const NAMED_HTML_ENTITIES: Record<string, string> = {
nbsp: " ",
amp: "&",
lt: "<",
gt: ">",
quot: '"',
apos: "'",
};
export function removeMarkdown(value: string) {
const htmlReplaceRegex = /<[^>]*>/g;
return (
value
// Remove HTML tags
.replace(htmlReplaceRegex, "")
// Decode named HTML entities (e.g. &nbsp;, &amp;)
.replace(/&([a-zA-Z]+);/g, (match, name: string) => {
const replacement = NAMED_HTML_ENTITIES[name.toLowerCase()];
return replacement ?? match;
})
// Decode numeric HTML entities (e.g. &#160; or &#xA0;)
.replace(/&#(x?[0-9a-fA-F]+);/g, (_, code: string) => {
const codePoint = code.startsWith("x")
? Number.parseInt(code.slice(1), 16)
: Number.parseInt(code, 10);
const isValidCodePoint =
Number.isFinite(codePoint) && codePoint >= 0 && codePoint <= 0x10ffff;
return isValidCodePoint ? String.fromCodePoint(codePoint) : "";
})
// Remove setext-style headers
.replace(/^[=-]{2,}\s*$/g, "")
// Remove footnotes?
.replace(/\[\^.+?\](: .*?$)?/g, "")
.replace(/\s{0,2}\[.*?\]: .*?$/g, "")
// Remove images
.replace(/!\[(.*?)\][[(].*?[\])]/g, "")
// Remove inline links
.replace(/\[([^\]]*?)\][[(].*?[\])]/g, "$1")
// Remove blockquotes
.replace(/^(\n)?\s{0,3}>\s?/gm, "$1")
// Remove reference-style links?
.replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, "")
// Remove headers
.replace(/^\s{0,3}#{1,6}\s*/gm, "")
// Remove * emphasis
.replace(/(\*+)([^\s*])(.*?[^\s*])??\1/g, "$2$3")
// Remove _ emphasis; unlike *, it only renders when surrounded by whitespace or string edges
.replace(/(^|\W)([_]+)(\S)(.*?\S)??\2($|\W)/g, "$1$3$4$5")
// Remove code blocks
.replace(/(`{3,})(.*?)\1/gm, "$2")
// Remove inline code
.replace(/`(.+?)`/g, "$1")
// Replace strike through
.replace(/~(.*?)~/g, "$1")
// Collapse runs of whitespace (e.g. from decoded &nbsp; or stripped tags)
.replace(/[ \t ]{2,}/g, " ")
.trim()
);
}