copy-llms-txt
Version:
A lightweight JavaScript library that lets you copy the content of a webpage as clean markdown with a simple keyboard shortcut.
62 lines (37 loc) • 13.6 kB
JavaScript
(function(f,c){typeof exports=="object"&&typeof module<"u"?module.exports=c():typeof define=="function"&&define.amd?define(c):(f=typeof globalThis<"u"?globalThis:f||self,f.CopyLlmsTxt=c())})(this,function(){"use strict";var ie=Object.defineProperty;var ae=(f,c,m)=>c in f?ie(f,c,{enumerable:!0,configurable:!0,writable:!0,value:m}):f[c]=m;var p=(f,c,m)=>ae(f,typeof c!="symbol"?c+"":c,m);function f(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)n.hasOwnProperty(r)&&(e[r]=n[r])}return e}function c(e,t){return Array(t+1).join(e)}function m(e){return e.replace(/^\n*/,"")}function x(e){for(var t=e.length;t>0&&e[t-1]===`
`;)t--;return e.substring(0,t)}var P=["ADDRESS","ARTICLE","ASIDE","AUDIO","BLOCKQUOTE","BODY","CANVAS","CENTER","DD","DIR","DIV","DL","DT","FIELDSET","FIGCAPTION","FIGURE","FOOTER","FORM","FRAMESET","H1","H2","H3","H4","H5","H6","HEADER","HGROUP","HR","HTML","ISINDEX","LI","MAIN","MENU","NAV","NOFRAMES","NOSCRIPT","OL","OUTPUT","P","PRE","SECTION","TABLE","TBODY","TD","TFOOT","TH","THEAD","TR","UL"];function y(e){return E(e,P)}var A=["AREA","BASE","BR","COL","COMMAND","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"];function k(e){return E(e,A)}function M(e){return R(e,A)}var b=["A","TABLE","THEAD","TBODY","TFOOT","TH","TD","IFRAME","SCRIPT","AUDIO","VIDEO"];function I(e){return E(e,b)}function H(e){return R(e,b)}function E(e,t){return t.indexOf(e.nodeName)>=0}function R(e,t){return e.getElementsByTagName&&t.some(function(n){return e.getElementsByTagName(n).length})}var s={};s.paragraph={filter:"p",replacement:function(e){return`
`+e+`
`}},s.lineBreak={filter:"br",replacement:function(e,t,n){return n.br+`
`}},s.heading={filter:["h1","h2","h3","h4","h5","h6"],replacement:function(e,t,n){var r=Number(t.nodeName.charAt(1));if(n.headingStyle==="setext"&&r<3){var i=c(r===1?"=":"-",e.length);return`
`+e+`
`+i+`
`}else return`
`+c("#",r)+" "+e+`
`}},s.blockquote={filter:"blockquote",replacement:function(e){return e=e.replace(/^\n+|\n+$/g,""),e=e.replace(/^/gm,"> "),`
`+e+`
`}},s.list={filter:["ul","ol"],replacement:function(e,t){var n=t.parentNode;return n.nodeName==="LI"&&n.lastElementChild===t?`
`+e:`
`+e+`
`}},s.listItem={filter:"li",replacement:function(e,t,n){e=e.replace(/^\n+/,"").replace(/\n+$/,`
`).replace(/\n/gm,`
`);var r=n.bulletListMarker+" ",i=t.parentNode;if(i.nodeName==="OL"){var a=i.getAttribute("start"),o=Array.prototype.indexOf.call(i.children,t);r=(a?Number(a)+o:o+1)+". "}return r+e+(t.nextSibling&&!/\n$/.test(e)?`
`:"")}},s.indentedCodeBlock={filter:function(e,t){return t.codeBlockStyle==="indented"&&e.nodeName==="PRE"&&e.firstChild&&e.firstChild.nodeName==="CODE"},replacement:function(e,t,n){return`
`+t.firstChild.textContent.replace(/\n/g,`
`)+`
`}},s.fencedCodeBlock={filter:function(e,t){return t.codeBlockStyle==="fenced"&&e.nodeName==="PRE"&&e.firstChild&&e.firstChild.nodeName==="CODE"},replacement:function(e,t,n){for(var r=t.firstChild.getAttribute("class")||"",i=(r.match(/language-(\S+)/)||[null,""])[1],a=t.firstChild.textContent,o=n.fence.charAt(0),u=3,l=new RegExp("^"+o+"{3,}","gm"),d;d=l.exec(a);)d[0].length>=u&&(u=d[0].length+1);var v=c(o,u);return`
`+v+i+`
`+a.replace(/\n$/,"")+`
`+v+`
`}},s.horizontalRule={filter:"hr",replacement:function(e,t,n){return`
`+n.hr+`
`}},s.inlineLink={filter:function(e,t){return t.linkStyle==="inlined"&&e.nodeName==="A"&&e.getAttribute("href")},replacement:function(e,t){var n=t.getAttribute("href");n&&(n=n.replace(/([()])/g,"\\$1"));var r=g(t.getAttribute("title"));return r&&(r=' "'+r.replace(/"/g,'\\"')+'"'),"["+e+"]("+n+r+")"}},s.referenceLink={filter:function(e,t){return t.linkStyle==="referenced"&&e.nodeName==="A"&&e.getAttribute("href")},replacement:function(e,t,n){var r=t.getAttribute("href"),i=g(t.getAttribute("title"));i&&(i=' "'+i+'"');var a,o;switch(n.linkReferenceStyle){case"collapsed":a="["+e+"][]",o="["+e+"]: "+r+i;break;case"shortcut":a="["+e+"]",o="["+e+"]: "+r+i;break;default:var u=this.references.length+1;a="["+e+"]["+u+"]",o="["+u+"]: "+r+i}return this.references.push(o),a},references:[],append:function(e){var t="";return this.references.length&&(t=`
`+this.references.join(`
`)+`
`,this.references=[]),t}},s.emphasis={filter:["em","i"],replacement:function(e,t,n){return e.trim()?n.emDelimiter+e+n.emDelimiter:""}},s.strong={filter:["strong","b"],replacement:function(e,t,n){return e.trim()?n.strongDelimiter+e+n.strongDelimiter:""}},s.code={filter:function(e){var t=e.previousSibling||e.nextSibling,n=e.parentNode.nodeName==="PRE"&&!t;return e.nodeName==="CODE"&&!n},replacement:function(e){if(!e)return"";e=e.replace(/\r?\n|\r/g," ");for(var t=/^`|^ .*?[^ ].* $|`$/.test(e)?" ":"",n="`",r=e.match(/`+/gm)||[];r.indexOf(n)!==-1;)n=n+"`";return n+t+e+t+n}},s.image={filter:"img",replacement:function(e,t){var n=g(t.getAttribute("alt")),r=t.getAttribute("src")||"",i=g(t.getAttribute("title")),a=i?' "'+i+'"':"";return r?"":""}};function g(e){return e?e.replace(/(\n+\s*)+/g,`
`):""}function S(e){this.options=e,this._keep=[],this._remove=[],this.blankRule={replacement:e.blankReplacement},this.keepReplacement=e.keepReplacement,this.defaultRule={replacement:e.defaultReplacement},this.array=[];for(var t in e.rules)this.array.push(e.rules[t])}S.prototype={add:function(e,t){this.array.unshift(t)},keep:function(e){this._keep.unshift({filter:e,replacement:this.keepReplacement})},remove:function(e){this._remove.unshift({filter:e,replacement:function(){return""}})},forNode:function(e){if(e.isBlank)return this.blankRule;var t;return(t=C(this.array,e,this.options))||(t=C(this._keep,e,this.options))||(t=C(this._remove,e,this.options))?t:this.defaultRule},forEach:function(e){for(var t=0;t<this.array.length;t++)e(this.array[t],t)}};function C(e,t,n){for(var r=0;r<e.length;r++){var i=e[r];if($(i,t,n))return i}}function $(e,t,n){var r=e.filter;if(typeof r=="string"){if(r===t.nodeName.toLowerCase())return!0}else if(Array.isArray(r)){if(r.indexOf(t.nodeName.toLowerCase())>-1)return!0}else if(typeof r=="function"){if(r.call(e,t,n))return!0}else throw new TypeError("`filter` needs to be a string, array, or function")}function F(e){var t=e.element,n=e.isBlock,r=e.isVoid,i=e.isPre||function(re){return re.nodeName==="PRE"};if(!(!t.firstChild||i(t))){for(var a=null,o=!1,u=null,l=O(u,t,i);l!==t;){if(l.nodeType===3||l.nodeType===4){var d=l.data.replace(/[ \r\n\t]+/g," ");if((!a||/ $/.test(a.data))&&!o&&d[0]===" "&&(d=d.substr(1)),!d){l=T(l);continue}l.data=d,a=l}else if(l.nodeType===1)n(l)||l.nodeName==="BR"?(a&&(a.data=a.data.replace(/ $/,"")),a=null,o=!1):r(l)||i(l)?(a=null,o=!0):a&&(o=!1);else{l=T(l);continue}var v=O(u,l,i);u=l,l=v}a&&(a.data=a.data.replace(/ $/,""),a.data||T(a))}}function T(e){var t=e.nextSibling||e.parentNode;return e.parentNode.removeChild(e),t}function O(e,t,n){return e&&e.parentNode===t||n(t)?t.nextSibling||t.parentNode:t.firstChild||t.nextSibling||t.parentNode}var w=typeof window<"u"?window:{};function V(){var e=w.DOMParser,t=!1;try{new e().parseFromString("","text/html")&&(t=!0)}catch{}return t}function U(){var e=function(){};return W()?e.prototype.parseFromString=function(t){var n=new window.ActiveXObject("htmlfile");return n.designMode="on",n.open(),n.write(t),n.close(),n}:e.prototype.parseFromString=function(t){var n=document.implementation.createHTMLDocument("");return n.open(),n.write(t),n.close(),n},e}function W(){var e=!1;try{document.implementation.createHTMLDocument("").open()}catch{w.ActiveXObject&&(e=!0)}return e}var z=V()?w.DOMParser:U();function _(e,t){var n;if(typeof e=="string"){var r=K().parseFromString('<x-turndown id="turndown-root">'+e+"</x-turndown>","text/html");n=r.getElementById("turndown-root")}else n=e.cloneNode(!0);return F({element:n,isBlock:y,isVoid:k,isPre:t.preformattedCode?j:null}),n}var N;function K(){return N=N||new z,N}function j(e){return e.nodeName==="PRE"||e.nodeName==="CODE"}function q(e,t){return e.isBlock=y(e),e.isCode=e.nodeName==="CODE"||e.parentNode.isCode,e.isBlank=G(e),e.flankingWhitespace=X(e,t),e}function G(e){return!k(e)&&!I(e)&&/^\s*$/i.test(e.textContent)&&!M(e)&&!H(e)}function X(e,t){if(e.isBlock||t.preformattedCode&&e.isCode)return{leading:"",trailing:""};var n=Y(e.textContent);return n.leadingAscii&&D("left",e,t)&&(n.leading=n.leadingNonAscii),n.trailingAscii&&D("right",e,t)&&(n.trailing=n.trailingNonAscii),{leading:n.leading,trailing:n.trailing}}function Y(e){var t=e.match(/^(([ \t\r\n]*)(\s*))(?:(?=\S)[\s\S]*\S)?((\s*?)([ \t\r\n]*))$/);return{leading:t[1],leadingAscii:t[2],leadingNonAscii:t[3],trailing:t[4],trailingNonAscii:t[5],trailingAscii:t[6]}}function D(e,t,n){var r,i,a;return e==="left"?(r=t.previousSibling,i=/ $/):(r=t.nextSibling,i=/^ /),r&&(r.nodeType===3?a=i.test(r.nodeValue):n.preformattedCode&&r.nodeName==="CODE"?a=!1:r.nodeType===1&&!y(r)&&(a=i.test(r.textContent))),a}var Q=Array.prototype.reduce,J=[[/\\/g,"\\\\"],[/\*/g,"\\*"],[/^-/g,"\\-"],[/^\+ /g,"\\+ "],[/^(=+)/g,"\\$1"],[/^(#{1,6}) /g,"\\$1 "],[/`/g,"\\`"],[/^~~~/g,"\\~~~"],[/\[/g,"\\["],[/\]/g,"\\]"],[/^>/g,"\\>"],[/_/g,"\\_"],[/^(\d+)\. /g,"$1\\. "]];function h(e){if(!(this instanceof h))return new h(e);var t={rules:s,headingStyle:"setext",hr:"* * *",bulletListMarker:"*",codeBlockStyle:"indented",fence:"```",emDelimiter:"_",strongDelimiter:"**",linkStyle:"inlined",linkReferenceStyle:"full",br:" ",preformattedCode:!1,blankReplacement:function(n,r){return r.isBlock?`
`:""},keepReplacement:function(n,r){return r.isBlock?`
`+r.outerHTML+`
`:r.outerHTML},defaultReplacement:function(n,r){return r.isBlock?`
`+n+`
`:n}};this.options=f({},t,e),this.rules=new S(this.options)}h.prototype={turndown:function(e){if(!te(e))throw new TypeError(e+" is not a string, or an element/document/fragment node.");if(e==="")return"";var t=L.call(this,new _(e,this.options));return Z.call(this,t)},use:function(e){if(Array.isArray(e))for(var t=0;t<e.length;t++)this.use(e[t]);else if(typeof e=="function")e(this);else throw new TypeError("plugin must be a Function or an Array of Functions");return this},addRule:function(e,t){return this.rules.add(e,t),this},keep:function(e){return this.rules.keep(e),this},remove:function(e){return this.rules.remove(e),this},escape:function(e){return J.reduce(function(t,n){return t.replace(n[0],n[1])},e)}};function L(e){var t=this;return Q.call(e.childNodes,function(n,r){r=new q(r,t.options);var i="";return r.nodeType===3?i=r.isCode?r.nodeValue:t.escape(r.nodeValue):r.nodeType===1&&(i=ee.call(t,r)),B(n,i)},"")}function Z(e){var t=this;return this.rules.forEach(function(n){typeof n.append=="function"&&(e=B(e,n.append(t.options)))}),e.replace(/^[\t\r\n]+/,"").replace(/[\t\r\n\s]+$/,"")}function ee(e){var t=this.rules.forNode(e),n=L.call(this,e),r=e.flankingWhitespace;return(r.leading||r.trailing)&&(n=n.trim()),r.leading+t.replacement(n,e,this.options)+r.trailing}function B(e,t){var n=x(e),r=m(t),i=Math.max(e.length-n.length,t.length-r.length),a=`
`.substring(0,i);return n+a+r}function te(e){return e!=null&&(typeof e=="string"||e.nodeType&&(e.nodeType===1||e.nodeType===9||e.nodeType===11))}class ne{constructor(){p(this,"options",{key:"c",meta:!1,turndownOptions:{},selectorsToRemove:["nav","header","footer","aside",".navigation",".nav",".menu",".sidebar",".ad",".ads",".advertisement","script","style","noscript","iframe"]});p(this,"turndownService");p(this,"initialized",!1);p(this,"handleKeyDown",t=>{(this.options.meta?t.metaKey:t.ctrlKey)&&t.key.toLowerCase()===this.options.key.toLowerCase()&&(t.preventDefault(),this.copyPageContent())});this.turndownService=new h}isMac(){return navigator.platform.toLowerCase().includes("mac")||navigator.userAgent.toLowerCase().includes("mac")}init(t){if(this.initialized){console.warn("CopyLlmsTxt is already initialized. Call destroy() before reinitializing.");return}const n={key:"c",meta:this.isMac(),turndownOptions:{},selectorsToRemove:["nav","header","footer","aside",".navigation",".nav",".menu",".sidebar",".ad",".ads",".advertisement","script","style","noscript","iframe"]};this.options={...n,...t},this.turndownService=new h(this.options.turndownOptions),document.addEventListener("keydown",this.handleKeyDown),this.initialized=!0,console.log(`CopyLlmsTxt initialized. Press ${this.options.meta?"Meta/Cmd":"Ctrl"}+${this.options.key} to copy content as markdown.`)}destroy(){this.initialized&&(document.removeEventListener("keydown",this.handleKeyDown),this.initialized=!1,console.log("CopyLlmsTxt destroyed."))}copyPageContent(){try{const t=document.body.cloneNode(!0);this.cleanupDOM(t);const n=this.turndownService.turndown(t);navigator.clipboard.writeText(n).then(()=>{console.log("Page content copied as markdown!")}).catch(r=>{console.error("Failed to copy text: ",r)})}catch(t){console.error("Error copying page content:",t)}}removeNonVisibleElements(t){const n=document.body.querySelectorAll("*"),r=t.querySelectorAll("*");if(n.length!==r.length)console.warn("Element count mismatch between original and clone");else for(let i=r.length-1;i>=0;i--){const a=n[i],o=r[i],u=window.getComputedStyle(a),l=a.getBoundingClientRect();(u.display==="none"||u.visibility==="hidden"||u.opacity==="0"||l.height===0||l.width===0)&&o.parentNode&&o.parentNode.removeChild(o)}}removeElementsBySelector(t){["nav","header","footer","aside",".navigation",".nav",".menu",".sidebar",".ad",".ads",".advertisement","script","style","noscript","iframe"].forEach(r=>{t.querySelectorAll(r).forEach(a=>{var o;return(o=a.parentNode)==null?void 0:o.removeChild(a)})})}cleanupDOM(t){this.removeNonVisibleElements(t),this.removeElementsBySelector(t)}copy(){this.copyPageContent()}}return new ne});