From ddb8391b02c624349e5cb78eb5daf37ef7114852 Mon Sep 17 00:00:00 2001 From: Khushvendra Date: Tue, 7 Apr 2026 00:30:46 +0530 Subject: [PATCH] refactor: move room and space mention rendering into link components --- package.json | 4 +- src/app/components/Content.svelte | 3 - src/app/components/ContentLinkBlock.svelte | 33 ++++++- src/app/components/ContentLinkInline.svelte | 25 +++++- src/app/components/ContentMinimal.svelte | 3 - src/app/components/ContentText.svelte | 24 ------ src/lib/content-text.ts | 95 --------------------- tests/content-text.test.ts | 47 ---------- 8 files changed, 53 insertions(+), 181 deletions(-) delete mode 100644 src/app/components/ContentText.svelte delete mode 100644 src/lib/content-text.ts delete mode 100644 tests/content-text.test.ts diff --git a/package.json b/package.json index bad0504b..caa76285 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "tauri:icons": "tauri icon assets/logo.png --output src-tauri/icons", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "test": "vitest run", "lint": "prettier --check src && eslint src", "format": "git diff head --name-only --diff-filter d | grep -E '(js|ts|svelte|css)$' | xargs -r prettier --write", "format:all": "prettier --write src", @@ -40,8 +39,7 @@ "tailwindcss": "^4.2.2", "typescript": "^5.9.3", "typescript-eslint": "^8.53.1", - "vite": "^5.4.21", - "vitest": "^2.1.9" + "vite": "^5.4.21" }, "type": "module", "dependencies": { diff --git a/src/app/components/Content.svelte b/src/app/components/Content.svelte index 6af6d8d0..e252b0bb 100644 --- a/src/app/components/Content.svelte +++ b/src/app/components/Content.svelte @@ -24,7 +24,6 @@ import Danger from "@assets/icons/danger-triangle.svg?dataurl" import Icon from "@lib/components/Icon.svelte" import Button from "@lib/components/Button.svelte" - import ContentText from "@app/components/ContentText.svelte" import ContentToken from "@app/components/ContentToken.svelte" import ContentEmoji from "@app/components/ContentEmoji.svelte" import ContentCode from "@app/components/ContentCode.svelte" @@ -156,8 +155,6 @@ {#each shortContent as parsed, i} {#if isNewline(parsed) && !isBlock(i - 1)} - {:else if isText(parsed)} - {:else if isTopic(parsed)} {:else if isEmoji(parsed)} diff --git a/src/app/components/ContentLinkBlock.svelte b/src/app/components/ContentLinkBlock.svelte index c818f1ad..ac776d28 100644 --- a/src/app/components/ContentLinkBlock.svelte +++ b/src/app/components/ContentLinkBlock.svelte @@ -1,8 +1,10 @@ - -{#each parts as part, i (i)} - {#if part.type === "room"} - {part.value} - {:else if part.type === "relay"} - {part.value} - {:else} - {part.value} - {/if} -{/each} diff --git a/src/lib/content-text.ts b/src/lib/content-text.ts deleted file mode 100644 index 27e2ab7d..00000000 --- a/src/lib/content-text.ts +++ /dev/null @@ -1,95 +0,0 @@ -import {isRelayUrl, normalizeRelayUrl} from "@welshman/util" - -export type ContentTextPart = - | {type: "text"; value: string} - | {type: "room"; value: string; url: string; h: string} - | {type: "relay"; value: string; url: string} - -const CONTENT_URL_PATTERN = /wss?:\/\/\S+/g -const TRAILING_PUNCTUATION_PATTERN = /[),.!?;:\]}]+$/ - -const appendTextPart = (parts: ContentTextPart[], text: string) => { - if (!text) { - return - } - - const last = parts.at(-1) - - if (last?.type === "text") { - last.value += text - - return - } - - parts.push({type: "text", value: text}) -} - -const splitTokenSuffix = (token: string) => { - const suffixMatch = token.match(TRAILING_PUNCTUATION_PATTERN) - const suffix = suffixMatch?.[0] || "" - - if (!suffix) { - return {trimmed: token, suffix} - } - - return { - trimmed: token.slice(0, -suffix.length), - suffix, - } -} - -const toRoomPart = (token: string) => { - const separatorIndex = token.indexOf("'") - - if (separatorIndex === -1) { - return undefined - } - - const url = token.slice(0, separatorIndex) - const h = token.slice(separatorIndex + 1) - - if (!h || !isRelayUrl(url)) { - return undefined - } - - return {type: "room", value: token, url: normalizeRelayUrl(url), h} as const -} - -const toRelayPart = (token: string) => { - if (!isRelayUrl(token)) { - return undefined - } - - return {type: "relay", value: token, url: normalizeRelayUrl(token)} as const -} - -export const parseContentTextParts = (text: string) => { - const parts: ContentTextPart[] = [] - let lastIndex = 0 - - for (const match of text.matchAll(CONTENT_URL_PATTERN)) { - const start = match.index || 0 - - appendTextPart(parts, text.slice(lastIndex, start)) - - const token = match[0] - const {trimmed, suffix} = splitTokenSuffix(token) - const roomPart = toRoomPart(trimmed) - const relayPart = toRelayPart(trimmed) - - if (roomPart) { - parts.push(roomPart) - } else if (relayPart) { - parts.push(relayPart) - } else { - appendTextPart(parts, trimmed) - } - - appendTextPart(parts, suffix) - lastIndex = start + token.length - } - - appendTextPart(parts, text.slice(lastIndex)) - - return parts -} diff --git a/tests/content-text.test.ts b/tests/content-text.test.ts deleted file mode 100644 index b04e29b2..00000000 --- a/tests/content-text.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {describe, expect, it} from "vitest" -import {parseContentTextParts} from "../src/lib/content-text" - -describe("parseContentTextParts", () => { - it("parses room references as relay_url'room_id", () => { - const parts = parseContentTextParts("Join wss://relay.example.com'general now") - - expect(parts).toHaveLength(3) - expect(parts[0]).toEqual({type: "text", value: "Join "}) - expect(parts[1]).toMatchObject({ - type: "room", - value: "wss://relay.example.com'general", - h: "general", - }) - expect(parts[2]).toEqual({type: "text", value: " now"}) - }) - - it("parses relay urls as relay parts", () => { - const parts = parseContentTextParts("Try wss://relay.example.com") - - expect(parts).toHaveLength(2) - expect(parts[0]).toEqual({type: "text", value: "Try "}) - expect(parts[1]).toMatchObject({ - type: "relay", - value: "wss://relay.example.com", - }) - }) - - it("keeps trailing punctuation outside links", () => { - const parts = parseContentTextParts("See wss://relay.example.com'chat), thanks") - - expect(parts).toHaveLength(3) - expect(parts[0]).toEqual({type: "text", value: "See "}) - expect(parts[1]).toMatchObject({ - type: "room", - value: "wss://relay.example.com'chat", - h: "chat", - }) - expect(parts[2]).toEqual({type: "text", value: "), thanks"}) - }) - - it("leaves non-relay urls as plain text", () => { - const parts = parseContentTextParts("https://example.com/path") - - expect(parts).toEqual([{type: "text", value: "https://example.com/path"}]) - }) -}) \ No newline at end of file