{"id":25996,"date":"2026-06-13T03:05:51","date_gmt":"2026-06-13T01:05:51","guid":{"rendered":"https:\/\/www.wjst.de\/blog\/?p=25996"},"modified":"2026-06-13T03:14:50","modified_gmt":"2026-06-13T01:14:50","slug":"a-bookmarklet-that-highlights-ai-generated-prose","status":"publish","type":"post","link":"https:\/\/www.wjst.de\/blog\/sciencesurf\/2026\/06\/a-bookmarklet-that-highlights-ai-generated-prose\/","title":{"rendered":"A bookmarklet that highlights AI generated prose"},"content":{"rendered":"<p>As AI models are trained on vast amounts of text, including published, polished prose that uses professional typesetting, which is why they default to these more precise characters although they cannot be simply reached by a keyboard. Some examples are<\/p>\n<p>\u2014 Em Dash U+2014 Two hyphens (&#8211;) or special key commands Used to set off a phrase for emphasis or a sharp break in thought\u2014like this.<\/p>\n<p>\u2013 En Dash U+2013 Hyphen (-) Used for ranges of numbers or dates (e.g., 1990\u20132000) or to join two names in a phrase.<\/p>\n<p>\u201c \u201d Curly Quotes U+201C, U+201D Straight quotes (&#8220;) Typographically correct quotation marks used in formal writing.<\/p>\n<p>\u2018 \u2019 Curly Apostrophe U+2018, U+2019 Straight apostrophe (&#8216;) Typographically correct apostrophe.<\/p>\n<p>\u2026 Ellipsis U+2026 Three periods (&#8230;) A single character for an ellipsis.<\/p>\n<p>A forensic caveat: these are heuristics, not proof. Smart-punctuation in any word processor produces identical glyphs from human input, and a model can be told to emit ASCII. The presence raises the prior; it does not establish AI provenance.<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\njavascript:(function(){var ID=&#039;glyphHL&#039;,ex=document.querySelectorAll(&#039;mark.&#039;+ID);if(ex.length){ex.forEach(function(m){m.replaceWith(document.createTextNode(m.dataset.c))});document.body.normalize();&#x5B;&#039;_&#039;+ID+&#039;s&#039;,&#039;_&#039;+ID+&#039;b&#039;].forEach(function(i){var e=document.getElementById(i);if(e)e.remove()});return}var G={&#039;\\u2014&#039;:&#x5B;&#039;em dash&#039;,&#039;U+2014&#039;,&#039;dash&#039;],&#039;\\u2013&#039;:&#x5B;&#039;en dash&#039;,&#039;U+2013&#039;,&#039;dash&#039;],&#039;\\u2212&#039;:&#x5B;&#039;minus&#039;,&#039;U+2212&#039;,&#039;dash&#039;],&#039;\\u2010&#039;:&#x5B;&#039;hyphen&#039;,&#039;U+2010&#039;,&#039;dash&#039;],&#039;\\u2012&#039;:&#x5B;&#039;figure dash&#039;,&#039;U+2012&#039;,&#039;dash&#039;],&#039;\\u2015&#039;:&#x5B;&#039;horizontal bar&#039;,&#039;U+2015&#039;,&#039;dash&#039;],&#039;\\u201C&#039;:&#x5B;&#039;left double quote&#039;,&#039;U+201C&#039;,&#039;quote&#039;],&#039;\\u201D&#039;:&#x5B;&#039;right double quote&#039;,&#039;U+201D&#039;,&#039;quote&#039;],&#039;\\u2018&#039;:&#x5B;&#039;left single quote&#039;,&#039;U+2018&#039;,&#039;quote&#039;],&#039;\\u2019&#039;:&#x5B;&#039;apostrophe \/ right single&#039;,&#039;U+2019&#039;,&#039;quote&#039;],&#039;\\u2032&#039;:&#x5B;&#039;prime&#039;,&#039;U+2032&#039;,&#039;quote&#039;],&#039;\\u2033&#039;:&#x5B;&#039;double prime&#039;,&#039;U+2033&#039;,&#039;quote&#039;],&#039;\\u2026&#039;:&#x5B;&#039;ellipsis&#039;,&#039;U+2026&#039;,&#039;ell&#039;],&#039;\\u00A0&#039;:&#x5B;&#039;no-break space&#039;,&#039;U+00A0&#039;,&#039;space&#039;,&#039;NB&#039;],&#039;\\u2009&#039;:&#x5B;&#039;thin space&#039;,&#039;U+2009&#039;,&#039;space&#039;,&#039;TH&#039;],&#039;\\u202F&#039;:&#x5B;&#039;narrow nbsp&#039;,&#039;U+202F&#039;,&#039;space&#039;,&#039;NNB&#039;],&#039;\\u200C&#039;:&#x5B;&#039;zero-width non-joiner&#039;,&#039;U+200C&#039;,&#039;space&#039;,&#039;ZWNJ&#039;],&#039;\\u200D&#039;:&#x5B;&#039;zero-width joiner&#039;,&#039;U+200D&#039;,&#039;space&#039;,&#039;ZWJ&#039;],&#039;\\uFEFF&#039;:&#x5B;&#039;BOM \/ zwnbsp&#039;,&#039;U+FEFF&#039;,&#039;space&#039;,&#039;BOM&#039;]};var cls=&#039;&#x5B;\\\\u2014\\\\u2013\\\\u2212\\\\u2010\\\\u2012\\\\u2015\\\\u201C\\\\u201D\\\\u2018\\\\u2019\\\\u2032\\\\u2033\\\\u2026\\\\u00A0\\\\u2009\\\\u202F\\\\u200C\\\\u200D\\\\uFEFF]&#039;;var has=new RegExp(cls),rx=new RegExp(cls,&#039;g&#039;);var s=document.createElement(&#039;style&#039;);s.id=&#039;_&#039;+ID+&#039;s&#039;;s.textContent=&#039;mark.&#039;+ID+&#039;{border-radius:2px;padding:0 1px;color:inherit;box-shadow:0 0 0 1px rgba(0,0,0,.2)}mark.&#039;+ID+&#039;.dash{background:#bcd0ff}mark.&#039;+ID+&#039;.quote{background:#ecc9ff}mark.&#039;+ID+&#039;.ell{background:#b8efe2}mark.&#039;+ID+&#039;.space{background:#ffd0c4;outline:1px dashed #b23b2e;display:inline-block;min-width:.5em;text-align:center}mark.&#039;+ID+&#039;.space::after{content:attr(data-x);font:9px\/1 monospace;color:#b23b2e;vertical-align:super;margin-left:1px}&#039;;document.head.appendChild(s);var c={dash:0,quote:0,ell:0,space:0},nodes=&#x5B;],w=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT,{acceptNode:function(n){if(!n.nodeValue||!has.test(n.nodeValue))return 3;var p=n.parentNode,t=p&amp;&amp;p.nodeName;if(t==&#039;SCRIPT&#039;||t==&#039;STYLE&#039;||t==&#039;NOSCRIPT&#039;||t==&#039;TEXTAREA&#039;)return 3;if(p&amp;&amp;p.isContentEditable)return 3;return 1}});while(w.nextNode())nodes.push(w.currentNode);nodes.forEach(function(n){var f=document.createDocumentFragment(),v=n.nodeValue,last=0,m;rx.lastIndex=0;while((m=rx.exec(v))){var ch=m&#x5B;0],i=m.index,g=G&#x5B;ch];if(i&gt;last)f.appendChild(document.createTextNode(v.slice(last,i)));var mk=document.createElement(&#039;mark&#039;);mk.className=ID+&#039; &#039;+g&#x5B;2];mk.dataset.c=ch;mk.title=g&#x5B;0]+&#039; &#039;+g&#x5B;1];if(g&#x5B;2]==&#039;space&#039;)mk.dataset.x=g&#x5B;3];mk.textContent=ch;f.appendChild(mk);c&#x5B;g&#x5B;2]]++;last=i+ch.length}if(last&lt;v.length)f.appendChild(document.createTextNode(v.slice(last)));n.parentNode.replaceChild(f,n)});var tot=c.dash+c.quote+c.ell+c.space,b=document.createElement(&#039;div&#039;);b.id=&#039;_&#039;+ID+&#039;b&#039;;b.style.cssText=&#039;position:fixed;z-index:2147483647;right:12px;bottom:12px;background:#14161a;color:#fff;font:12px\/1.5 system-ui,sans-serif;padding:8px 12px;border-radius:6px;box-shadow:0 2px 10px rgba(0,0,0,.3);max-width:240px&#039;;b.innerHTML=&#039;&lt;b&gt;&#039;+tot+&#039;&lt;\/b&gt; typeset glyphs&lt;br&gt;&#039;+c.dash+&#039; dash &amp;middot; &#039;+c.quote+&#039; quote &amp;middot; &#039;+c.ell+&#039; ellipsis &amp;middot; &#039;+c.space+&#039; space\/hidden&lt;br&gt;&lt;span style=&quot;opacity:.7&quot;&gt;click the bookmarklet again to clear&lt;\/span&gt;&#039;;document.body.appendChild(b)})();\r\n\r\n\/* glyph-highlighter bookmarklet \u2014 readable source\r\n *\r\n * Highlights typeset Unicode glyphs that a plain keyboard does not produce.\r\n * Run once to highlight; run again on the same page to remove.\r\n *\r\n * Categories (CSS class + colour):\r\n *   dash   blue          em\/en\/minus\/hyphen\/figure dash\/horizontal bar\r\n *   quote  purple        curly quotes, curly apostrophe, prime, double prime\r\n *   ell    teal          ellipsis\r\n *   space  red, dashed   no-break\/thin\/narrow spaces + zero-width chars (ZWJ\/ZWNJ\/BOM)\r\n *\r\n * Invisible characters carry a small superscript tag (NB, TH, ZWJ, ...) via ::after,\r\n * because they have no visible shape of their own.\r\n *\r\n * To install: minify to a single line and prefix with &quot;javascript:&quot; as a bookmark URL,\r\n * or use the one-liner already provided.\r\n *\/\r\n(function () {\r\n  var ID = &#039;glyphHL&#039;;\r\n\r\n  \/\/ --- toggle off: if marks exist, unwrap them and remove injected nodes ---\r\n  var existing = document.querySelectorAll(&#039;mark.&#039; + ID);\r\n  if (existing.length) {\r\n    existing.forEach(function (m) {\r\n      m.replaceWith(document.createTextNode(m.dataset.c)); \/\/ restore original char\r\n    });\r\n    document.body.normalize(); \/\/ merge split text nodes back together\r\n    &#x5B;&#039;_&#039; + ID + &#039;s&#039;, &#039;_&#039; + ID + &#039;b&#039;].forEach(function (id) {\r\n      var el = document.getElementById(id);\r\n      if (el) el.remove();\r\n    });\r\n    return;\r\n  }\r\n\r\n  \/\/ --- glyph table: char -&gt; &#x5B;name, codepoint, category, shortTag?] ---\r\n  var G = {\r\n    &#039;\\u2014&#039;: &#x5B;&#039;em dash&#039;, &#039;U+2014&#039;, &#039;dash&#039;],\r\n    &#039;\\u2013&#039;: &#x5B;&#039;en dash&#039;, &#039;U+2013&#039;, &#039;dash&#039;],\r\n    &#039;\\u2212&#039;: &#x5B;&#039;minus&#039;, &#039;U+2212&#039;, &#039;dash&#039;],\r\n    &#039;\\u2010&#039;: &#x5B;&#039;hyphen&#039;, &#039;U+2010&#039;, &#039;dash&#039;],\r\n    &#039;\\u2012&#039;: &#x5B;&#039;figure dash&#039;, &#039;U+2012&#039;, &#039;dash&#039;],\r\n    &#039;\\u2015&#039;: &#x5B;&#039;horizontal bar&#039;, &#039;U+2015&#039;, &#039;dash&#039;],\r\n    &#039;\\u201C&#039;: &#x5B;&#039;left double quote&#039;, &#039;U+201C&#039;, &#039;quote&#039;],\r\n    &#039;\\u201D&#039;: &#x5B;&#039;right double quote&#039;, &#039;U+201D&#039;, &#039;quote&#039;],\r\n    &#039;\\u2018&#039;: &#x5B;&#039;left single quote&#039;, &#039;U+2018&#039;, &#039;quote&#039;],\r\n    &#039;\\u2019&#039;: &#x5B;&#039;apostrophe \/ right single&#039;, &#039;U+2019&#039;, &#039;quote&#039;],\r\n    &#039;\\u2032&#039;: &#x5B;&#039;prime&#039;, &#039;U+2032&#039;, &#039;quote&#039;],\r\n    &#039;\\u2033&#039;: &#x5B;&#039;double prime&#039;, &#039;U+2033&#039;, &#039;quote&#039;],\r\n    &#039;\\u2026&#039;: &#x5B;&#039;ellipsis&#039;, &#039;U+2026&#039;, &#039;ell&#039;],\r\n    &#039;\\u00A0&#039;: &#x5B;&#039;no-break space&#039;, &#039;U+00A0&#039;, &#039;space&#039;, &#039;NB&#039;],\r\n    &#039;\\u2009&#039;: &#x5B;&#039;thin space&#039;, &#039;U+2009&#039;, &#039;space&#039;, &#039;TH&#039;],\r\n    &#039;\\u202F&#039;: &#x5B;&#039;narrow no-break space&#039;, &#039;U+202F&#039;, &#039;space&#039;, &#039;NNB&#039;],\r\n    &#039;\\u200C&#039;: &#x5B;&#039;zero-width non-joiner&#039;, &#039;U+200C&#039;, &#039;space&#039;, &#039;ZWNJ&#039;],\r\n    &#039;\\u200D&#039;: &#x5B;&#039;zero-width joiner&#039;, &#039;U+200D&#039;, &#039;space&#039;, &#039;ZWJ&#039;],\r\n    &#039;\\uFEFF&#039;: &#x5B;&#039;BOM \/ zero-width no-break space&#039;, &#039;U+FEFF&#039;, &#039;space&#039;, &#039;BOM&#039;]\r\n  };\r\n\r\n  \/\/ character class covering every key above\r\n  var cls = &#039;&#x5B;\\\\u2014\\\\u2013\\\\u2212\\\\u2010\\\\u2012\\\\u2015\\\\u201C\\\\u201D\\\\u2018\\\\u2019&#039; +\r\n            &#039;\\\\u2032\\\\u2033\\\\u2026\\\\u00A0\\\\u2009\\\\u202F\\\\u200C\\\\u200D\\\\uFEFF]&#039;;\r\n  var has = new RegExp(cls);        \/\/ non-global: safe for .test()\r\n  var rx  = new RegExp(cls, &#039;g&#039;);   \/\/ global: used to scan\/split text\r\n\r\n  \/\/ --- injected stylesheet ---\r\n  var style = document.createElement(&#039;style&#039;);\r\n  style.id = &#039;_&#039; + ID + &#039;s&#039;;\r\n  style.textContent =\r\n    &#039;mark.&#039; + ID + &#039;{border-radius:2px;padding:0 1px;color:inherit;box-shadow:0 0 0 1px rgba(0,0,0,.2)}&#039; +\r\n    &#039;mark.&#039; + ID + &#039;.dash{background:#bcd0ff}&#039; +\r\n    &#039;mark.&#039; + ID + &#039;.quote{background:#ecc9ff}&#039; +\r\n    &#039;mark.&#039; + ID + &#039;.ell{background:#b8efe2}&#039; +\r\n    &#039;mark.&#039; + ID + &#039;.space{background:#ffd0c4;outline:1px dashed #b23b2e;display:inline-block;min-width:.5em;text-align:center}&#039; +\r\n    &#039;mark.&#039; + ID + &#039;.space::after{content:attr(data-x);font:9px\/1 monospace;color:#b23b2e;vertical-align:super;margin-left:1px}&#039;;\r\n  document.head.appendChild(style);\r\n\r\n  \/\/ --- collect candidate text nodes ---\r\n  var counts = { dash: 0, quote: 0, ell: 0, space: 0 };\r\n  var nodes = &#x5B;];\r\n  var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {\r\n    acceptNode: function (n) {\r\n      if (!n.nodeValue || !has.test(n.nodeValue)) return NodeFilter.FILTER_REJECT;\r\n      var p = n.parentNode, t = p &amp;&amp; p.nodeName;\r\n      if (t === &#039;SCRIPT&#039; || t === &#039;STYLE&#039; || t === &#039;NOSCRIPT&#039; || t === &#039;TEXTAREA&#039;) return NodeFilter.FILTER_REJECT;\r\n      if (p &amp;&amp; p.isContentEditable) return NodeFilter.FILTER_REJECT;\r\n      return NodeFilter.FILTER_ACCEPT;\r\n    }\r\n  });\r\n  while (walker.nextNode()) nodes.push(walker.currentNode);\r\n\r\n  \/\/ --- wrap each matched glyph ---\r\n  nodes.forEach(function (n) {\r\n    var frag = document.createDocumentFragment();\r\n    var v = n.nodeValue, last = 0, m;\r\n    rx.lastIndex = 0;\r\n    while ((m = rx.exec(v))) {\r\n      var ch = m&#x5B;0], i = m.index, g = G&#x5B;ch];\r\n      if (i &gt; last) frag.appendChild(document.createTextNode(v.slice(last, i)));\r\n      var mk = document.createElement(&#039;mark&#039;);\r\n      mk.className = ID + &#039; &#039; + g&#x5B;2];\r\n      mk.dataset.c = ch;                 \/\/ original char, for clean restore\r\n      mk.title = g&#x5B;0] + &#039; &#039; + g&#x5B;1];      \/\/ hover tooltip: name + codepoint\r\n      if (g&#x5B;2] === &#039;space&#039;) mk.dataset.x = g&#x5B;3];\r\n      mk.textContent = ch;\r\n      frag.appendChild(mk);\r\n      counts&#x5B;g&#x5B;2]]++;\r\n      last = i + ch.length;\r\n    }\r\n    if (last &lt; v.length) frag.appendChild(document.createTextNode(v.slice(last)));\r\n    n.parentNode.replaceChild(frag, n);\r\n  });\r\n\r\n  \/\/ --- count badge ---\r\n  var total = counts.dash + counts.quote + counts.ell + counts.space;\r\n  var badge = document.createElement(&#039;div&#039;);\r\n  badge.id = &#039;_&#039; + ID + &#039;b&#039;;\r\n  badge.style.cssText =\r\n    &#039;position:fixed;z-index:2147483647;right:12px;bottom:12px;background:#14161a;color:#fff;&#039; +\r\n    &#039;font:12px\/1.5 system-ui,sans-serif;padding:8px 12px;border-radius:6px;&#039; +\r\n    &#039;box-shadow:0 2px 10px rgba(0,0,0,.3);max-width:240px&#039;;\r\n  badge.innerHTML =\r\n    &#039;&lt;b&gt;&#039; + total + &#039;&lt;\/b&gt; typeset glyphs&lt;br&gt;&#039; +\r\n    counts.dash + &#039; dash \\u00B7 &#039; + counts.quote + &#039; quote \\u00B7 &#039; +\r\n    counts.ell + &#039; ellipsis \\u00B7 &#039; + counts.space + &#039; space\/hidden&lt;br&gt;&#039; +\r\n    &#039;&lt;span style=&quot;opacity:.7&quot;&gt;click the bookmarklet again to clear&lt;\/span&gt;&#039;;\r\n  document.body.appendChild(badge);\r\n})();\r\n<\/pre>\n\n<p>&nbsp;<\/p>\n<div class=\"bottom-note\">\n  <span class=\"mod1\">CC-BY-NC Science Surf , accessed 13.06.2026<\/span>\n <\/div>","protected":false},"excerpt":{"rendered":"<p>As AI models are trained on vast amounts of text, including published, polished prose that uses professional typesetting, which is why they default to these more precise characters although they cannot be simply reached by a keyboard. Some examples are \u2014 Em Dash U+2014 Two hyphens (&#8211;) or special key commands Used to set off &hellip; <a href=\"https:\/\/www.wjst.de\/blog\/sciencesurf\/2026\/06\/a-bookmarklet-that-highlights-ai-generated-prose\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">A bookmarklet that highlights AI generated prose<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5129,20],"tags":[],"class_list":["post-25996","post","type-post","status-publish","format-standard","hentry","category-ai-supported","category-note-worthy"],"_links":{"self":[{"href":"https:\/\/www.wjst.de\/blog\/wp-json\/wp\/v2\/posts\/25996","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wjst.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wjst.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wjst.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wjst.de\/blog\/wp-json\/wp\/v2\/comments?post=25996"}],"version-history":[{"count":11,"href":"https:\/\/www.wjst.de\/blog\/wp-json\/wp\/v2\/posts\/25996\/revisions"}],"predecessor-version":[{"id":26463,"href":"https:\/\/www.wjst.de\/blog\/wp-json\/wp\/v2\/posts\/25996\/revisions\/26463"}],"wp:attachment":[{"href":"https:\/\/www.wjst.de\/blog\/wp-json\/wp\/v2\/media?parent=25996"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wjst.de\/blog\/wp-json\/wp\/v2\/categories?post=25996"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wjst.de\/blog\/wp-json\/wp\/v2\/tags?post=25996"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}