From ee8677cc6d9a11cea2b3ba1ef668b8d09400be80 Mon Sep 17 00:00:00 2001 From: john Date: Fri, 11 Sep 2026 18:50:01 +0800 Subject: [PATCH] fix(learn): skip plaza share widget on learning assistant pages Wire delivery path detection through portal static routes and strip baked share chrome from learn parent/child HTML. Co-authored-by: Cursor --- mindspace-public-delivery.mjs | 34 ++++++++-- mindspace-public-delivery.test.mjs | 62 +++++++++++++++++++ mindspace-public-share-widget.mjs | 5 +- mindspace-public-share-widget.test.mjs | 13 +++- public/learn/child.html | 3 +- public/learn/parent.html | 3 +- .../build-tang-learning-assistant-pages.mjs | 3 +- server/portal-published-page-delivery.mjs | 11 +++- server/portal-static-delivery-routes.mjs | 33 ++++++++-- .../portal-workspace-publication-delivery.mjs | 3 + 10 files changed, 154 insertions(+), 16 deletions(-) diff --git a/mindspace-public-delivery.mjs b/mindspace-public-delivery.mjs index 1939d37..1e0d806 100644 --- a/mindspace-public-delivery.mjs +++ b/mindspace-public-delivery.mjs @@ -9,6 +9,7 @@ import { stripPublicationHtmlCspMeta } from './plaza-embed.mjs'; import { decorateMindSpaceSeoGeoHtml, } from './mindspace-seo-geo-delivery.mjs'; +import { stripPublicFileShareWidget } from './mindspace-public-share-widget.mjs'; const INLINE_SCRIPT_PATTERN = /]*\bsrc\b)[^>]*>([\s\S]*?)<\/script>/gi; @@ -24,6 +25,22 @@ export function collectInlineScriptHashes(html) { return hashes; } +export function shouldSkipPublicShareWidget({ + htmlFilePath = '', + pageUrl = '', + relativePath = '', + requestPath = '', +} = {}) { + const haystack = `${htmlFilePath}\n${pageUrl}\n${relativePath}\n${requestPath}` + .replace(/\\/g, '/') + .toLowerCase(); + return ( + haystack.includes('learning-assistant') || + haystack.includes('/public/learn/') || + /\/learn(?:\/|$|[?#])/.test(haystack) + ); +} + export async function handleMindSpaceLongImageDownload({ query, filePath, @@ -59,6 +76,8 @@ export function decorateMindSpacePublishedHtml({ pageDataContext = null, context, htmlFilePath = '', + relativePath = '', + requestPath = '', fileExists = fs.existsSync, userAgent = '', preparePublicationHtmlForEmbed, @@ -97,10 +116,17 @@ export function decorateMindSpacePublishedHtml({ if (wechatShare) { nextHtml = injectWechatShareBridge(nextHtml, { pageUrl: context.pageUrl }); } - const shareInjection = !embed - ? injectPublicFileShareButton(nextHtml, { isOwner }) - : { html: nextHtml, scriptHashes: [] }; - nextHtml = shareInjection.html; + const skipShareWidget = !embed && shouldSkipPublicShareWidget({ + htmlFilePath, + pageUrl: context?.pageUrl ?? '', + relativePath, + requestPath, + }); + if (skipShareWidget) { + nextHtml = stripPublicFileShareWidget(nextHtml); + } else if (!embed) { + nextHtml = injectPublicFileShareButton(nextHtml, { isOwner }).html; + } // Retries transient asset-download failures client-side before a page's own onerror // permanently swaps the for a placeholder. See mindspace-public-image-retry.mjs. if (!embed) { diff --git a/mindspace-public-delivery.test.mjs b/mindspace-public-delivery.test.mjs index 74f17eb..460dfb2 100644 --- a/mindspace-public-delivery.test.mjs +++ b/mindspace-public-delivery.test.mjs @@ -4,6 +4,7 @@ import { collectInlineScriptHashes, decorateMindSpacePublishedHtml, handleMindSpaceLongImageDownload, + shouldSkipPublicShareWidget, } from './mindspace-public-delivery.mjs'; test('collectInlineScriptHashes returns sha256 hashes for inline scripts only', () => { @@ -173,6 +174,67 @@ test('decorateMindSpacePublishedHtml forwards isOwner=false to the share button assert.equal(sharedIsOwner, false); }); +test('shouldSkipPublicShareWidget matches learning assistant delivery paths', () => { + assert.equal( + shouldSkipPublicShareWidget({ + pageUrl: 'https://m.tkmind.cn/MindSpace/u/public/learning-assistant.html', + }), + true, + ); + assert.equal( + shouldSkipPublicShareWidget({ + htmlFilePath: '/Users/john/Project/Memind/public/learn/child.html', + }), + true, + ); + assert.equal( + shouldSkipPublicShareWidget({ + relativePath: 'public/learning-assistant.html', + }), + true, + ); + assert.equal( + shouldSkipPublicShareWidget({ + requestPath: '/MindSpace/u/public/learning-assistant-parent.html', + }), + true, + ); + assert.equal( + shouldSkipPublicShareWidget({ + pageUrl: 'https://m.tkmind.cn/MindSpace/u/public/report.html', + }), + false, + ); +}); + +test('decorateMindSpacePublishedHtml skips plaza share widget on learning assistant pages', () => { + let shareInjected = false; + const baked = `

Learn

`; + const result = decorateMindSpacePublishedHtml({ + html: baked, + embed: false, + htmlFilePath: '/tmp/MindSpace/u/public/learning-assistant.html', + context: { + origin: 'https://m.tkmind.cn', + pageUrl: 'https://m.tkmind.cn/MindSpace/u/public/learning-assistant.html', + pageDirUrl: 'https://m.tkmind.cn/MindSpace/u/public/', + fallbackImageUrl: '', + }, + preparePublicationHtmlForEmbed: (value) => value, + injectOgTags: (value) => value, + injectWechatShareBridge: (value) => value, + injectPublicFileShareButton: (value) => { + shareInjected = true; + return { html: `${value}`, scriptHashes: [] }; + }, + publishedPageCsp: (value) => value, + isWechatUserAgent: () => false, + }); + + assert.equal(shareInjected, false); + assert.doesNotMatch(result.html, /mindspace-public-share/); +}); + test('decorateMindSpacePublishedHtml supports embed mode without share injection', () => { const result = decorateMindSpacePublishedHtml({ html: '', diff --git a/mindspace-public-share-widget.mjs b/mindspace-public-share-widget.mjs index cb0cf9a..cfff1ea 100644 --- a/mindspace-public-share-widget.mjs +++ b/mindspace-public-share-widget.mjs @@ -202,7 +202,10 @@ const PUBLIC_FILE_SHARE_WIDGET_BLOCK_PATTERN = / +.empty{text-align:center;color:#9ca3af;font-size:13px;padding:18px 0;line-height:1.7} +[data-mindspace-public-share],[data-mindspace-wechat-dialog],.publication-share-fab,.publication-share-sheet{display:none!important;visibility:hidden!important}
v2