From 39a55430cd9db0ffe65d9d9570733dd599ac5eb6 Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Tue, 3 Mar 2026 00:22:57 -0800 Subject: [PATCH 01/16] initial commit --- .../hide_recommended_community_posts.js | 14 ++-- src/features/postblock/index.css | 4 - src/features/postblock/index.js | 18 +++-- src/features/tweaks/hide_blocked_blogs.js | 22 +++--- src/features/tweaks/hide_filtered_posts.js | 21 ++--- src/features/tweaks/hide_liked_posts.js | 15 ++-- src/features/tweaks/hide_my_posts.js | 18 ++--- src/main_world/hide_posts.js | 77 +++++++++++++++++++ src/utils/timeline_id.js | 9 +++ 9 files changed, 139 insertions(+), 59 deletions(-) delete mode 100644 src/features/postblock/index.css create mode 100644 src/main_world/hide_posts.js diff --git a/src/features/no_recommended/hide_recommended_community_posts.js b/src/features/no_recommended/hide_recommended_community_posts.js index 896b047c81..bcf70d4fae 100644 --- a/src/features/no_recommended/hide_recommended_community_posts.js +++ b/src/features/no_recommended/hide_recommended_community_posts.js @@ -1,23 +1,20 @@ -import { buildStyle, filterPostElements, getTimelineItemWrapper } from '../../utils/interface.js'; +import { createPostHideFunctions } from '../../main_world/hide_posts.js'; +import { filterPostElements } from '../../utils/interface.js'; import { onNewPosts } from '../../utils/mutations.js'; import { timelineObject } from '../../utils/react_props.js'; import { forYouTimelineFilter } from '../../utils/timeline_id.js'; import { joinedCommunityUuids } from '../../utils/user.js'; -const hiddenAttribute = 'data-no-recommended-community-posts-hidden'; const timeline = forYouTimelineFilter; const includeFiltered = true; -export const styleElement = buildStyle(`[${hiddenAttribute}] { - content: linear-gradient(transparent, transparent); - height: 0; -}`); +const { hidePost, showPosts } = createPostHideFunctions({ id: 'no-recommended-community-posts' }); const processPosts = postElements => filterPostElements(postElements, { timeline, includeFiltered }).forEach(async postElement => { const { community } = await timelineObject(postElement); if (community && !joinedCommunityUuids.includes(community.uuid)) { - getTimelineItemWrapper(postElement).setAttribute(hiddenAttribute, ''); + hidePost(postElement); } }); @@ -27,6 +24,5 @@ export const main = async function () { export const clean = async function () { onNewPosts.removeListener(processPosts); - - $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); + showPosts(); }; diff --git a/src/features/postblock/index.css b/src/features/postblock/index.css deleted file mode 100644 index 6e41175aeb..0000000000 --- a/src/features/postblock/index.css +++ /dev/null @@ -1,4 +0,0 @@ -[data-postblock-hidden] { - content: linear-gradient(transparent, transparent); - height: 0; -} diff --git a/src/features/postblock/index.js b/src/features/postblock/index.js index c4d24023f2..4c875d71d9 100644 --- a/src/features/postblock/index.js +++ b/src/features/postblock/index.js @@ -1,5 +1,6 @@ +import { createPostHideFunctions } from '../../main_world/hide_posts.js'; import { dom } from '../../utils/dom.js'; -import { getTimelineItemWrapper, filterPostElements } from '../../utils/interface.js'; +import { filterPostElements } from '../../utils/interface.js'; import { registerMeatballItem, unregisterMeatballItem } from '../../utils/meatballs.js'; import { showModal, hideModal, modalCancelButton } from '../../utils/modals.js'; import { onNewPosts, pageModifications } from '../../utils/mutations.js'; @@ -7,11 +8,18 @@ import { timelineObject } from '../../utils/react_props.js'; const meatballButtonId = 'postblock'; const meatballButtonLabel = 'Block this post'; -const hiddenAttribute = 'data-postblock-hidden'; const storageKey = 'postblock.blockedPostRootIDs'; let blockedPostRootIDs = []; +const { hidePost, showPost, showPosts } = createPostHideFunctions({ + id: 'postblock', + controlsOnPermalinkPage: { + message: 'You have hidden this post with PostBlock!', + buttonText: 'show post anyway', + }, +}); + const processPosts = postElements => filterPostElements(postElements, { includeFiltered: true }).forEach(async postElement => { const postID = postElement.dataset.id; @@ -20,9 +28,9 @@ const processPosts = postElements => const rootID = rebloggedRootId || postID; if (blockedPostRootIDs.includes(rootID)) { - getTimelineItemWrapper(postElement).setAttribute(hiddenAttribute, ''); + hidePost(postElement); } else { - getTimelineItemWrapper(postElement).removeAttribute(hiddenAttribute); + showPost(postElement); } }); @@ -68,7 +76,7 @@ export const clean = async function () { unregisterMeatballItem(meatballButtonId); onNewPosts.removeListener(processPosts); - $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); + showPosts(); }; export const stylesheet = true; diff --git a/src/features/tweaks/hide_blocked_blogs.js b/src/features/tweaks/hide_blocked_blogs.js index 7b6bcb9868..c508b99c87 100644 --- a/src/features/tweaks/hide_blocked_blogs.js +++ b/src/features/tweaks/hide_blocked_blogs.js @@ -1,14 +1,18 @@ -import { buildStyle, getTimelineItemWrapper, filterPostElements } from '../../utils/interface.js'; +import { createPostHideFunctions } from '../../main_world/hide_posts.js'; +import { filterPostElements } from '../../utils/interface.js'; import { onNewPosts } from '../../utils/mutations.js'; import { isMyPost, timelineObject } from '../../utils/react_props.js'; import { blogTimelineFilter, timelineSelector } from '../../utils/timeline_id.js'; -const hiddenAttribute = 'data-xkit-tweaks-hide-blocked-blogs-hidden'; -export const styleElement = buildStyle(` -[${hiddenAttribute}] { - content: linear-gradient(transparent, transparent); - height: 0; -}`); +const { hidePost, showPosts } = createPostHideFunctions({ + id: 'tweaks-hide-blocked-blogs', + + // Only applies to posts hidden by a blocked blog in the trail (see isTimelineExempt below) + controlsOnPermalinkPage: { + message: 'This post contains a blocked blog!', + buttonText: 'show post anyway', + }, +}); const processPosts = (postElements) => { filterPostElements(postElements, { includeFiltered: true }).forEach(async postElement => { @@ -32,7 +36,7 @@ const processPosts = (postElements) => { continue; } - getTimelineItemWrapper(postElement).setAttribute(hiddenAttribute, ''); + hidePost(postElement); break; } }); @@ -44,5 +48,5 @@ export const main = async function () { export const clean = async function () { onNewPosts.removeListener(processPosts); - $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); + showPosts(); }; diff --git a/src/features/tweaks/hide_filtered_posts.js b/src/features/tweaks/hide_filtered_posts.js index 2919488831..50ba894364 100644 --- a/src/features/tweaks/hide_filtered_posts.js +++ b/src/features/tweaks/hide_filtered_posts.js @@ -1,17 +1,19 @@ +import { createPostHideFunctions } from '../../main_world/hide_posts.js'; import { keyToCss } from '../../utils/css_map.js'; -import { buildStyle, getTimelineItemWrapper } from '../../utils/interface.js'; +import { getTimelineItemWrapper } from '../../utils/interface.js'; import { pageModifications } from '../../utils/mutations.js'; -const hiddenAttribute = 'data-tweaks-hide-filtered-posts-hidden'; -export const styleElement = buildStyle(` -[${hiddenAttribute}] { - content: linear-gradient(transparent, transparent); - height: 0; -}`); +const { hidePost, showPosts } = createPostHideFunctions({ + id: 'tweaks-hide-filtered-posts', + controlsOnPermalinkPage: { + message: 'This post contains filtered tags or content!', + buttonText: 'show post anyway', + }, +}); const hideFilteredPosts = filteredScreens => filteredScreens .map(getTimelineItemWrapper) - .forEach(timelineItem => timelineItem.setAttribute(hiddenAttribute, '')); + .forEach(hidePost); export const main = async function () { const filteredScreenSelector = `article ${keyToCss('filteredScreen')}`; @@ -20,6 +22,5 @@ export const main = async function () { export const clean = async function () { pageModifications.unregister(hideFilteredPosts); - - $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); + showPosts(); }; diff --git a/src/features/tweaks/hide_liked_posts.js b/src/features/tweaks/hide_liked_posts.js index 6b600e2fc3..4a3fb62c02 100644 --- a/src/features/tweaks/hide_liked_posts.js +++ b/src/features/tweaks/hide_liked_posts.js @@ -1,23 +1,19 @@ -import { buildStyle, getTimelineItemWrapper, filterPostElements } from '../../utils/interface.js'; +import { createPostHideFunctions } from '../../main_world/hide_posts.js'; +import { filterPostElements } from '../../utils/interface.js'; import { onNewPosts } from '../../utils/mutations.js'; import { isMyPost, timelineObject } from '../../utils/react_props.js'; import { followingTimelineFilter } from '../../utils/timeline_id.js'; const timeline = followingTimelineFilter; -const hiddenAttribute = 'data-tweaks-hide-liked-posts-hidden'; -export const styleElement = buildStyle(` -[${hiddenAttribute}] { - content: linear-gradient(transparent, transparent); - height: 0; -}`); +const { hidePost, showPosts } = createPostHideFunctions({ id: 'tweaks-hide-liked-posts' }); const processPosts = async function (postElements) { filterPostElements(postElements, { timeline }).forEach(async postElement => { const { liked } = await timelineObject(postElement); const myPost = await isMyPost(postElement); - if (liked && !myPost) getTimelineItemWrapper(postElement).setAttribute(hiddenAttribute, ''); + if (liked && !myPost) hidePost(postElement); }); }; @@ -27,6 +23,5 @@ export const main = async function () { export const clean = async function () { onNewPosts.removeListener(processPosts); - - $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); + showPosts(); }; diff --git a/src/features/tweaks/hide_my_posts.js b/src/features/tweaks/hide_my_posts.js index f930ee7791..2932ef9617 100644 --- a/src/features/tweaks/hide_my_posts.js +++ b/src/features/tweaks/hide_my_posts.js @@ -1,4 +1,5 @@ -import { buildStyle, getTimelineItemWrapper, filterPostElements } from '../../utils/interface.js'; +import { createPostHideFunctions } from '../../main_world/hide_posts.js'; +import { filterPostElements } from '../../utils/interface.js'; import { onNewPosts } from '../../utils/mutations.js'; import { isMyPost } from '../../utils/react_props.js'; import { followingTimelineFilter } from '../../utils/timeline_id.js'; @@ -6,19 +7,12 @@ import { followingTimelineFilter } from '../../utils/timeline_id.js'; const excludeClass = 'xkit-tweaks-hide-my-posts-done'; const timeline = followingTimelineFilter; -const hiddenAttribute = 'data-tweaks-hide-my-posts-hidden'; -export const styleElement = buildStyle(` -[${hiddenAttribute}] { - content: linear-gradient(transparent, transparent); - height: 0; -}`); +const { hidePost, showPosts } = createPostHideFunctions({ id: 'tweaks-hide-my-posts' }); const processPosts = async function (postElements) { filterPostElements(postElements, { excludeClass, timeline }).forEach(async postElement => { - const myPost = await isMyPost(postElement); - - if (myPost) { - getTimelineItemWrapper(postElement).setAttribute(hiddenAttribute, ''); + if (await isMyPost(postElement)) { + hidePost(postElement); } }); }; @@ -31,5 +25,5 @@ export const clean = async function () { onNewPosts.removeListener(processPosts); $(`.${excludeClass}`).removeClass(excludeClass); - $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); + showPosts(); }; diff --git a/src/main_world/hide_posts.js b/src/main_world/hide_posts.js new file mode 100644 index 0000000000..7b902a55fa --- /dev/null +++ b/src/main_world/hide_posts.js @@ -0,0 +1,77 @@ +import { br, button, div } from '../utils/dom.js'; +import { buildStyle, getTimelineItemWrapper } from '../utils/interface.js'; +import { anyPostPermalinkTimelineFilter, timelineSelector } from '../utils/timeline_id.js'; + +const controlsClass = 'xkit-hidden-post-controls'; + +const styleElement = buildStyle(` +.${controlsClass} { + padding: 25px 20px; + border-radius: 3px; + margin-bottom: var(--post-padding); + + background-color: var(--blog-title-color-15, rgba(var(--white-on-dark), 0.25)); + color: var(--blog-title-color, rgba(var(--white-on-dark))); + + font-weight: 700; + text-align: center; + line-height: 1.5em; +} + +.${controlsClass} button { + color: var(--blog-link-color, rgb(var(--deprecated-accent))); +} +`); +document.documentElement.append(styleElement); + +export const createPostHideFunctions = ({ id, controlsOnPermalinkPage, hideAutomatically = true }) => { + const hiddenAttribute = `data-xkit-${id}-hidden`; + + const controlledHiddenAttribute = `data-xkit-${id}-hidden-controlled`; + const controlsAttribute = `data-xkit-${id}-hidden-controls`; + + if (hideAutomatically) { + styleElement.textContent += ` + [${hiddenAttribute}], [${controlsAttribute}] ~ div [${controlledHiddenAttribute}] { + content: linear-gradient(transparent, transparent); + height: 0; + } + `; + } + + return { + hidePost: postElement => { + const timelineElement = postElement.closest(timelineSelector); + + if (anyPostPermalinkTimelineFilter(timelineElement)) { + if (controlsOnPermalinkPage) { + getTimelineItemWrapper(postElement).setAttribute(controlledHiddenAttribute, ''); + + if (timelineElement.querySelector(`[${controlsAttribute}]`) === null) { + const { message, buttonText } = controlsOnPermalinkPage; + const controlsElement = div({ class: controlsClass, [controlsAttribute]: id }, [ + message, + br(), + button({ click: () => controlsElement.remove() }, [buttonText]), + ]); + timelineElement.prepend(controlsElement); + } + } + return; + } + + getTimelineItemWrapper(postElement).setAttribute(hiddenAttribute, ''); + }, + showPost: postElement => { + getTimelineItemWrapper(postElement).removeAttribute(hiddenAttribute); + }, + showPosts: () => { + $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); + $(`[${controlledHiddenAttribute}]`).removeAttr(controlledHiddenAttribute); + $(`[${controlsAttribute}]`).remove(); + }, + hiddenAttribute, + controlledHiddenAttribute, + controlsAttribute, + }; +}; diff --git a/src/utils/timeline_id.js b/src/utils/timeline_id.js index 92d0f4d91b..26cbf64d98 100644 --- a/src/utils/timeline_id.js +++ b/src/utils/timeline_id.js @@ -3,9 +3,11 @@ const createSelector = (...components) => `:is(${components.filter(Boolean).join export const timelineSelector = ':is([data-timeline], [data-timeline-id])'; const startsWith = string => `^${string}`; +const endsWith = string => `${string}$`; const exactly = string => `^${string}$`; const anyBlogName = '[a-z0-9-]{1,32}'; +const anyPostId = '[0-9]{1,20}'; const uuidV4 = '[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}'; const peeprPostsTimelineId = ({ blogName, postId, postRole, searchMode, searchTerm, postType, tag }) => @@ -53,6 +55,13 @@ export const blogPostsTimelineFilter = blogName => // Matches any blog's main posts timeline, not including subpages such as drafts or in-blog searches. export const anyBlogPostsTimelineFilter = blogPostsTimelineFilter(anyBlogName); +export const postPermalinkTimelineFilter = postId => + ({ dataset: { timeline, timelineId } }) => + timeline?.match(endsWith(`posts/${postId}/permalink`)) || + timelineId?.match(exactly(peeprPostsTimelineId({ blogName: anyBlogName, postId }))); + +export const anyPostPermalinkTimelineFilter = postPermalinkTimelineFilter(anyPostId); + export const blogSubsTimelineFilter = ({ dataset: { timeline, which, timelineId } }) => timeline === '/v2/timeline?which=blog_subscriptions' || which === 'blog_subscriptions' || From a3d6730ae0238f9f60e011872a6278b1d14e079b Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Thu, 5 Mar 2026 10:46:46 -0800 Subject: [PATCH 02/16] rename variable --- src/features/postblock/index.js | 2 +- src/features/tweaks/hide_blocked_blogs.js | 2 +- src/features/tweaks/hide_filtered_posts.js | 2 +- src/main_world/hide_posts.js | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/features/postblock/index.js b/src/features/postblock/index.js index 4c875d71d9..b4b4bf1e49 100644 --- a/src/features/postblock/index.js +++ b/src/features/postblock/index.js @@ -14,7 +14,7 @@ let blockedPostRootIDs = []; const { hidePost, showPost, showPosts } = createPostHideFunctions({ id: 'postblock', - controlsOnPermalinkPage: { + permalinkPageControls: { message: 'You have hidden this post with PostBlock!', buttonText: 'show post anyway', }, diff --git a/src/features/tweaks/hide_blocked_blogs.js b/src/features/tweaks/hide_blocked_blogs.js index c508b99c87..ee9dc7d6c8 100644 --- a/src/features/tweaks/hide_blocked_blogs.js +++ b/src/features/tweaks/hide_blocked_blogs.js @@ -8,7 +8,7 @@ const { hidePost, showPosts } = createPostHideFunctions({ id: 'tweaks-hide-blocked-blogs', // Only applies to posts hidden by a blocked blog in the trail (see isTimelineExempt below) - controlsOnPermalinkPage: { + permalinkPageControls: { message: 'This post contains a blocked blog!', buttonText: 'show post anyway', }, diff --git a/src/features/tweaks/hide_filtered_posts.js b/src/features/tweaks/hide_filtered_posts.js index 50ba894364..6f53585f9d 100644 --- a/src/features/tweaks/hide_filtered_posts.js +++ b/src/features/tweaks/hide_filtered_posts.js @@ -5,7 +5,7 @@ import { pageModifications } from '../../utils/mutations.js'; const { hidePost, showPosts } = createPostHideFunctions({ id: 'tweaks-hide-filtered-posts', - controlsOnPermalinkPage: { + permalinkPageControls: { message: 'This post contains filtered tags or content!', buttonText: 'show post anyway', }, diff --git a/src/main_world/hide_posts.js b/src/main_world/hide_posts.js index 7b902a55fa..d0619a8c5d 100644 --- a/src/main_world/hide_posts.js +++ b/src/main_world/hide_posts.js @@ -24,7 +24,7 @@ const styleElement = buildStyle(` `); document.documentElement.append(styleElement); -export const createPostHideFunctions = ({ id, controlsOnPermalinkPage, hideAutomatically = true }) => { +export const createPostHideFunctions = ({ id, permalinkPageControls, hideAutomatically = true }) => { const hiddenAttribute = `data-xkit-${id}-hidden`; const controlledHiddenAttribute = `data-xkit-${id}-hidden-controlled`; @@ -44,11 +44,11 @@ export const createPostHideFunctions = ({ id, controlsOnPermalinkPage, hideAutom const timelineElement = postElement.closest(timelineSelector); if (anyPostPermalinkTimelineFilter(timelineElement)) { - if (controlsOnPermalinkPage) { + if (permalinkPageControls) { getTimelineItemWrapper(postElement).setAttribute(controlledHiddenAttribute, ''); if (timelineElement.querySelector(`[${controlsAttribute}]`) === null) { - const { message, buttonText } = controlsOnPermalinkPage; + const { message, buttonText } = permalinkPageControls; const controlsElement = div({ class: controlsClass, [controlsAttribute]: id }, [ message, br(), From 62dc615bb611982604253404f1492d175a7f7d6d Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Fri, 6 Mar 2026 08:11:02 -0800 Subject: [PATCH 03/16] small refactor --- src/main_world/hide_posts.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main_world/hide_posts.js b/src/main_world/hide_posts.js index d0619a8c5d..de1d4f8e23 100644 --- a/src/main_world/hide_posts.js +++ b/src/main_world/hide_posts.js @@ -39,6 +39,18 @@ export const createPostHideFunctions = ({ id, permalinkPageControls, hideAutomat `; } + const addPermalinkPageControls = timelineElement => { + if (timelineElement.querySelector(`[${controlsAttribute}]`) === null) { + const { message, buttonText } = permalinkPageControls; + const controlsElement = div({ class: controlsClass, [controlsAttribute]: id }, [ + message, + br(), + button({ click: () => controlsElement.remove() }, [buttonText]), + ]); + timelineElement.prepend(controlsElement); + } + }; + return { hidePost: postElement => { const timelineElement = postElement.closest(timelineSelector); @@ -46,16 +58,7 @@ export const createPostHideFunctions = ({ id, permalinkPageControls, hideAutomat if (anyPostPermalinkTimelineFilter(timelineElement)) { if (permalinkPageControls) { getTimelineItemWrapper(postElement).setAttribute(controlledHiddenAttribute, ''); - - if (timelineElement.querySelector(`[${controlsAttribute}]`) === null) { - const { message, buttonText } = permalinkPageControls; - const controlsElement = div({ class: controlsClass, [controlsAttribute]: id }, [ - message, - br(), - button({ click: () => controlsElement.remove() }, [buttonText]), - ]); - timelineElement.prepend(controlsElement); - } + addPermalinkPageControls(timelineElement); } return; } From 9e86afbd55c3e8a8a5a11f256a0e57e4157e28cf Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Wed, 6 May 2026 02:23:48 -0700 Subject: [PATCH 04/16] why did I put that file there. --- .../no_recommended/hide_recommended_community_posts.js | 2 +- src/features/postblock/index.js | 2 +- src/features/tweaks/hide_blocked_blogs.js | 2 +- src/features/tweaks/hide_filtered_posts.js | 2 +- src/features/tweaks/hide_liked_posts.js | 2 +- src/features/tweaks/hide_my_posts.js | 2 +- src/{main_world => utils}/hide_posts.js | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) rename src/{main_world => utils}/hide_posts.js (94%) diff --git a/src/features/no_recommended/hide_recommended_community_posts.js b/src/features/no_recommended/hide_recommended_community_posts.js index 8566d29e42..7ebe349cb8 100644 --- a/src/features/no_recommended/hide_recommended_community_posts.js +++ b/src/features/no_recommended/hide_recommended_community_posts.js @@ -1,4 +1,4 @@ -import { createPostHideFunctions } from '../../main_world/hide_posts.js'; +import { createPostHideFunctions } from '../../utils/hide_posts.js'; import { filterPostElements } from '../../utils/interface.js'; import { onNewPosts } from '../../utils/mutations.js'; import { timelineObject } from '../../utils/react_props.js'; diff --git a/src/features/postblock/index.js b/src/features/postblock/index.js index b4b4bf1e49..b871cf69e1 100644 --- a/src/features/postblock/index.js +++ b/src/features/postblock/index.js @@ -1,5 +1,5 @@ -import { createPostHideFunctions } from '../../main_world/hide_posts.js'; import { dom } from '../../utils/dom.js'; +import { createPostHideFunctions } from '../../utils/hide_posts.js'; import { filterPostElements } from '../../utils/interface.js'; import { registerMeatballItem, unregisterMeatballItem } from '../../utils/meatballs.js'; import { showModal, hideModal, modalCancelButton } from '../../utils/modals.js'; diff --git a/src/features/tweaks/hide_blocked_blogs.js b/src/features/tweaks/hide_blocked_blogs.js index ee9dc7d6c8..9e6404303b 100644 --- a/src/features/tweaks/hide_blocked_blogs.js +++ b/src/features/tweaks/hide_blocked_blogs.js @@ -1,4 +1,4 @@ -import { createPostHideFunctions } from '../../main_world/hide_posts.js'; +import { createPostHideFunctions } from '../../utils/hide_posts.js'; import { filterPostElements } from '../../utils/interface.js'; import { onNewPosts } from '../../utils/mutations.js'; import { isMyPost, timelineObject } from '../../utils/react_props.js'; diff --git a/src/features/tweaks/hide_filtered_posts.js b/src/features/tweaks/hide_filtered_posts.js index 6f53585f9d..29e806ed73 100644 --- a/src/features/tweaks/hide_filtered_posts.js +++ b/src/features/tweaks/hide_filtered_posts.js @@ -1,5 +1,5 @@ -import { createPostHideFunctions } from '../../main_world/hide_posts.js'; import { keyToCss } from '../../utils/css_map.js'; +import { createPostHideFunctions } from '../../utils/hide_posts.js'; import { getTimelineItemWrapper } from '../../utils/interface.js'; import { pageModifications } from '../../utils/mutations.js'; diff --git a/src/features/tweaks/hide_liked_posts.js b/src/features/tweaks/hide_liked_posts.js index 4a3fb62c02..4635025449 100644 --- a/src/features/tweaks/hide_liked_posts.js +++ b/src/features/tweaks/hide_liked_posts.js @@ -1,4 +1,4 @@ -import { createPostHideFunctions } from '../../main_world/hide_posts.js'; +import { createPostHideFunctions } from '../../utils/hide_posts.js'; import { filterPostElements } from '../../utils/interface.js'; import { onNewPosts } from '../../utils/mutations.js'; import { isMyPost, timelineObject } from '../../utils/react_props.js'; diff --git a/src/features/tweaks/hide_my_posts.js b/src/features/tweaks/hide_my_posts.js index 2932ef9617..7da34189ac 100644 --- a/src/features/tweaks/hide_my_posts.js +++ b/src/features/tweaks/hide_my_posts.js @@ -1,4 +1,4 @@ -import { createPostHideFunctions } from '../../main_world/hide_posts.js'; +import { createPostHideFunctions } from '../../utils/hide_posts.js'; import { filterPostElements } from '../../utils/interface.js'; import { onNewPosts } from '../../utils/mutations.js'; import { isMyPost } from '../../utils/react_props.js'; diff --git a/src/main_world/hide_posts.js b/src/utils/hide_posts.js similarity index 94% rename from src/main_world/hide_posts.js rename to src/utils/hide_posts.js index de1d4f8e23..66b3c7ad89 100644 --- a/src/main_world/hide_posts.js +++ b/src/utils/hide_posts.js @@ -1,6 +1,6 @@ -import { br, button, div } from '../utils/dom.js'; -import { buildStyle, getTimelineItemWrapper } from '../utils/interface.js'; -import { anyPostPermalinkTimelineFilter, timelineSelector } from '../utils/timeline_id.js'; +import { br, button, div } from './dom.js'; +import { buildStyle, getTimelineItemWrapper } from './interface.js'; +import { anyPostPermalinkTimelineFilter, timelineSelector } from './timeline_id.js'; const controlsClass = 'xkit-hidden-post-controls'; From 5e38dd7791d9247e5ad5b1ff52f8a536526620ee Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Sun, 17 May 2026 14:40:42 -0700 Subject: [PATCH 05/16] small refactors --- src/utils/hide_posts.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/utils/hide_posts.js b/src/utils/hide_posts.js index 66b3c7ad89..ee73951c04 100644 --- a/src/utils/hide_posts.js +++ b/src/utils/hide_posts.js @@ -54,16 +54,18 @@ export const createPostHideFunctions = ({ id, permalinkPageControls, hideAutomat return { hidePost: postElement => { const timelineElement = postElement.closest(timelineSelector); + const onPermalinkPage = anyPostPermalinkTimelineFilter(timelineElement); - if (anyPostPermalinkTimelineFilter(timelineElement)) { + if (onPermalinkPage) { if (permalinkPageControls) { getTimelineItemWrapper(postElement).setAttribute(controlledHiddenAttribute, ''); addPermalinkPageControls(timelineElement); + } else { + // do nothing; avoid hiding single post and making permalink page look broken } - return; + } else { + getTimelineItemWrapper(postElement).setAttribute(hiddenAttribute, ''); } - - getTimelineItemWrapper(postElement).setAttribute(hiddenAttribute, ''); }, showPost: postElement => { getTimelineItemWrapper(postElement).removeAttribute(hiddenAttribute); From f2703fd99270c4592b9fc29b2fe6907a86a4a5b7 Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Sun, 17 May 2026 15:11:30 -0700 Subject: [PATCH 06/16] add jsdoc --- src/utils/hide_posts.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/utils/hide_posts.js b/src/utils/hide_posts.js index ee73951c04..50db986625 100644 --- a/src/utils/hide_posts.js +++ b/src/utils/hide_posts.js @@ -24,13 +24,33 @@ const styleElement = buildStyle(` `); document.documentElement.append(styleElement); -export const createPostHideFunctions = ({ id, permalinkPageControls, hideAutomatically = true }) => { +/** + * @typedef {object} PostHideFunctions + * @property {(postElement: Element) => void} hidePost Hides a post. + * @property {(postElement: Element) => void} showPost Shows a post that was previously hidden. + * @property {() => void} showPosts Shows all posts hidden by this post hiding instance. + */ + +/** + * @typedef {object} PermalinkPageOptions + * @property {string} message Message to display in permalink page controls (e.g. "This post contains a blocked blog!") + * @property {string} buttonText Text for the button that permalink page controls dismiss button (e.g. "show post anyway") + */ + +/** + * @param {object} options Destructured + * @param {string} options.id Identifier for this post hiding instance (must be unique) + * @param {PermalinkPageOptions} [options.permalinkPageControls] If specified, single posts on permalink pages are hidden with an informative, dismissable UI + * @param {boolean} [options.hideManually] Disables CSS injection for manual control over hidden post styling + * @returns {PostHideFunctions} Functions to hide/show posts + */ +export const createPostHideFunctions = ({ id, permalinkPageControls, hideManually = false }) => { const hiddenAttribute = `data-xkit-${id}-hidden`; const controlledHiddenAttribute = `data-xkit-${id}-hidden-controlled`; const controlsAttribute = `data-xkit-${id}-hidden-controls`; - if (hideAutomatically) { + if (!hideManually) { styleElement.textContent += ` [${hiddenAttribute}], [${controlsAttribute}] ~ div [${controlledHiddenAttribute}] { content: linear-gradient(transparent, transparent); From dc2c787bc39696be185d653b19c55e874d3e4a2b Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Sun, 17 May 2026 15:17:27 -0700 Subject: [PATCH 07/16] improve comment --- src/features/tweaks/hide_blocked_blogs.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/tweaks/hide_blocked_blogs.js b/src/features/tweaks/hide_blocked_blogs.js index 9e6404303b..9c09eba908 100644 --- a/src/features/tweaks/hide_blocked_blogs.js +++ b/src/features/tweaks/hide_blocked_blogs.js @@ -7,7 +7,8 @@ import { blogTimelineFilter, timelineSelector } from '../../utils/timeline_id.js const { hidePost, showPosts } = createPostHideFunctions({ id: 'tweaks-hide-blocked-blogs', - // Only applies to posts hidden by a blocked blog in the trail (see isTimelineExempt below) + // Only applied to posts hidden by a blocked blog in the trail. + // Posts *authored by* blocked blogs aren't hidden with this util (see isTimelineExempt below). permalinkPageControls: { message: 'This post contains a blocked blog!', buttonText: 'show post anyway', From e9db566ace7f5a1a1b315725e3958a4b6f8e92ff Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Wed, 27 May 2026 15:24:01 -0700 Subject: [PATCH 08/16] pull in #719 postblock changes Co-Authored-By: April Sylph <28949509+AprilSylph@users.noreply.github.com> --- src/features/postblock/index.css | 56 -------------------- src/features/postblock/index.js | 38 +++++--------- src/features/tweaks/hide_blocked_blogs.js | 3 +- src/features/tweaks/hide_filtered_posts.js | 3 +- src/utils/hide_posts.js | 60 ++++++++++++++++++---- 5 files changed, 63 insertions(+), 97 deletions(-) delete mode 100644 src/features/postblock/index.css diff --git a/src/features/postblock/index.css b/src/features/postblock/index.css deleted file mode 100644 index 0eb6c48963..0000000000 --- a/src/features/postblock/index.css +++ /dev/null @@ -1,56 +0,0 @@ -[data-postblock-hidden], .xkit-postblock-hidden-post-controls ~ div [data-xkit-postblock-hidden-controlled] { - content: linear-gradient(transparent, transparent); - height: 0; -} - -.xkit-postblock-hidden-post-controls { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - padding: var(--space-m); - border-radius: 8px; - margin-bottom: var(--space-m); - - background-color: var(--content-panel); - background-image: linear-gradient(var(--content-tint), var(--content-tint)); - color: var(--content-fg-secondary); - - font-family: var(--font-family-modern); - font-size: 1rem; - font-weight: 350; - line-height: 1.5rem; -} - -.xkit-postblock-hidden-post-controls button { - flex-shrink: 0; - padding: 10px 16px; - border-radius: 9999px; - - font-family: var(--font-family-modern); - font-size: 1rem; - font-weight: 500; - line-height: 1.5rem; -} - -.xkit-postblock-hidden-post-controls button:hover { - background-color: var(--content-tint); - color: var(--content-fg); -} - -.xkit-postblock-hidden-post-controls button:active { - background-color: var(--content-tint-strong); -} - -.xkit-postblock-hidden-post-controls button:focus-visible { - outline: 2px solid var(--content-ui-focus); - outline-offset: 2px; -} - -/* Palettes for Tumblr font override compatibility */ -:root[style*="--font-family-modern"] .xkit-postblock-hidden-post-controls { - font-weight: normal; -} -:root[style*="--font-family-modern"] .xkit-postblock-hidden-post-controls button { - font-weight: bold; -} diff --git a/src/features/postblock/index.js b/src/features/postblock/index.js index 202ba3c7c9..e3c52d5042 100644 --- a/src/features/postblock/index.js +++ b/src/features/postblock/index.js @@ -1,10 +1,10 @@ import { dom } from '../../utils/dom.js'; -import { getTimelineItemWrapper, filterPostElements } from '../../utils/interface.js'; +import { createPostHideFunctions } from '../../utils/hide_posts.js'; +import { filterPostElements } from '../../utils/interface.js'; import { registerMeatballItem, unregisterMeatballItem } from '../../utils/meatballs.js'; import { showModal, hideModal, modalCancelButton } from '../../utils/modals.js'; import { onNewPosts, pageModifications } from '../../utils/mutations.js'; import { timelineObject } from '../../utils/react_props.js'; -import { postPermalinkTimelineFilter, timelineSelector } from '../../utils/timeline_id.js'; import { navigate } from '../../utils/tumblr_helpers.js'; const meatballButtonBlockId = 'postblock-block'; @@ -12,27 +12,20 @@ const meatballButtonBlockLabel = 'Block this post'; const meatballButtonUnblockId = 'postblock-unblock'; const meatballButtonUnblockLabel = 'Unblock this post'; -const hiddenAttribute = 'data-postblock-hidden'; -const controlsClass = 'xkit-postblock-hidden-post-controls'; -const controlledHiddenAttribute = 'data-xkit-postblock-hidden-controlled'; const storageKey = 'postblock.blockedPostRootIDs'; const blogUuidsStorageKey = 'postblock.blockedPostBlogUUIDs'; -// Remove outdated elements when loading module -$(`.${controlsClass}`).remove(); - let blogUuids = {}; -const addPermalinkPageControls = timelineElement => { - const controlsElement = dom('div', { class: controlsClass }, null, [ - 'This post is hidden by PostBlock.', - dom('button', null, { click: () => controlsElement.remove() }, 'View post'), - ]); - timelineElement.prepend(controlsElement); -}; - let blockedPostRootIDs = []; +const { hidePost, showPost, showPosts } = createPostHideFunctions({ + id: 'postblock', + permalinkPageControls: { + message: 'This post is hidden by PostBlock.', + }, +}); + const saveUuidPair = (postId, blogUuid) => { if (blockedPostRootIDs.includes(postId) && !blogUuids[postId]) { blogUuids[postId] = blogUuid; @@ -55,15 +48,9 @@ const processPosts = postElements => const rootID = rebloggedRootId || postID; if (blockedPostRootIDs.includes(rootID)) { - const timelineElement = postElement.closest(timelineSelector); - if (postPermalinkTimelineFilter(postID)(timelineElement)) { - getTimelineItemWrapper(postElement).setAttribute(controlledHiddenAttribute, ''); - addPermalinkPageControls(timelineElement); - } else { - getTimelineItemWrapper(postElement).setAttribute(hiddenAttribute, ''); - } + hidePost(postElement); } else { - getTimelineItemWrapper(postElement).removeAttribute(hiddenAttribute); + showPost(postElement); } saveUuidPair(id, uuid); @@ -144,8 +131,7 @@ export const clean = async function () { unregisterMeatballItem(meatballButtonUnblockId); onNewPosts.removeListener(processPosts); - $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); - $(`.${controlsClass}`).remove(); + showPosts(); }; export const stylesheet = true; diff --git a/src/features/tweaks/hide_blocked_blogs.js b/src/features/tweaks/hide_blocked_blogs.js index 9c09eba908..35e3d01f9b 100644 --- a/src/features/tweaks/hide_blocked_blogs.js +++ b/src/features/tweaks/hide_blocked_blogs.js @@ -10,8 +10,7 @@ const { hidePost, showPosts } = createPostHideFunctions({ // Only applied to posts hidden by a blocked blog in the trail. // Posts *authored by* blocked blogs aren't hidden with this util (see isTimelineExempt below). permalinkPageControls: { - message: 'This post contains a blocked blog!', - buttonText: 'show post anyway', + message: 'This post contains a blocked blog.', }, }); diff --git a/src/features/tweaks/hide_filtered_posts.js b/src/features/tweaks/hide_filtered_posts.js index 29e806ed73..d39730feb5 100644 --- a/src/features/tweaks/hide_filtered_posts.js +++ b/src/features/tweaks/hide_filtered_posts.js @@ -6,8 +6,7 @@ import { pageModifications } from '../../utils/mutations.js'; const { hidePost, showPosts } = createPostHideFunctions({ id: 'tweaks-hide-filtered-posts', permalinkPageControls: { - message: 'This post contains filtered tags or content!', - buttonText: 'show post anyway', + message: 'This post contains filtered tags or content.', }, }); diff --git a/src/utils/hide_posts.js b/src/utils/hide_posts.js index 50db986625..1f19e5b80c 100644 --- a/src/utils/hide_posts.js +++ b/src/utils/hide_posts.js @@ -4,22 +4,60 @@ import { anyPostPermalinkTimelineFilter, timelineSelector } from './timeline_id. const controlsClass = 'xkit-hidden-post-controls'; +// Remove outdated elements when loading module +$(`.${controlsClass}`).remove(); + const styleElement = buildStyle(` .${controlsClass} { - padding: 25px 20px; - border-radius: 3px; - margin-bottom: var(--post-padding); + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: var(--space-m); + border-radius: 8px; + margin-bottom: var(--space-m); - background-color: var(--blog-title-color-15, rgba(var(--white-on-dark), 0.25)); - color: var(--blog-title-color, rgba(var(--white-on-dark))); + background-color: var(--content-panel); + background-image: linear-gradient(var(--content-tint), var(--content-tint)); + color: var(--content-fg-secondary); - font-weight: 700; - text-align: center; - line-height: 1.5em; + font-family: var(--font-family-modern); + font-size: 1rem; + font-weight: 350; + line-height: 1.5rem; } .${controlsClass} button { - color: var(--blog-link-color, rgb(var(--deprecated-accent))); + flex-shrink: 0; + padding: 10px 16px; + border-radius: 9999px; + + font-family: var(--font-family-modern); + font-size: 1rem; + font-weight: 500; + line-height: 1.5rem; +} + +.${controlsClass} button:hover { + background-color: var(--content-tint); + color: var(--content-fg); +} + +.${controlsClass} button:active { + background-color: var(--content-tint-strong); +} + +.${controlsClass} button:focus-visible { + outline: 2px solid var(--content-ui-focus); + outline-offset: 2px; +} + +/* Palettes for Tumblr font override compatibility */ +:root[style*="--font-family-modern"] .${controlsClass} { + font-weight: normal; +} +:root[style*="--font-family-modern"] .${controlsClass} button { + font-weight: bold; } `); document.documentElement.append(styleElement); @@ -34,7 +72,7 @@ document.documentElement.append(styleElement); /** * @typedef {object} PermalinkPageOptions * @property {string} message Message to display in permalink page controls (e.g. "This post contains a blocked blog!") - * @property {string} buttonText Text for the button that permalink page controls dismiss button (e.g. "show post anyway") + * @property {string} [buttonText] Override text for the button that permalink page controls dismiss button (default: 'View post') */ /** @@ -61,7 +99,7 @@ export const createPostHideFunctions = ({ id, permalinkPageControls, hideManuall const addPermalinkPageControls = timelineElement => { if (timelineElement.querySelector(`[${controlsAttribute}]`) === null) { - const { message, buttonText } = permalinkPageControls; + const { message, buttonText = 'View post' } = permalinkPageControls; const controlsElement = div({ class: controlsClass, [controlsAttribute]: id }, [ message, br(), From fcc7657a4b8c4b1761ec02c2c8551f7f08104b57 Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Fri, 29 May 2026 21:15:32 -0700 Subject: [PATCH 09/16] remove now-unnecessary stylesheet reference --- src/features/postblock/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/features/postblock/index.js b/src/features/postblock/index.js index e3c52d5042..5d1b40e0cd 100644 --- a/src/features/postblock/index.js +++ b/src/features/postblock/index.js @@ -133,5 +133,3 @@ export const clean = async function () { showPosts(); }; - -export const stylesheet = true; From 42cd580af73afce380758db20e2c50ad3b1d4330 Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Fri, 29 May 2026 21:24:07 -0700 Subject: [PATCH 10/16] remove (for now at least) unused config options --- src/utils/hide_posts.js | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/utils/hide_posts.js b/src/utils/hide_posts.js index 1f19e5b80c..1cce8fe03c 100644 --- a/src/utils/hide_posts.js +++ b/src/utils/hide_posts.js @@ -72,38 +72,34 @@ document.documentElement.append(styleElement); /** * @typedef {object} PermalinkPageOptions * @property {string} message Message to display in permalink page controls (e.g. "This post contains a blocked blog!") - * @property {string} [buttonText] Override text for the button that permalink page controls dismiss button (default: 'View post') */ /** * @param {object} options Destructured * @param {string} options.id Identifier for this post hiding instance (must be unique) * @param {PermalinkPageOptions} [options.permalinkPageControls] If specified, single posts on permalink pages are hidden with an informative, dismissable UI - * @param {boolean} [options.hideManually] Disables CSS injection for manual control over hidden post styling * @returns {PostHideFunctions} Functions to hide/show posts */ -export const createPostHideFunctions = ({ id, permalinkPageControls, hideManually = false }) => { +export const createPostHideFunctions = ({ id, permalinkPageControls }) => { const hiddenAttribute = `data-xkit-${id}-hidden`; const controlledHiddenAttribute = `data-xkit-${id}-hidden-controlled`; const controlsAttribute = `data-xkit-${id}-hidden-controls`; - if (!hideManually) { - styleElement.textContent += ` - [${hiddenAttribute}], [${controlsAttribute}] ~ div [${controlledHiddenAttribute}] { - content: linear-gradient(transparent, transparent); - height: 0; - } - `; - } + styleElement.textContent += ` + [${hiddenAttribute}], [${controlsAttribute}] ~ div [${controlledHiddenAttribute}] { + content: linear-gradient(transparent, transparent); + height: 0; + } + `; const addPermalinkPageControls = timelineElement => { if (timelineElement.querySelector(`[${controlsAttribute}]`) === null) { - const { message, buttonText = 'View post' } = permalinkPageControls; + const { message } = permalinkPageControls; const controlsElement = div({ class: controlsClass, [controlsAttribute]: id }, [ message, br(), - button({ click: () => controlsElement.remove() }, [buttonText]), + button({ click: () => controlsElement.remove() }, ['View post']), ]); timelineElement.prepend(controlsElement); } @@ -133,8 +129,5 @@ export const createPostHideFunctions = ({ id, permalinkPageControls, hideManuall $(`[${controlledHiddenAttribute}]`).removeAttr(controlledHiddenAttribute); $(`[${controlsAttribute}]`).remove(); }, - hiddenAttribute, - controlledHiddenAttribute, - controlsAttribute, }; }; From a3633dece2b69ad9e5858062ad20c8715830ba30 Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Fri, 5 Jun 2026 02:55:13 -0700 Subject: [PATCH 11/16] fix showing posts in permalink view --- src/utils/hide_posts.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/hide_posts.js b/src/utils/hide_posts.js index 1cce8fe03c..0fcc99199c 100644 --- a/src/utils/hide_posts.js +++ b/src/utils/hide_posts.js @@ -123,6 +123,8 @@ export const createPostHideFunctions = ({ id, permalinkPageControls }) => { }, showPost: postElement => { getTimelineItemWrapper(postElement).removeAttribute(hiddenAttribute); + getTimelineItemWrapper(postElement).removeAttribute(controlledHiddenAttribute); + postElement.closest(timelineSelector).querySelector(`[${controlsAttribute}]`).remove(); }, showPosts: () => { $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); From 78c4300ba504f5a9fa74e1c638da9311c1d4c181 Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Fri, 5 Jun 2026 03:10:28 -0700 Subject: [PATCH 12/16] don't add controls to the same filtered post twice --- src/utils/hide_posts.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/utils/hide_posts.js b/src/utils/hide_posts.js index 0fcc99199c..2d44519e4b 100644 --- a/src/utils/hide_posts.js +++ b/src/utils/hide_posts.js @@ -93,8 +93,11 @@ export const createPostHideFunctions = ({ id, permalinkPageControls }) => { } `; - const addPermalinkPageControls = timelineElement => { - if (timelineElement.querySelector(`[${controlsAttribute}]`) === null) { + const addPermalinkPageControls = (postElement, timelineElement) => { + const timelineItemWrapper = getTimelineItemWrapper(postElement); + if (timelineItemWrapper.getAttribute(controlledHiddenAttribute) !== '') { + timelineItemWrapper.setAttribute(controlledHiddenAttribute, ''); + const { message } = permalinkPageControls; const controlsElement = div({ class: controlsClass, [controlsAttribute]: id }, [ message, @@ -112,8 +115,7 @@ export const createPostHideFunctions = ({ id, permalinkPageControls }) => { if (onPermalinkPage) { if (permalinkPageControls) { - getTimelineItemWrapper(postElement).setAttribute(controlledHiddenAttribute, ''); - addPermalinkPageControls(timelineElement); + addPermalinkPageControls(postElement, timelineElement); } else { // do nothing; avoid hiding single post and making permalink page look broken } From a2c92494bd9a5581af7362d002331482d4283a58 Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Fri, 5 Jun 2026 03:11:39 -0700 Subject: [PATCH 13/16] remember to put in optional chaining --- src/utils/hide_posts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/hide_posts.js b/src/utils/hide_posts.js index 2d44519e4b..7cee2af4bf 100644 --- a/src/utils/hide_posts.js +++ b/src/utils/hide_posts.js @@ -126,7 +126,7 @@ export const createPostHideFunctions = ({ id, permalinkPageControls }) => { showPost: postElement => { getTimelineItemWrapper(postElement).removeAttribute(hiddenAttribute); getTimelineItemWrapper(postElement).removeAttribute(controlledHiddenAttribute); - postElement.closest(timelineSelector).querySelector(`[${controlsAttribute}]`).remove(); + postElement.closest(timelineSelector)?.querySelector(`[${controlsAttribute}]`)?.remove(); }, showPosts: () => { $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); From 66398e6126d47e9f3334fbd89956d47f7699a9c6 Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Fri, 5 Jun 2026 03:46:34 -0700 Subject: [PATCH 14/16] fix extension restart behavior --- src/utils/hide_posts.js | 47 +++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/utils/hide_posts.js b/src/utils/hide_posts.js index 7cee2af4bf..1ff8a614fe 100644 --- a/src/utils/hide_posts.js +++ b/src/utils/hide_posts.js @@ -108,30 +108,31 @@ export const createPostHideFunctions = ({ id, permalinkPageControls }) => { } }; - return { - hidePost: postElement => { - const timelineElement = postElement.closest(timelineSelector); - const onPermalinkPage = anyPostPermalinkTimelineFilter(timelineElement); - - if (onPermalinkPage) { - if (permalinkPageControls) { - addPermalinkPageControls(postElement, timelineElement); - } else { - // do nothing; avoid hiding single post and making permalink page look broken - } + const hidePost = postElement => { + const timelineElement = postElement.closest(timelineSelector); + const onPermalinkPage = anyPostPermalinkTimelineFilter(timelineElement); + + if (onPermalinkPage) { + if (permalinkPageControls) { + addPermalinkPageControls(postElement, timelineElement); } else { - getTimelineItemWrapper(postElement).setAttribute(hiddenAttribute, ''); + // do nothing; avoid hiding single post and making permalink page look broken } - }, - showPost: postElement => { - getTimelineItemWrapper(postElement).removeAttribute(hiddenAttribute); - getTimelineItemWrapper(postElement).removeAttribute(controlledHiddenAttribute); - postElement.closest(timelineSelector)?.querySelector(`[${controlsAttribute}]`)?.remove(); - }, - showPosts: () => { - $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); - $(`[${controlledHiddenAttribute}]`).removeAttr(controlledHiddenAttribute); - $(`[${controlsAttribute}]`).remove(); - }, + } else { + getTimelineItemWrapper(postElement).setAttribute(hiddenAttribute, ''); + } + }; + const showPost = postElement => { + getTimelineItemWrapper(postElement).removeAttribute(hiddenAttribute); + getTimelineItemWrapper(postElement).removeAttribute(controlledHiddenAttribute); + postElement.closest(timelineSelector)?.querySelector(`[${controlsAttribute}]`)?.remove(); }; + const showPosts = () => { + $(`[${hiddenAttribute}]`).removeAttr(hiddenAttribute); + $(`[${controlledHiddenAttribute}]`).removeAttr(controlledHiddenAttribute); + $(`[${controlsAttribute}]`).remove(); + }; + showPosts(); + + return { hidePost, showPost, showPosts }; }; From 6d24df096fb6ca54c446c7b4c253bd19b1c9e552 Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Fri, 5 Jun 2026 03:50:24 -0700 Subject: [PATCH 15/16] only show one signpost at a time Co-Authored-By: April Sylph <28949509+AprilSylph@users.noreply.github.com> --- src/utils/hide_posts.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/utils/hide_posts.js b/src/utils/hide_posts.js index 1ff8a614fe..7fe175c31a 100644 --- a/src/utils/hide_posts.js +++ b/src/utils/hide_posts.js @@ -27,6 +27,10 @@ const styleElement = buildStyle(` line-height: 1.5rem; } +.${controlsClass} + .${controlsClass} { + display: none; +} + .${controlsClass} button { flex-shrink: 0; padding: 10px 16px; From 025884fe09f62cc2d7141ddf7f37d7328438edbd Mon Sep 17 00:00:00 2001 From: marcustyphoon Date: Fri, 5 Jun 2026 04:06:57 -0700 Subject: [PATCH 16/16] remove unused dom element --- src/utils/hide_posts.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/hide_posts.js b/src/utils/hide_posts.js index 7fe175c31a..e5ca971dd6 100644 --- a/src/utils/hide_posts.js +++ b/src/utils/hide_posts.js @@ -1,4 +1,4 @@ -import { br, button, div } from './dom.js'; +import { button, div } from './dom.js'; import { buildStyle, getTimelineItemWrapper } from './interface.js'; import { anyPostPermalinkTimelineFilter, timelineSelector } from './timeline_id.js'; @@ -105,7 +105,6 @@ export const createPostHideFunctions = ({ id, permalinkPageControls }) => { const { message } = permalinkPageControls; const controlsElement = div({ class: controlsClass, [controlsAttribute]: id }, [ message, - br(), button({ click: () => controlsElement.remove() }, ['View post']), ]); timelineElement.prepend(controlsElement);