Created
September 21, 2026 04:09
-
-
Save TypeA2/727b279cee0a03b680226df0b4643e93 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name XHS - Copy DText | |
| // @author hdk5, TypeA2 | |
| // @version 20260921-1 | |
| // @namespace https://gist.github.com/TypeA2/ | |
| // @updateURL https://gist.github.com/TypeA2/727b279cee0a03b680226df0b4643e93/raw/xhs-copy-dtext.user.js | |
| // @downloadURL https://gist.github.com/TypeA2/727b279cee0a03b680226df0b4643e93/raw/xhs-copy-dtext.user.js | |
| // @match https://www.xiaohongshu.com/* | |
| // @match https://www.rednote.com/* | |
| // @grant none | |
| // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js | |
| // ==/UserScript== | |
| /* globals $ */ | |
| function reprNode(node) { | |
| const tag = node.tagName.toLowerCase(); | |
| const id = node.id ? `#${node.id}` : ''; | |
| const cls = node.className.split(' ').map(c => `.${c}`).join(''); | |
| return `${tag}${id}${cls}`; | |
| } | |
| function convertHtml(el) { | |
| const errors = []; | |
| const text = $(el).contents().filter(function () { | |
| return this.nodeType === Node.ELEMENT_NODE; | |
| }).map(function () { | |
| const $node = $(this); | |
| const redmojiMap = window.__INITIAL_STATE__?.redMoji?.mojiData?._rawValue?.redmojiMap || {}; | |
| const redmojiMapInvert = Object.fromEntries( | |
| Object.entries(redmojiMap).map(([key, value]) => [value, key]), | |
| ); | |
| if ($node.hasClass('tag')) { | |
| // TPT fucks up the layout a bit - can't use just .text() | |
| const tag = $node.contents().filter(function () { | |
| return this.nodeType === Node.TEXT_NODE; | |
| }).text(); | |
| return `"${tag}":[https://www.xiaohongshu.com/search_result?keyword=${encodeURIComponent(tag.slice(1))}]`; | |
| } | |
| if ($node.hasClass('note-content-user')) { | |
| const user = $node.text().trim(); | |
| const userId = $node.attr('data-user-id'); | |
| return `"${user}":[https://www.xiaohongshu.com/user/profile/${userId}]`; | |
| } | |
| if ($node.hasClass('note-content-emoji')) { | |
| const emojiUrl = $node.attr('src'); | |
| const emojiTag = redmojiMapInvert[emojiUrl] || '[emoji]'; | |
| return `"${emojiTag}":[${emojiUrl}]`; | |
| } | |
| if ( | |
| this.tagName !== 'SPAN' | |
| || this.id !== '' | |
| || this.className !== '' | |
| ) { | |
| errors.push(`Unsupported element: ${reprNode(this)}`); | |
| console.warn('Unsupported element:', this); | |
| } | |
| return $node.text(); | |
| }).get().join(''); | |
| return { text, errors }; | |
| } | |
| function addButtons() { | |
| $('.note-text:not(:has(+ .ex-copy-dtext))').each(function () { | |
| const note = this; | |
| const $note = $(note); | |
| const $btn = $('<a>', { | |
| class: 'ex-copy-dtext', | |
| text: 'Copy DText', | |
| css: { | |
| display: 'block', | |
| cursor: 'pointer', | |
| }, | |
| }); | |
| $btn.on('click', (ev) => { | |
| ev.preventDefault(); | |
| const { text, errors } = convertHtml(note); | |
| if (errors.length) { | |
| alert(errors.join("\n")); | |
| } | |
| navigator.clipboard.writeText(text); | |
| $btn.text("Copied!") | |
| }); | |
| $note.after($btn); | |
| }); | |
| } | |
| new MutationObserver(addButtons).observe(document.body, { childList: true, subtree: true }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment