Vercel SDK Playground – tidy inline LaTeX

Render .math-inline / .math-display elements in the Playground and all its iframes.

  1. // ==UserScript==
  2. // @name Vercel SDK Playground – tidy inline LaTeX
  3. // @namespace https://sdk.vercel.ai/
  4. // @version 0.5
  5. // @description Render .math-inline / .math-display elements in the Playground and all its iframes.
  6. // @match https://sdk.vercel.ai/*
  7. // @run-at document-start
  8. // @grant GM_addStyle
  9. // ==/UserScript==
  10.  
  11. (() => {
  12. 'use strict';
  13.  
  14. /* 0. MathJax configuration (must precede library load) */
  15. unsafeWindow.MathJax = {
  16. tex: { inlineMath: [['\\(', '\\)']], displayMath: [['\\[', '\\]']] },
  17. chtml: { scale: 1 },
  18. options: { skipHtmlTags: ['script','noscript','style','textarea'] }
  19. };
  20.  
  21. /* 0.1 Keep inline math from becoming block‑level */
  22. GM_addStyle('mjx-container.MathJax { display:inline-block !important; }');
  23.  
  24. /* 1. Inject MathJax CHTML build */
  25. const s = document.createElement('script');
  26. s.async = true;
  27. s.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js';
  28. document.head.appendChild(s);
  29.  
  30. /* 2. Helpers ---------------------------------------------------------- */
  31. const wrap = (el) => {
  32. if (el.dataset.mjReady) return;
  33. const raw = el.textContent.trim();
  34. if (!raw) return;
  35. el.textContent = el.classList.contains('math-inline') ? `\\(${raw}\\)` : `\\[${raw}\\]`;
  36. el.dataset.mjReady = '1';
  37. };
  38.  
  39. const typeset = (nodes) => {
  40. if (unsafeWindow.MathJax?.typesetPromise) {
  41. unsafeWindow.MathJax.typesetPromise(nodes).catch(console.error);
  42. } else {
  43. setTimeout(() => typeset(nodes), 120);
  44. }
  45. };
  46.  
  47. const scan = (root = document) => {
  48. const elems = root.querySelectorAll('.math-inline, .math-display');
  49. if (!elems.length) return;
  50. elems.forEach(wrap);
  51. typeset(Array.from(elems));
  52. };
  53.  
  54. /* 3. First pass + live updates --------------------------------------- */
  55. (document.readyState === 'loading')
  56. ? document.addEventListener('DOMContentLoaded', () => scan())
  57. : scan();
  58.  
  59. const mo = new MutationObserver((ms) => {
  60. const add = [];
  61. ms.forEach((m) =>
  62. m.addedNodes.forEach((n) => {
  63. if (!(n instanceof HTMLElement)) return;
  64. if (n.matches?.('.math-inline, .math-display')) add.push(n);
  65. add.push(...n.querySelectorAll?.('.math-inline, .math-display') || []);
  66. })
  67. );
  68. if (add.length) scan({ querySelectorAll: () => add });
  69. });
  70. mo.observe(document.body, { childList: true, subtree: true });
  71. })();