From 9031055ec9bfa45969347ba4641be9d79fb99e06 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Thu, 26 Feb 2026 03:30:10 +0400 Subject: [PATCH] Replace the tag link text to resolved tag name when possible --- src/content/components/BlockCommunication.ts | 71 ++++++++++++++++++-- src/lib/extension/settings/TagSettings.ts | 9 +++ src/routes/preferences/tags/+page.svelte | 13 +++- src/stores/preferences/tag.ts | 7 ++ 4 files changed, 94 insertions(+), 6 deletions(-) diff --git a/src/content/components/BlockCommunication.ts b/src/content/components/BlockCommunication.ts index 217170d..0c0d151 100644 --- a/src/content/components/BlockCommunication.ts +++ b/src/content/components/BlockCommunication.ts @@ -8,6 +8,7 @@ export class BlockCommunication extends BaseComponent { #tagLinks: HTMLAnchorElement[] = []; #tagLinksReplaced: boolean | null = null; + #linkTextReplaced: boolean | null = null; protected build() { this.#contentSection = this.container.querySelector('.communication__content'); @@ -15,14 +16,30 @@ export class BlockCommunication extends BaseComponent { } protected init() { - BlockCommunication.#tagSettings.resolveReplaceLinks().then(this.#onReplaceLinkSettingResolved.bind(this)); + Promise.all([ + BlockCommunication.#tagSettings.resolveReplaceLinks(), + BlockCommunication.#tagSettings.resolveReplaceLinkText(), + ]).then(([replaceLinks, replaceLinkText]) => { + this.#onReplaceLinkSettingResolved( + replaceLinks, + replaceLinkText + ); + }); + BlockCommunication.#tagSettings.subscribe(settings => { - this.#onReplaceLinkSettingResolved(settings.replaceLinks ?? false); + this.#onReplaceLinkSettingResolved( + settings.replaceLinks ?? false, + settings.replaceLinkText ?? true + ); }); } - #onReplaceLinkSettingResolved(haveToReplaceLinks: boolean) { - if (!this.#tagLinks.length || this.#tagLinksReplaced === haveToReplaceLinks) { + #onReplaceLinkSettingResolved(haveToReplaceLinks: boolean, shouldReplaceLinkText: boolean) { + if ( + !this.#tagLinks.length + || this.#tagLinksReplaced === haveToReplaceLinks + && this.#linkTextReplaced === shouldReplaceLinkText + ) { return; } @@ -34,15 +51,52 @@ export class BlockCommunication extends BaseComponent { linkElement.textContent = linkElement.children[0].textContent; } + /** + * Resolved tag name. It should be stored for the text replacement. + */ + let tagName: string | undefined; + if (haveToReplaceLinks) { - const tagName = resolveTagNameFromLink(new URL(linkElement.href)) ?? ''; + tagName = resolveTagNameFromLink(new URL(linkElement.href)) ?? ''; linkElement.dataset.tagCategory = resolveTagCategoryFromTagName(tagName) ?? ''; } else { linkElement.dataset.tagCategory = ''; } + + this.#toggleTagLinkText( + linkElement, + haveToReplaceLinks && shouldReplaceLinkText, + tagName, + ); } this.#tagLinksReplaced = haveToReplaceLinks; + this.#linkTextReplaced = shouldReplaceLinkText; + } + + /** + * Swap the link text with the tag name or restore it back to original content. This function will only perform + * replacement on links without any additional tags inside. This will ensure link won't break original content. + * @param linkElement Element to swap the text on. + * @param shouldSwapToTagName Should we swap the text to tag name or retore it back from memory. + * @param tagName Tag name to swap the text to. If not provided, text will be swapped back. + * @private + */ + #toggleTagLinkText(linkElement: HTMLElement, shouldSwapToTagName: boolean, tagName?: string) { + if (linkElement.childElementCount) { + return; + } + + // Make sure we save the original text to memory. + if (!BlockCommunication.#originalTagLinkTexts.has(linkElement)) { + BlockCommunication.#originalTagLinkTexts.set(linkElement, linkElement.textContent); + } + + if (shouldSwapToTagName && tagName) { + linkElement.textContent = tagName; + } else { + linkElement.textContent = BlockCommunication.#originalTagLinkTexts.get(linkElement) ?? linkElement.textContent; + } } #findAllTagLinks(): HTMLAnchorElement[] { @@ -60,6 +114,13 @@ export class BlockCommunication extends BaseComponent { static #tagSettings = new TagSettings(); + /** + * Map of links to their original texts. These texts need to be stored here to make them restorable. Keys is a link + * element and value is a text. + * @private + */ + static #originalTagLinkTexts: WeakMap = new WeakMap(); + static findAndInitializeAll() { for (const container of document.querySelectorAll('.block.communication')) { if (getComponent(container)) { diff --git a/src/lib/extension/settings/TagSettings.ts b/src/lib/extension/settings/TagSettings.ts index 7c69628..f657557 100644 --- a/src/lib/extension/settings/TagSettings.ts +++ b/src/lib/extension/settings/TagSettings.ts @@ -3,6 +3,7 @@ import CacheableSettings from "$lib/extension/base/CacheableSettings"; interface TagSettingsFields { groupSeparation: boolean; replaceLinks: boolean; + replaceLinkText: boolean; } export default class TagSettings extends CacheableSettings { @@ -18,6 +19,10 @@ export default class TagSettings extends CacheableSettings { return this._resolveSetting("replaceLinks", false); } + async resolveReplaceLinkText() { + return this._resolveSetting("replaceLinkText", true); + } + async setGroupSeparation(value: boolean) { return this._writeSetting("groupSeparation", Boolean(value)); } @@ -25,4 +30,8 @@ export default class TagSettings extends CacheableSettings { async setReplaceLinks(value: boolean) { return this._writeSetting("replaceLinks", Boolean(value)); } + + async setReplaceLinkText(value: boolean) { + return this._writeSetting("replaceLinkText", Boolean(value)); + } } diff --git a/src/routes/preferences/tags/+page.svelte b/src/routes/preferences/tags/+page.svelte index 958c2bf..a9e4df2 100644 --- a/src/routes/preferences/tags/+page.svelte +++ b/src/routes/preferences/tags/+page.svelte @@ -5,7 +5,11 @@ import Menu from "$components/ui/menu/Menu.svelte"; import MenuItem from "$components/ui/menu/MenuItem.svelte"; import { stripBlacklistedTagsEnabled } from "$stores/preferences/maintenance"; - import { shouldReplaceLinksOnForumPosts, shouldSeparateTagGroups } from "$stores/preferences/tag"; + import { + shouldReplaceLinksOnForumPosts, + shouldReplaceTextOfTagLinks, + shouldSeparateTagGroups + } from "$stores/preferences/tag"; import { popupTitle } from "$stores/popup"; $popupTitle = 'Tagging Preferences'; @@ -31,4 +35,11 @@ Find and replace links to the tags in the forum posts + {#if $shouldReplaceLinksOnForumPosts} + + + Try to replace text on links pointing to tags in forum posts + + + {/if} diff --git a/src/stores/preferences/tag.ts b/src/stores/preferences/tag.ts index d7a06cf..f491c5c 100644 --- a/src/stores/preferences/tag.ts +++ b/src/stores/preferences/tag.ts @@ -5,11 +5,13 @@ const tagSettings = new TagSettings(); export const shouldSeparateTagGroups = writable(false); export const shouldReplaceLinksOnForumPosts = writable(false); +export const shouldReplaceTextOfTagLinks = writable(true); Promise .allSettled([ tagSettings.resolveGroupSeparation().then(value => shouldSeparateTagGroups.set(value)), tagSettings.resolveReplaceLinks().then(value => shouldReplaceLinksOnForumPosts.set(value)), + tagSettings.resolveReplaceLinkText().then(value => shouldReplaceTextOfTagLinks.set(value)), ]) .then(() => { shouldSeparateTagGroups.subscribe(value => { @@ -20,8 +22,13 @@ Promise void tagSettings.setReplaceLinks(value); }); + shouldReplaceTextOfTagLinks.subscribe(value => { + void tagSettings.setReplaceLinkText(value); + }); + tagSettings.subscribe(settings => { shouldSeparateTagGroups.set(Boolean(settings.groupSeparation)); shouldReplaceLinksOnForumPosts.set(Boolean(settings.replaceLinks)); + shouldReplaceTextOfTagLinks.set(Boolean(settings.replaceLinkText)); }); });