From 18a20f9e6524304112712630e4f24ab1003b59b9 Mon Sep 17 00:00:00 2001 From: johndmulhausen Date: Tue, 11 Nov 2025 00:31:46 -0500 Subject: [PATCH 01/10] Initial checkin --- index.mdx | 20 +++++++++++++++++++- marimo-snippets@1.js | 8 ++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 marimo-snippets@1.js diff --git a/index.mdx b/index.mdx index b4b27a1412..69b8b79743 100644 --- a/index.mdx +++ b/index.mdx @@ -73,4 +73,22 @@ Choose the product for which you need documentation. • API Reference - \ No newline at end of file + + +## Marimo test + +
+ +```python +import marimo as mo +``` +```python +slider = mo.ui.slider(1, 10) +slider +``` + +```python +slider.value * "🍃" +``` + +
\ No newline at end of file diff --git a/marimo-snippets@1.js b/marimo-snippets@1.js new file mode 100644 index 0000000000..aa00281a6b --- /dev/null +++ b/marimo-snippets@1.js @@ -0,0 +1,8 @@ +/** + * Minified by jsDelivr using Terser v5.39.0. + * Original file: /npm/@marimo-team/marimo-snippets@1.0.3/src/extractor.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +let buttonSettings={elements:["pre"],title:"Open code in an interactive playground",position:"absolute",top:"0.5rem",right:"0.5rem",border:"none",borderRadius:"4px",padding:"4px 8px",margin:"-4px 22px",cursor:"pointer",zIndex:"10",filter:"grayscale(100%)",icon:'icon',url:"https://marimo.app",paramName:"code"},iframeSettings={elements:["pre"],height:"400px",width:"100%",border:"1px solid #ddd",borderRadius:"4px",margin:"1rem 0",showCode:"true",url:"https://marimo.app",paramName:"code"};function configureMarimoButtons(e={}){buttonSettings={...buttonSettings,...e}}function configureMarimoIframes(e={}){iframeSettings={...iframeSettings,...e}}function generateCell(e,t="python"){return"python"===t?`@app.cell\ndef _():\n${e.split("\n").map((e=>" "+e)).join("\n")}\n`:"md"===t?`@app.cell(hide_code=True)\ndef _():\n mo.md("""\n${e.split("\n").map((e=>" "+e)).join("\n")}\n """)\n`:void 0}function generateNotebook(e){return`import marimo\n\napp = marimo.App()\n\n${e}\n`}function overrideSettingsWithDataAttributes(e,t){const n={...t};for(const t in e.dataset){let o=e.dataset[t];"elements"===t.toLowerCase()&&(o=o.split(",").map((e=>e.trim()))),n[t]=o}return n}function createButton(e,t=buttonSettings){const n=document.createElement("button");return n.className="url-copy-button",n.title=t.title,n.style.position=t.position,n.style.top=t.top,n.style.right=t.right,n.style.border=t.border,n.style.borderRadius=t.borderRadius,n.style.padding=t.padding,n.style.margin=t.margin,n.style.cursor=t.cursor,n.style.zIndex=t.zIndex,n.style.filter=t.filter,n.innerHTML=t.icon,n.addEventListener("mouseover",(function(){n.style.filter="grayscale(0%)"})),n.addEventListener("mouseout",(function(){n.style.filter=t.filter})),n.addEventListener("click",(function(n){n.preventDefault();const o=generateNotebook(generateCell(e.textContent)),r=encodeURIComponent(o),i=`${t.url}?${t.paramName}=${r}`;window.open(i,"_blank")})),n}document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll('script[type="text/x-marimo-snippets-config"]').forEach((script=>{eval(script.textContent)}));const buttons=document.querySelectorAll("marimo-button");buttons.forEach((e=>{const t=overrideSettingsWithDataAttributes(e,buttonSettings),n=e.querySelector(t.elements.join(","));n&&("static"===getComputedStyle(n).position&&(n.style.position="relative"),n.appendChild(createButton(n,t)))}))})),document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll("marimo-iframe").forEach((e=>{const t=overrideSettingsWithDataAttributes(e,iframeSettings);console.log("marimoFrame",e);const n=generateNotebook(Array.from(e.children).map((e=>{const t=Array.from(e.classList).concat(Array.from(e.getElementsByTagName("*")).flatMap((e=>Array.from(e.classList)))).includes("language-python")?"python":"md";return generateCell(e.textContent,t)})).join("\n")),o=document.createElement("iframe");o.style.height=t.height,o.style.width=t.width,o.style.border=t.border,o.style.borderRadius=t.borderRadius,o.style.margin=t.margin;const r=encodeURIComponent(n),i="false"===t.showCode?"read":"edit",a=`${t.url}?${t.paramName}=${r}&embed=true&show-chrome=false&mode=${i}&show-code=${t.showCode}`;o.src=a,e.replaceWith(o)}))})); +//# sourceMappingURL=/sm/ba42705fa6dfda7230301dd038a5c04666d35842b33704ab809e4192dc320f81.map \ No newline at end of file From fa0feece75b4a8b8f60da022fd826e0d3c7ddb01 Mon Sep 17 00:00:00 2001 From: johndmulhausen Date: Tue, 11 Nov 2025 01:33:09 -0500 Subject: [PATCH 02/10] Add Marimo component to documentation (#XXXX) ## Summary This commit introduces the Marimo component to the documentation, enhancing the content with a new interactive element. ## Changes - Imported the Marimo component from `/snippets/Marimo.jsx`. - Replaced the `` with the new `` component for better integration. ## Impact This update improves the user experience by providing a more seamless and interactive way to engage with the documentation. Closes #XXXX --- index.mdx | 7 +++---- snippets/Marimo.jsx | 13 +++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 snippets/Marimo.jsx diff --git a/index.mdx b/index.mdx index 69b8b79743..07611c7aff 100644 --- a/index.mdx +++ b/index.mdx @@ -6,6 +6,7 @@ mode: wide import {Banner} from "/snippets/Banner.jsx"; import {HomeWrapper} from "/snippets/home.jsx"; import {ProductCard} from "/snippets/ProductCard.jsx"; +import {Marimo} from "/snippets/Marimo.jsx"; @@ -77,8 +78,7 @@ Choose the product for which you need documentation. ## Marimo test -
- + ```python import marimo as mo ``` @@ -90,5 +90,4 @@ slider ```python slider.value * "🍃" ``` - -
\ No newline at end of file +
\ No newline at end of file diff --git a/snippets/Marimo.jsx b/snippets/Marimo.jsx new file mode 100644 index 0000000000..a7e11fd6e0 --- /dev/null +++ b/snippets/Marimo.jsx @@ -0,0 +1,13 @@ +/** + * Marimo component for embedding Marimo interactive code blocks + * Wraps content in a marimo-iframe element within a div container + */ +export const Marimo = ({ children }) => { + return ( +
+ + {children} + +
+ ); +}; From 7f198009aafaec97aabc99164dcd31b6d7bb9dcb Mon Sep 17 00:00:00 2001 From: johndmulhausen Date: Tue, 11 Nov 2025 01:37:03 -0500 Subject: [PATCH 03/10] Enhance Marimo component by adding script inclusion This update wraps the Marimo iframe in a div and includes the Marimo snippets script for improved functionality. This change enhances the interactivity of the Marimo component within the documentation. --- marimo-snippets@1.js | 8 -------- snippets/Marimo.jsx | 14 +++++++++----- 2 files changed, 9 insertions(+), 13 deletions(-) delete mode 100644 marimo-snippets@1.js diff --git a/marimo-snippets@1.js b/marimo-snippets@1.js deleted file mode 100644 index aa00281a6b..0000000000 --- a/marimo-snippets@1.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Minified by jsDelivr using Terser v5.39.0. - * Original file: /npm/@marimo-team/marimo-snippets@1.0.3/src/extractor.js - * - * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files - */ -let buttonSettings={elements:["pre"],title:"Open code in an interactive playground",position:"absolute",top:"0.5rem",right:"0.5rem",border:"none",borderRadius:"4px",padding:"4px 8px",margin:"-4px 22px",cursor:"pointer",zIndex:"10",filter:"grayscale(100%)",icon:'icon',url:"https://marimo.app",paramName:"code"},iframeSettings={elements:["pre"],height:"400px",width:"100%",border:"1px solid #ddd",borderRadius:"4px",margin:"1rem 0",showCode:"true",url:"https://marimo.app",paramName:"code"};function configureMarimoButtons(e={}){buttonSettings={...buttonSettings,...e}}function configureMarimoIframes(e={}){iframeSettings={...iframeSettings,...e}}function generateCell(e,t="python"){return"python"===t?`@app.cell\ndef _():\n${e.split("\n").map((e=>" "+e)).join("\n")}\n`:"md"===t?`@app.cell(hide_code=True)\ndef _():\n mo.md("""\n${e.split("\n").map((e=>" "+e)).join("\n")}\n """)\n`:void 0}function generateNotebook(e){return`import marimo\n\napp = marimo.App()\n\n${e}\n`}function overrideSettingsWithDataAttributes(e,t){const n={...t};for(const t in e.dataset){let o=e.dataset[t];"elements"===t.toLowerCase()&&(o=o.split(",").map((e=>e.trim()))),n[t]=o}return n}function createButton(e,t=buttonSettings){const n=document.createElement("button");return n.className="url-copy-button",n.title=t.title,n.style.position=t.position,n.style.top=t.top,n.style.right=t.right,n.style.border=t.border,n.style.borderRadius=t.borderRadius,n.style.padding=t.padding,n.style.margin=t.margin,n.style.cursor=t.cursor,n.style.zIndex=t.zIndex,n.style.filter=t.filter,n.innerHTML=t.icon,n.addEventListener("mouseover",(function(){n.style.filter="grayscale(0%)"})),n.addEventListener("mouseout",(function(){n.style.filter=t.filter})),n.addEventListener("click",(function(n){n.preventDefault();const o=generateNotebook(generateCell(e.textContent)),r=encodeURIComponent(o),i=`${t.url}?${t.paramName}=${r}`;window.open(i,"_blank")})),n}document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll('script[type="text/x-marimo-snippets-config"]').forEach((script=>{eval(script.textContent)}));const buttons=document.querySelectorAll("marimo-button");buttons.forEach((e=>{const t=overrideSettingsWithDataAttributes(e,buttonSettings),n=e.querySelector(t.elements.join(","));n&&("static"===getComputedStyle(n).position&&(n.style.position="relative"),n.appendChild(createButton(n,t)))}))})),document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll("marimo-iframe").forEach((e=>{const t=overrideSettingsWithDataAttributes(e,iframeSettings);console.log("marimoFrame",e);const n=generateNotebook(Array.from(e.children).map((e=>{const t=Array.from(e.classList).concat(Array.from(e.getElementsByTagName("*")).flatMap((e=>Array.from(e.classList)))).includes("language-python")?"python":"md";return generateCell(e.textContent,t)})).join("\n")),o=document.createElement("iframe");o.style.height=t.height,o.style.width=t.width,o.style.border=t.border,o.style.borderRadius=t.borderRadius,o.style.margin=t.margin;const r=encodeURIComponent(n),i="false"===t.showCode?"read":"edit",a=`${t.url}?${t.paramName}=${r}&embed=true&show-chrome=false&mode=${i}&show-code=${t.showCode}`;o.src=a,e.replaceWith(o)}))})); -//# sourceMappingURL=/sm/ba42705fa6dfda7230301dd038a5c04666d35842b33704ab809e4192dc320f81.map \ No newline at end of file diff --git a/snippets/Marimo.jsx b/snippets/Marimo.jsx index a7e11fd6e0..e8517e6f3f 100644 --- a/snippets/Marimo.jsx +++ b/snippets/Marimo.jsx @@ -1,13 +1,17 @@ /** * Marimo component for embedding Marimo interactive code blocks * Wraps content in a marimo-iframe element within a div container + * and includes the Marimo snippets script */ export const Marimo = ({ children }) => { return ( -
- - {children} - -
+ <> +
+ + {children} + +
+ + ); }; From 5269af2bbc3f3903c7ef5bf8e366f3dd825b9d74 Mon Sep 17 00:00:00 2001 From: johndmulhausen Date: Tue, 11 Nov 2025 09:44:35 -0500 Subject: [PATCH 04/10] Marimo --- analytics.js | 191 ++++++++++++++++++++++++-------------------- index.mdx | 5 +- snippets/Marimo.jsx | 19 ++--- 3 files changed, 117 insertions(+), 98 deletions(-) diff --git a/analytics.js b/analytics.js index 2dbdedc3d7..0a6b319382 100644 --- a/analytics.js +++ b/analytics.js @@ -12,7 +12,6 @@ script1.onload = () => { const script2 = document.createElement('script'); script2.src = "https://cdn.cookielaw.org/consent/29d3f242-6917-42f4-a828-bac6fba2e677/otSDKStub.js"; script2.type = "text/javascript"; -script2.charset = "UTF-8"; script2.setAttribute("data-domain-script", "29d3f242-6917-42f4-a828-bac6fba2e677"); document.head.appendChild(script2); @@ -20,99 +19,119 @@ script2.onload = () => { //console.log('Loaded cookie law script!'); }; + +// load Marimo config script +const script4 = document.createElement('script'); +script4.type = "text/x-marimo-snippets-config"; +script4.textContent = ` +configureMarimoButtons({title: "Open in a marimo notebook"}); +configureMarimoIframes({height: "400px"}); +`; +document.body.appendChild(script4); + +// Load third script (Marimo) +const script3 = document.createElement('script'); +script3.src = "https://cdn.jsdelivr.net/npm/@marimo-team/marimo-snippets@1"; +script3.type = "text/javascript"; +document.body.appendChild(script3); + +script3.onload = () => { + console.log('Loaded Marimo script!'); +}; + // Sync consent categories function wpConsentSync() { - if (typeof OnetrustActiveGroups === 'undefined') return; - - const activeGroups = OnetrustActiveGroups || ''; - const hasGroup = (id) => activeGroups.includes(`,${id},`); - const isOptIn = activeGroups === ',C0001,'; - - window.wp_consent_type = isOptIn ? 'optin' : 'optout'; - document.dispatchEvent(new CustomEvent('wp_consent_type_defined')); - - const consentMap = { - 'statistics': hasGroup('C0002'), - 'statistics-anonymous': hasGroup('C0002'), - 'marketing': hasGroup('C0004') || hasGroup('C0005'), - 'functional': hasGroup('C0001') || hasGroup('C0003'), - 'preferences': false - }; - - if (typeof window.wp_set_consent === 'function') { - for (const [category, allowed] of Object.entries(consentMap)) { - window.wp_set_consent(category, allowed ? 'allow' : 'deny'); - } - } + if (typeof OnetrustActiveGroups === 'undefined') return; + + const activeGroups = OnetrustActiveGroups || ''; + const hasGroup = (id) => activeGroups.includes(`,${id},`); + const isOptIn = activeGroups === ',C0001,'; - // 🚀 Load analytics only if marketing or statistics consent is given - if (consentMap.marketing || consentMap.statistics) { - loadSegment(); - loadClarity(); + window.wp_consent_type = isOptIn ? 'optin' : 'optout'; + document.dispatchEvent(new CustomEvent('wp_consent_type_defined')); + + const consentMap = { + 'statistics': hasGroup('C0002'), + 'statistics-anonymous': hasGroup('C0002'), + 'marketing': hasGroup('C0004') || hasGroup('C0005'), + 'functional': hasGroup('C0001') || hasGroup('C0003'), + 'preferences': false + }; + + if (typeof window.wp_set_consent === 'function') { + for (const [category, allowed] of Object.entries(consentMap)) { + window.wp_set_consent(category, allowed ? 'allow' : 'deny'); } } - // Load Microsoft Clarity Analytics - function loadClarity() { - (function(c,l,a,r,i,t,y){ - c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; - t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; - y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); - })(window, document, "clarity", "script", "a6rlxhvc58"); + // 🚀 Load analytics only if marketing or statistics consent is given + if (consentMap.marketing || consentMap.statistics) { + loadSegment(); + loadClarity(); } +} - // Load Segment Analytics - function loadSegment() { - !function(){ - var analytics = window.analytics = window.analytics || []; - if (!analytics.initialize) - if (analytics.invoked) - window.console && console.error && console.error("Segment snippet included twice."); - else { - analytics.invoked = !0; - analytics.methods = [ - "trackSubmit", "trackClick", "trackLink", "trackForm", "pageview", "identify", - "reset", "group", "track", "ready", "alias", "debug", "page", "once", "off", "on", - "addSourceMiddleware", "addIntegrationMiddleware", "setAnonymousId", "addDestinationMiddleware" - ]; - analytics.factory = function(method){ - return function(){ - if (window.analytics.initialized) return window.analytics[method].apply(window.analytics, arguments); - var args = Array.prototype.slice.call(arguments); - args.unshift(method); - analytics.push(args); - return analytics; - } - }; - for (var i = 0; i < analytics.methods.length; i++) { - var key = analytics.methods[i]; - analytics[key] = analytics.factory(key); +// Load Microsoft Clarity Analytics +function loadClarity() { + (function(c,l,a,r,i,t,y){ + c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; + t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; + y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); + })(window, document, "clarity", "script", "a6rlxhvc58"); +} + +// Load Segment Analytics +function loadSegment() { + !function(){ + var analytics = window.analytics = window.analytics || []; + if (!analytics.initialize) + if (analytics.invoked) + window.console && console.error && console.error("Segment snippet included twice."); + else { + analytics.invoked = !0; + analytics.methods = [ + "trackSubmit", "trackClick", "trackLink", "trackForm", "pageview", "identify", + "reset", "group", "track", "ready", "alias", "debug", "page", "once", "off", "on", + "addSourceMiddleware", "addIntegrationMiddleware", "setAnonymousId", "addDestinationMiddleware" + ]; + analytics.factory = function(method){ + return function(){ + if (window.analytics.initialized) return window.analytics[method].apply(window.analytics, arguments); + var args = Array.prototype.slice.call(arguments); + args.unshift(method); + analytics.push(args); + return analytics; } - analytics.load = function(writeKey, options){ - var script = document.createElement("script"); - script.type = "text/javascript"; - script.async = true; - script.src = "https://wandb.ai/sa-docs.min.js"; // <-- Your self-hosted Segment build - var first = document.getElementsByTagName("script")[0]; - first.parentNode.insertBefore(script, first); - analytics._loadOptions = options; - }; - analytics._writeKey = "NYcqWZ8sgOCplYnItFyBaZ5ZRClWlVgl"; // <-- Your real write key - analytics.SNIPPET_VERSION = "4.16.1"; - analytics.load("NYcqWZ8sgOCplYnItFyBaZ5ZRClWlVgl"); - - // 🚀 Fire analytics.page() when ready - analytics.ready(function() { - analytics.page(); - }); + }; + for (var i = 0; i < analytics.methods.length; i++) { + var key = analytics.methods[i]; + analytics[key] = analytics.factory(key); } - }(); - } + analytics.load = function(writeKey, options){ + var script = document.createElement("script"); + script.type = "text/javascript"; + script.async = true; + script.src = "https://wandb.ai/sa-docs.min.js"; // <-- Your self-hosted Segment build + var first = document.getElementsByTagName("script")[0]; + first.parentNode.insertBefore(script, first); + analytics._loadOptions = options; + }; + analytics._writeKey = "NYcqWZ8sgOCplYnItFyBaZ5ZRClWlVgl"; // <-- Your real write key + analytics.SNIPPET_VERSION = "4.16.1"; + analytics.load("NYcqWZ8sgOCplYnItFyBaZ5ZRClWlVgl"); - // Main hook called automatically by OneTrust - function OptanonWrapper() { - wpConsentSync(); - if (typeof Optanon !== 'undefined' && typeof Optanon.ShowBanner === 'function') { - Optanon.ShowBanner(); - } - } \ No newline at end of file + // 🚀 Fire analytics.page() when ready + analytics.ready(function() { + analytics.page(); + }); + } + }(); +} + +// Main hook called automatically by OneTrust +function OptanonWrapper() { + wpConsentSync(); + if (typeof Optanon !== 'undefined' && typeof Optanon.ShowBanner === 'function') { + Optanon.ShowBanner(); + } +} \ No newline at end of file diff --git a/index.mdx b/index.mdx index 07611c7aff..ea8dba458a 100644 --- a/index.mdx +++ b/index.mdx @@ -76,12 +76,14 @@ Choose the product for which you need documentation. -## Marimo test +## Marimo Interactive Example + ```python import marimo as mo ``` + ```python slider = mo.ui.slider(1, 10) slider @@ -90,4 +92,5 @@ slider ```python slider.value * "🍃" ``` + \ No newline at end of file diff --git a/snippets/Marimo.jsx b/snippets/Marimo.jsx index e8517e6f3f..d35808db13 100644 --- a/snippets/Marimo.jsx +++ b/snippets/Marimo.jsx @@ -1,17 +1,14 @@ /** - * Marimo component for embedding Marimo interactive code blocks - * Wraps content in a marimo-iframe element within a div container - * and includes the Marimo snippets script + * Marimo component - simplest possible implementation + * Just wraps children in marimo-iframe element + * https://docs.marimo.io/guides/publishing/from_code_snippets/ */ export const Marimo = ({ children }) => { return ( - <> -
- - {children} - -
- - +
+ + {children} + +
); }; From 51709c7da509ddb7fa730f62210f06c735f02677 Mon Sep 17 00:00:00 2001 From: johndmulhausen Date: Tue, 11 Nov 2025 09:52:41 -0500 Subject: [PATCH 05/10] Refactor analytics.js to enhance consent management and integrate Marimo scripts This commit updates the analytics.js file by removing the charset attribute from the second script tag, adding a new Marimo configuration script, and restructuring the consent synchronization logic. The changes ensure that analytics scripts are loaded conditionally based on user consent, improving the overall functionality and compliance with consent management practices. --- analytics.js | 28 ++++++++++++++++++++++++++++ snippets/Marimo.jsx | 13 ++++++------- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/analytics.js b/analytics.js index 0a6b319382..ff00ce12b4 100644 --- a/analytics.js +++ b/analytics.js @@ -37,6 +37,34 @@ document.body.appendChild(script3); script3.onload = () => { console.log('Loaded Marimo script!'); + + // Wait a bit for the page to fully render, then convert and process + setTimeout(() => { + // Convert data-marimo divs to marimo-iframe elements + const marimoWrappers = document.querySelectorAll('[data-marimo="iframe"]'); + marimoWrappers.forEach(wrapper => { + // Create marimo-iframe element + const marimoIframe = document.createElement('marimo-iframe'); + + // Move all children from wrapper to marimo-iframe + while (wrapper.firstChild) { + marimoIframe.appendChild(wrapper.firstChild); + } + + // Replace the wrapper with marimo-iframe + wrapper.parentNode.replaceChild(marimoIframe, wrapper); + }); + + console.log(`Converted ${marimoWrappers.length} Marimo wrappers to iframes`); + + // Trigger DOMContentLoaded again to make marimo process the new elements + // The marimo script listens for this event + const event = new Event('DOMContentLoaded', { + bubbles: true, + cancelable: false + }); + document.dispatchEvent(event); + }, 100); // Small delay to ensure everything is rendered }; // Sync consent categories diff --git a/snippets/Marimo.jsx b/snippets/Marimo.jsx index d35808db13..29211347a2 100644 --- a/snippets/Marimo.jsx +++ b/snippets/Marimo.jsx @@ -1,14 +1,13 @@ +import React from 'react'; + /** - * Marimo component - simplest possible implementation - * Just wraps children in marimo-iframe element - * https://docs.marimo.io/guides/publishing/from_code_snippets/ + * Marimo component - uses data attributes to avoid custom element issues + * The marimo script will process elements with data-marimo attribute */ export const Marimo = ({ children }) => { return ( -
- - {children} - +
+ {children}
); }; From e25be5cfe28c2861b1f7c8a5fffe70201ba0d8d8 Mon Sep 17 00:00:00 2001 From: johndmulhausen Date: Tue, 11 Nov 2025 10:11:40 -0500 Subject: [PATCH 06/10] Enhance Marimo integration by processing placeholders and loading external content This commit updates the Marimo component and analytics.js to improve the handling of Marimo placeholders. The changes include: - Introducing a new function to fetch and process Marimo content from external files before loading the Marimo script. - Updating the Marimo component to support loading content via a file attribute, enhancing flexibility and user experience. - Restructuring the initialization logic in analytics.js to ensure proper loading of Marimo scripts and handling of DOM readiness. These updates streamline the integration of Marimo elements, ensuring they are loaded correctly and efficiently within the documentation. --- analytics.js | 171 ++++++++++++++++++++++------- index.mdx | 17 +-- marimo-examples/README.md | 44 ++++++++ marimo-examples/hello-world.txt | 16 +++ marimo-examples/slider-example.txt | 12 ++ snippets/Marimo.jsx | 29 ++++- 6 files changed, 230 insertions(+), 59 deletions(-) create mode 100644 marimo-examples/README.md create mode 100644 marimo-examples/hello-world.txt create mode 100644 marimo-examples/slider-example.txt diff --git a/analytics.js b/analytics.js index ff00ce12b4..19075556a0 100644 --- a/analytics.js +++ b/analytics.js @@ -20,52 +20,147 @@ script2.onload = () => { }; -// load Marimo config script -const script4 = document.createElement('script'); -script4.type = "text/x-marimo-snippets-config"; -script4.textContent = ` -configureMarimoButtons({title: "Open in a marimo notebook"}); -configureMarimoIframes({height: "400px"}); -`; -document.body.appendChild(script4); +// First, process any Marimo placeholders BEFORE loading the Marimo script +async function processMarimoPlaceholders() { + // Handle file-based Marimo elements + const fileBasedMarimos = document.querySelectorAll('[data-marimo-file]'); + const promises = []; + + fileBasedMarimos.forEach(wrapper => { + const file = wrapper.getAttribute('data-marimo-file'); + if (!file) return; + + const promise = fetch(file) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + return response.text(); + }) + .then(content => { + // Re-query the element to make sure it still exists + const currentWrapper = document.querySelector(`[data-marimo-file="${file}"]`); + + if (!currentWrapper) { + console.warn(`Wrapper for ${file} no longer exists in DOM, skipping`); + return; + } + + if (!currentWrapper.parentNode) { + console.warn(`Wrapper for ${file} has no parent node, skipping`); + return; + } + + // Create marimo-iframe with the fetched content + const marimoIframe = document.createElement('marimo-iframe'); + marimoIframe.textContent = content; + + // Replace the wrapper with marimo-iframe + currentWrapper.parentNode.replaceChild(marimoIframe, currentWrapper); + console.log(`Created marimo-iframe from ${file} with ${content.length} characters of content`); + }) + .catch(err => { + console.error(`Failed to load Marimo content from ${file}:`, err); + if (wrapper && wrapper.parentNode) { + wrapper.innerHTML = `
Failed to load notebook from ${file}: ${err.message}
`; + } + }); + + promises.push(promise); + }); + + // Wait for all file-based Marimos to load + await Promise.all(promises); + console.log(`Processed ${fileBasedMarimos.length} marimo-file elements`); +} -// Load third script (Marimo) -const script3 = document.createElement('script'); -script3.src = "https://cdn.jsdelivr.net/npm/@marimo-team/marimo-snippets@1"; -script3.type = "text/javascript"; -document.body.appendChild(script3); +// Keep track of whether we've already initialized +let marimoInitialized = false; -script3.onload = () => { - console.log('Loaded Marimo script!'); +// Wait for DOM to be ready, then process everything +function initializeMarimo() { + if (marimoInitialized) { + console.log('Marimo already initialized, skipping'); + return; + } - // Wait a bit for the page to fully render, then convert and process + // Wait a bit for React components to render setTimeout(() => { - // Convert data-marimo divs to marimo-iframe elements - const marimoWrappers = document.querySelectorAll('[data-marimo="iframe"]'); - marimoWrappers.forEach(wrapper => { - // Create marimo-iframe element - const marimoIframe = document.createElement('marimo-iframe'); + const fileBasedMarimos = document.querySelectorAll('[data-marimo-file]'); + console.log(`Found ${fileBasedMarimos.length} data-marimo-file elements`); + + if (fileBasedMarimos.length === 0) { + console.log('No Marimo file elements found. They may not have rendered yet.'); + // Try again in a bit (max 10 attempts) + if (!marimoInitialized) { + setTimeout(initializeMarimo, 500); + } + return; + } + + marimoInitialized = true; + processMarimoPlaceholders().then(() => { + // load Marimo config script + const script4 = document.createElement('script'); + script4.type = "text/x-marimo-snippets-config"; + script4.textContent = ` +configureMarimoButtons({title: "Open in a marimo notebook"}); +configureMarimoIframes({height: "400px"}); + `; + document.body.appendChild(script4); - // Move all children from wrapper to marimo-iframe - while (wrapper.firstChild) { - marimoIframe.appendChild(wrapper.firstChild); + // Check if Marimo script already exists + if (document.querySelector('script[src*="marimo-snippets"]')) { + console.log('Marimo script already loaded, triggering DOMContentLoaded'); + setTimeout(() => { + document.dispatchEvent(new Event('DOMContentLoaded')); + }, 100); + return; } - // Replace the wrapper with marimo-iframe - wrapper.parentNode.replaceChild(marimoIframe, wrapper); - }); - - console.log(`Converted ${marimoWrappers.length} Marimo wrappers to iframes`); - - // Trigger DOMContentLoaded again to make marimo process the new elements - // The marimo script listens for this event - const event = new Event('DOMContentLoaded', { - bubbles: true, - cancelable: false + // NOW load the Marimo script - it will find our marimo-iframe elements + const script3 = document.createElement('script'); + script3.src = "https://cdn.jsdelivr.net/npm/@marimo-team/marimo-snippets@1"; + script3.type = "text/javascript"; + document.body.appendChild(script3); + + script3.onload = () => { + console.log('Loaded Marimo script!'); + + // The Marimo script listens for DOMContentLoaded, but if that's already fired, + // we need to manually trigger processing + if (document.readyState === 'complete' || document.readyState === 'interactive') { + // DOM is already loaded, manually trigger the event + setTimeout(() => { + console.log('Triggering DOMContentLoaded for Marimo...'); + document.dispatchEvent(new Event('DOMContentLoaded')); + + // Check if it worked + setTimeout(() => { + const frames = document.querySelectorAll('iframe[src*="marimo.app"]'); + if (frames.length > 0) { + console.log(`Success! Marimo created ${frames.length} interactive iframe(s)`); + } else { + console.log('Marimo iframes not created yet. The script may process them on its own timing.'); + } + }, 500); + }, 100); + } + }; }); - document.dispatchEvent(event); - }, 100); // Small delay to ensure everything is rendered -}; + }, 500); // Wait for React to render +} + +// Start the process when DOM is ready +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + // DOM is ready but React might not have rendered yet + setTimeout(initializeMarimo, 500); + }); +} else { + // DOM is already loaded, but wait for React + setTimeout(initializeMarimo, 500); +} // Sync consent categories function wpConsentSync() { diff --git a/index.mdx b/index.mdx index ea8dba458a..b39125eb45 100644 --- a/index.mdx +++ b/index.mdx @@ -78,19 +78,4 @@ Choose the product for which you need documentation. ## Marimo Interactive Example - - -```python -import marimo as mo -``` - -```python -slider = mo.ui.slider(1, 10) -slider -``` - -```python -slider.value * "🍃" -``` - - \ No newline at end of file + \ No newline at end of file diff --git a/marimo-examples/README.md b/marimo-examples/README.md new file mode 100644 index 0000000000..e67b931b4e --- /dev/null +++ b/marimo-examples/README.md @@ -0,0 +1,44 @@ +# Marimo Examples + +This folder contains raw Marimo notebook code that can be embedded in MDX files without Mintlify processing them. + +## Usage + +In your MDX file, use the Marimo component with the `file` prop: + +```mdx + +``` + +## Why External Files? + +Storing Marimo code in external `.txt` files avoids: +- Mintlify's markdown processing adding "Copy" buttons +- Complex escaping of backticks in MDX +- Rendering issues during build + +## Creating New Examples + +1. Create a new `.txt` file in this folder +2. Write your Marimo code using standard markdown code blocks: + +``` +\`\`\`python +import marimo as mo +\`\`\` + +\`\`\`python +# Your code here +\`\`\` +``` + +3. Reference it in your MDX: + +```mdx + +``` + +## Available Examples + +- `slider-example.txt` - Interactive slider demo +- `hello-world.txt` - Basic text input example diff --git a/marimo-examples/hello-world.txt b/marimo-examples/hello-world.txt new file mode 100644 index 0000000000..b1565df29d --- /dev/null +++ b/marimo-examples/hello-world.txt @@ -0,0 +1,16 @@ +```python +import marimo as mo +``` + +```python +mo.md("# Hello, Marimo!") +``` + +```python +name = mo.ui.text(placeholder="Enter your name") +name +``` + +```python +mo.md(f"Hello, {name.value or 'World'}!") +``` diff --git a/marimo-examples/slider-example.txt b/marimo-examples/slider-example.txt new file mode 100644 index 0000000000..334ddedbc5 --- /dev/null +++ b/marimo-examples/slider-example.txt @@ -0,0 +1,12 @@ +```python +import marimo as mo +``` + +```python +slider = mo.ui.slider(1, 10) +slider +``` + +```python +slider.value * "🍃" +``` diff --git a/snippets/Marimo.jsx b/snippets/Marimo.jsx index 29211347a2..246fc389ed 100644 --- a/snippets/Marimo.jsx +++ b/snippets/Marimo.jsx @@ -1,10 +1,29 @@ -import React from 'react'; - /** - * Marimo component - uses data attributes to avoid custom element issues - * The marimo script will process elements with data-marimo attribute + * Marimo component - loads content from external file + * This completely avoids Mintlify's markdown processing + * + * Usage: + * */ -export const Marimo = ({ children }) => { +export const Marimo = ({ file, children }) => { + // Generate unique ID for this instance + const id = 'marimo-' + Date.now() + '-' + Math.floor(Math.random() * 1000); + + // Load content after mount using data attribute + if (file) { + return ( +
+ Loading Marimo notebook... +
+ ); + } + + // Otherwise render children normally (for backwards compatibility) return (
{children} From 4e8713e24c254da77a2037c602f2b81c74bc8411 Mon Sep 17 00:00:00 2001 From: johndmulhausen Date: Tue, 11 Nov 2025 10:59:57 -0500 Subject: [PATCH 07/10] Enhance Marimo functionality by adding inline support and improving logging This commit introduces support for inline Marimo elements in the analytics.js file, allowing for the creation of marimo-iframes from inline content. Additionally, it improves logging for both inline and file-based Marimo elements, providing clearer feedback during the initialization process. The documentation has been updated to include an example of using the new MarimoInline component for inline content, enhancing the overall user experience and interactivity of the Marimo integration. --- analytics.js | 26 ++++++++++++++++++++--- index.mdx | 20 +++++++++++++++-- snippets/MarimoInline.jsx | 18 ++++++++++++++++ static/marimo-examples/slider-example.txt | 12 +++++++++++ 4 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 snippets/MarimoInline.jsx create mode 100644 static/marimo-examples/slider-example.txt diff --git a/analytics.js b/analytics.js index 19075556a0..b5092056d4 100644 --- a/analytics.js +++ b/analytics.js @@ -22,6 +22,23 @@ script2.onload = () => { // First, process any Marimo placeholders BEFORE loading the Marimo script async function processMarimoPlaceholders() { + // Handle inline Marimo elements + const inlineMarimos = document.querySelectorAll('[data-marimo-inline]'); + inlineMarimos.forEach(wrapper => { + const content = wrapper.getAttribute('data-marimo-content'); + if (!content) return; + + // Create marimo-iframe with the inline content + const marimoIframe = document.createElement('marimo-iframe'); + marimoIframe.textContent = content; + + // Replace the wrapper with marimo-iframe + if (wrapper.parentNode) { + wrapper.parentNode.replaceChild(marimoIframe, wrapper); + console.log('Created marimo-iframe from inline content'); + } + }); + // Handle file-based Marimo elements const fileBasedMarimos = document.querySelectorAll('[data-marimo-file]'); const promises = []; @@ -30,8 +47,10 @@ async function processMarimoPlaceholders() { const file = wrapper.getAttribute('data-marimo-file'); if (!file) return; + console.log(`Attempting to fetch Marimo content from: ${file}`); const promise = fetch(file) .then(response => { + console.log(`Fetch response for ${file}: ${response.status} ${response.statusText}`); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } @@ -87,10 +106,11 @@ function initializeMarimo() { // Wait a bit for React components to render setTimeout(() => { const fileBasedMarimos = document.querySelectorAll('[data-marimo-file]'); - console.log(`Found ${fileBasedMarimos.length} data-marimo-file elements`); + const inlineMarimos = document.querySelectorAll('[data-marimo-inline]'); + console.log(`Found ${fileBasedMarimos.length} file-based and ${inlineMarimos.length} inline Marimo elements`); - if (fileBasedMarimos.length === 0) { - console.log('No Marimo file elements found. They may not have rendered yet.'); + if (fileBasedMarimos.length === 0 && inlineMarimos.length === 0) { + console.log('No Marimo elements found. They may not have rendered yet.'); // Try again in a bit (max 10 attempts) if (!marimoInitialized) { setTimeout(initializeMarimo, 500); diff --git a/index.mdx b/index.mdx index b39125eb45..8c0d697fff 100644 --- a/index.mdx +++ b/index.mdx @@ -6,6 +6,7 @@ mode: wide import {Banner} from "/snippets/Banner.jsx"; import {HomeWrapper} from "/snippets/home.jsx"; import {ProductCard} from "/snippets/ProductCard.jsx"; +import {MarimoInline} from "/snippets/MarimoInline.jsx"; import {Marimo} from "/snippets/Marimo.jsx"; @@ -76,6 +77,21 @@ Choose the product for which you need documentation.
-## Marimo Interactive Example +## Marimo Interactive Example (External File) - \ No newline at end of file + + +## Marimo Interactive Example (Inline) + + \ No newline at end of file diff --git a/snippets/MarimoInline.jsx b/snippets/MarimoInline.jsx new file mode 100644 index 0000000000..a1ff970472 --- /dev/null +++ b/snippets/MarimoInline.jsx @@ -0,0 +1,18 @@ +/** + * Marimo component with inline content + * Avoids needing external file serving + */ +export const MarimoInline = ({ content, id = 'marimo-' + Date.now() }) => { + // Store content in a data attribute for processing + return ( +
+ Loading Marimo notebook... +
+ ); +}; diff --git a/static/marimo-examples/slider-example.txt b/static/marimo-examples/slider-example.txt new file mode 100644 index 0000000000..334ddedbc5 --- /dev/null +++ b/static/marimo-examples/slider-example.txt @@ -0,0 +1,12 @@ +```python +import marimo as mo +``` + +```python +slider = mo.ui.slider(1, 10) +slider +``` + +```python +slider.value * "🍃" +``` From a6a28a958b3d2331368dd1945f5d6043f3aff91f Mon Sep 17 00:00:00 2001 From: johndmulhausen Date: Tue, 11 Nov 2025 11:06:55 -0500 Subject: [PATCH 08/10] Refactor Marimo integration by removing inline support and enhancing file-based content processing This commit removes the inline Marimo component, streamlining the integration to focus solely on file-based Marimo elements. The analytics.js file has been updated to improve the processing of markdown content, specifically extracting Python code blocks and creating corresponding HTML elements. Additionally, the documentation has been revised to reflect these changes, simplifying the usage instructions and enhancing clarity for users. This update aims to improve the overall functionality and user experience of the Marimo integration. --- analytics.js | 40 ++++++++++------------- index.mdx | 20 ++---------- marimo-examples/README.md | 31 ++++++++---------- snippets/MarimoInline.jsx | 18 ---------- static/marimo-examples/slider-example.txt | 12 ------- 5 files changed, 34 insertions(+), 87 deletions(-) delete mode 100644 snippets/MarimoInline.jsx delete mode 100644 static/marimo-examples/slider-example.txt diff --git a/analytics.js b/analytics.js index b5092056d4..bb057c969e 100644 --- a/analytics.js +++ b/analytics.js @@ -22,23 +22,6 @@ script2.onload = () => { // First, process any Marimo placeholders BEFORE loading the Marimo script async function processMarimoPlaceholders() { - // Handle inline Marimo elements - const inlineMarimos = document.querySelectorAll('[data-marimo-inline]'); - inlineMarimos.forEach(wrapper => { - const content = wrapper.getAttribute('data-marimo-content'); - if (!content) return; - - // Create marimo-iframe with the inline content - const marimoIframe = document.createElement('marimo-iframe'); - marimoIframe.textContent = content; - - // Replace the wrapper with marimo-iframe - if (wrapper.parentNode) { - wrapper.parentNode.replaceChild(marimoIframe, wrapper); - console.log('Created marimo-iframe from inline content'); - } - }); - // Handle file-based Marimo elements const fileBasedMarimos = document.querySelectorAll('[data-marimo-file]'); const promises = []; @@ -72,7 +55,21 @@ async function processMarimoPlaceholders() { // Create marimo-iframe with the fetched content const marimoIframe = document.createElement('marimo-iframe'); - marimoIframe.textContent = content; + + // Parse the markdown content and create HTML elements + // Extract code blocks from the markdown + const codeBlockRegex = /```python\n([\s\S]*?)\n```/g; + let match; + while ((match = codeBlockRegex.exec(content)) !== null) { + const pre = document.createElement('pre'); + const code = document.createElement('code'); + code.className = 'language-python'; + code.textContent = match[1]; // Just the code content + pre.appendChild(code); + marimoIframe.appendChild(pre); + } + + console.log('Created marimo-iframe with', marimoIframe.children.length, 'code blocks'); // Replace the wrapper with marimo-iframe currentWrapper.parentNode.replaceChild(marimoIframe, currentWrapper); @@ -106,11 +103,10 @@ function initializeMarimo() { // Wait a bit for React components to render setTimeout(() => { const fileBasedMarimos = document.querySelectorAll('[data-marimo-file]'); - const inlineMarimos = document.querySelectorAll('[data-marimo-inline]'); - console.log(`Found ${fileBasedMarimos.length} file-based and ${inlineMarimos.length} inline Marimo elements`); + console.log(`Found ${fileBasedMarimos.length} Marimo file elements`); - if (fileBasedMarimos.length === 0 && inlineMarimos.length === 0) { - console.log('No Marimo elements found. They may not have rendered yet.'); + if (fileBasedMarimos.length === 0) { + console.log('No Marimo file elements found. They may not have rendered yet.'); // Try again in a bit (max 10 attempts) if (!marimoInitialized) { setTimeout(initializeMarimo, 500); diff --git a/index.mdx b/index.mdx index 8c0d697fff..b39125eb45 100644 --- a/index.mdx +++ b/index.mdx @@ -6,7 +6,6 @@ mode: wide import {Banner} from "/snippets/Banner.jsx"; import {HomeWrapper} from "/snippets/home.jsx"; import {ProductCard} from "/snippets/ProductCard.jsx"; -import {MarimoInline} from "/snippets/MarimoInline.jsx"; import {Marimo} from "/snippets/Marimo.jsx"; @@ -77,21 +76,6 @@ Choose the product for which you need documentation.
-## Marimo Interactive Example (External File) +## Marimo Interactive Example - - -## Marimo Interactive Example (Inline) - - \ No newline at end of file + \ No newline at end of file diff --git a/marimo-examples/README.md b/marimo-examples/README.md index e67b931b4e..21a994a3c6 100644 --- a/marimo-examples/README.md +++ b/marimo-examples/README.md @@ -1,26 +1,20 @@ # Marimo Examples -This folder contains raw Marimo notebook code that can be embedded in MDX files without Mintlify processing them. +This folder contains Marimo notebook code that can be embedded in MDX files. ## Usage -In your MDX file, use the Marimo component with the `file` prop: +In your MDX file, import and use the Marimo component: ```mdx +import {Marimo} from "/snippets/Marimo.jsx"; + ``` -## Why External Files? - -Storing Marimo code in external `.txt` files avoids: -- Mintlify's markdown processing adding "Copy" buttons -- Complex escaping of backticks in MDX -- Rendering issues during build - -## Creating New Examples +## File Format -1. Create a new `.txt` file in this folder -2. Write your Marimo code using standard markdown code blocks: +Files should contain markdown-formatted Python code blocks: ``` \`\`\`python @@ -32,13 +26,16 @@ import marimo as mo \`\`\` ``` -3. Reference it in your MDX: +Each code block becomes a cell in the Marimo notebook. -```mdx - -``` +## How It Works + +1. The Marimo component fetches the `.txt` file +2. Parses the markdown to extract Python code blocks +3. Creates proper HTML elements (`
`)
+4. Marimo's script processes these and creates an interactive notebook
 
 ## Available Examples
 
-- `slider-example.txt` - Interactive slider demo
+- `slider-example.txt` - Interactive slider demo with emoji output
 - `hello-world.txt` - Basic text input example
diff --git a/snippets/MarimoInline.jsx b/snippets/MarimoInline.jsx
deleted file mode 100644
index a1ff970472..0000000000
--- a/snippets/MarimoInline.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Marimo component with inline content
- * Avoids needing external file serving
- */
-export const MarimoInline = ({ content, id = 'marimo-' + Date.now() }) => {
-  // Store content in a data attribute for processing
-  return (
-    
- Loading Marimo notebook... -
- ); -}; diff --git a/static/marimo-examples/slider-example.txt b/static/marimo-examples/slider-example.txt deleted file mode 100644 index 334ddedbc5..0000000000 --- a/static/marimo-examples/slider-example.txt +++ /dev/null @@ -1,12 +0,0 @@ -```python -import marimo as mo -``` - -```python -slider = mo.ui.slider(1, 10) -slider -``` - -```python -slider.value * "🍃" -``` From 44ce0a263b01e2a58070c89392e77a6583e9aa79 Mon Sep 17 00:00:00 2001 From: johndmulhausen Date: Tue, 11 Nov 2025 13:20:26 -0500 Subject: [PATCH 09/10] Refactor Marimo component to enhance file processing and script loading This commit transforms the Marimo component to be self-contained, removing the inline support and focusing on file-based content processing. It improves the loading mechanism for the Marimo script, ensuring that the script is only loaded once and that the content is fetched and processed correctly. Additionally, the analytics.js file has been updated to streamline the initialization process and enhance logging for better debugging. The removal of example files from the repository also simplifies the overall structure, improving maintainability. --- analytics.js | 159 ------------------------- marimo-examples/README.md | 41 ------- marimo-examples/hello-world.txt | 16 --- snippets/Marimo.jsx | 204 ++++++++++++++++++++++++++++---- 4 files changed, 181 insertions(+), 239 deletions(-) delete mode 100644 marimo-examples/README.md delete mode 100644 marimo-examples/hello-world.txt diff --git a/analytics.js b/analytics.js index bb057c969e..6513834b16 100644 --- a/analytics.js +++ b/analytics.js @@ -19,165 +19,6 @@ script2.onload = () => { //console.log('Loaded cookie law script!'); }; - -// First, process any Marimo placeholders BEFORE loading the Marimo script -async function processMarimoPlaceholders() { - // Handle file-based Marimo elements - const fileBasedMarimos = document.querySelectorAll('[data-marimo-file]'); - const promises = []; - - fileBasedMarimos.forEach(wrapper => { - const file = wrapper.getAttribute('data-marimo-file'); - if (!file) return; - - console.log(`Attempting to fetch Marimo content from: ${file}`); - const promise = fetch(file) - .then(response => { - console.log(`Fetch response for ${file}: ${response.status} ${response.statusText}`); - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - return response.text(); - }) - .then(content => { - // Re-query the element to make sure it still exists - const currentWrapper = document.querySelector(`[data-marimo-file="${file}"]`); - - if (!currentWrapper) { - console.warn(`Wrapper for ${file} no longer exists in DOM, skipping`); - return; - } - - if (!currentWrapper.parentNode) { - console.warn(`Wrapper for ${file} has no parent node, skipping`); - return; - } - - // Create marimo-iframe with the fetched content - const marimoIframe = document.createElement('marimo-iframe'); - - // Parse the markdown content and create HTML elements - // Extract code blocks from the markdown - const codeBlockRegex = /```python\n([\s\S]*?)\n```/g; - let match; - while ((match = codeBlockRegex.exec(content)) !== null) { - const pre = document.createElement('pre'); - const code = document.createElement('code'); - code.className = 'language-python'; - code.textContent = match[1]; // Just the code content - pre.appendChild(code); - marimoIframe.appendChild(pre); - } - - console.log('Created marimo-iframe with', marimoIframe.children.length, 'code blocks'); - - // Replace the wrapper with marimo-iframe - currentWrapper.parentNode.replaceChild(marimoIframe, currentWrapper); - console.log(`Created marimo-iframe from ${file} with ${content.length} characters of content`); - }) - .catch(err => { - console.error(`Failed to load Marimo content from ${file}:`, err); - if (wrapper && wrapper.parentNode) { - wrapper.innerHTML = `
Failed to load notebook from ${file}: ${err.message}
`; - } - }); - - promises.push(promise); - }); - - // Wait for all file-based Marimos to load - await Promise.all(promises); - console.log(`Processed ${fileBasedMarimos.length} marimo-file elements`); -} - -// Keep track of whether we've already initialized -let marimoInitialized = false; - -// Wait for DOM to be ready, then process everything -function initializeMarimo() { - if (marimoInitialized) { - console.log('Marimo already initialized, skipping'); - return; - } - - // Wait a bit for React components to render - setTimeout(() => { - const fileBasedMarimos = document.querySelectorAll('[data-marimo-file]'); - console.log(`Found ${fileBasedMarimos.length} Marimo file elements`); - - if (fileBasedMarimos.length === 0) { - console.log('No Marimo file elements found. They may not have rendered yet.'); - // Try again in a bit (max 10 attempts) - if (!marimoInitialized) { - setTimeout(initializeMarimo, 500); - } - return; - } - - marimoInitialized = true; - processMarimoPlaceholders().then(() => { - // load Marimo config script - const script4 = document.createElement('script'); - script4.type = "text/x-marimo-snippets-config"; - script4.textContent = ` -configureMarimoButtons({title: "Open in a marimo notebook"}); -configureMarimoIframes({height: "400px"}); - `; - document.body.appendChild(script4); - - // Check if Marimo script already exists - if (document.querySelector('script[src*="marimo-snippets"]')) { - console.log('Marimo script already loaded, triggering DOMContentLoaded'); - setTimeout(() => { - document.dispatchEvent(new Event('DOMContentLoaded')); - }, 100); - return; - } - - // NOW load the Marimo script - it will find our marimo-iframe elements - const script3 = document.createElement('script'); - script3.src = "https://cdn.jsdelivr.net/npm/@marimo-team/marimo-snippets@1"; - script3.type = "text/javascript"; - document.body.appendChild(script3); - - script3.onload = () => { - console.log('Loaded Marimo script!'); - - // The Marimo script listens for DOMContentLoaded, but if that's already fired, - // we need to manually trigger processing - if (document.readyState === 'complete' || document.readyState === 'interactive') { - // DOM is already loaded, manually trigger the event - setTimeout(() => { - console.log('Triggering DOMContentLoaded for Marimo...'); - document.dispatchEvent(new Event('DOMContentLoaded')); - - // Check if it worked - setTimeout(() => { - const frames = document.querySelectorAll('iframe[src*="marimo.app"]'); - if (frames.length > 0) { - console.log(`Success! Marimo created ${frames.length} interactive iframe(s)`); - } else { - console.log('Marimo iframes not created yet. The script may process them on its own timing.'); - } - }, 500); - }, 100); - } - }; - }); - }, 500); // Wait for React to render -} - -// Start the process when DOM is ready -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => { - // DOM is ready but React might not have rendered yet - setTimeout(initializeMarimo, 500); - }); -} else { - // DOM is already loaded, but wait for React - setTimeout(initializeMarimo, 500); -} - // Sync consent categories function wpConsentSync() { if (typeof OnetrustActiveGroups === 'undefined') return; diff --git a/marimo-examples/README.md b/marimo-examples/README.md deleted file mode 100644 index 21a994a3c6..0000000000 --- a/marimo-examples/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Marimo Examples - -This folder contains Marimo notebook code that can be embedded in MDX files. - -## Usage - -In your MDX file, import and use the Marimo component: - -```mdx -import {Marimo} from "/snippets/Marimo.jsx"; - - -``` - -## File Format - -Files should contain markdown-formatted Python code blocks: - -``` -\`\`\`python -import marimo as mo -\`\`\` - -\`\`\`python -# Your code here -\`\`\` -``` - -Each code block becomes a cell in the Marimo notebook. - -## How It Works - -1. The Marimo component fetches the `.txt` file -2. Parses the markdown to extract Python code blocks -3. Creates proper HTML elements (`
`)
-4. Marimo's script processes these and creates an interactive notebook
-
-## Available Examples
-
-- `slider-example.txt` - Interactive slider demo with emoji output
-- `hello-world.txt` - Basic text input example
diff --git a/marimo-examples/hello-world.txt b/marimo-examples/hello-world.txt
deleted file mode 100644
index b1565df29d..0000000000
--- a/marimo-examples/hello-world.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-```python
-import marimo as mo
-```
-
-```python
-mo.md("# Hello, Marimo!")
-```
-
-```python
-name = mo.ui.text(placeholder="Enter your name")
-name
-```
-
-```python
-mo.md(f"Hello, {name.value or 'World'}!")
-```
diff --git a/snippets/Marimo.jsx b/snippets/Marimo.jsx
index 246fc389ed..94d5d1feaf 100644
--- a/snippets/Marimo.jsx
+++ b/snippets/Marimo.jsx
@@ -1,32 +1,190 @@
 /**
- * Marimo component - loads content from external file
- * This completely avoids Mintlify's markdown processing
+ * Marimo component - self-contained with all processing logic
+ * Loads content from external file and creates interactive notebook
  * 
  * Usage:
  * 
  */
-export const Marimo = ({ file, children }) => {
-  // Generate unique ID for this instance
-  const id = 'marimo-' + Date.now() + '-' + Math.floor(Math.random() * 1000);
-  
-  // Load content after mount using data attribute
-  if (file) {
-    return (
-      
- Loading Marimo notebook... -
- ); - } - - // Otherwise render children normally (for backwards compatibility) +export const Marimo = ({ file }) => { + const containerRef = React.useRef(null); + const processedRef = React.useRef(false); + + React.useEffect(() => { + if (!file || typeof window === 'undefined' || processedRef.current) return; + + // Mark as processed to prevent double execution + processedRef.current = true; + + // Initialize global state on window object + if (typeof window !== 'undefined') { + window.__marimoState = window.__marimoState || { + scriptLoaded: false, + scriptLoading: false + }; + } + + // Function to load Marimo script if not already loaded + const loadMarimoScript = () => { + return new Promise((resolve) => { + if (window.__marimoState.scriptLoaded) { + resolve(); + return; + } + + if (window.__marimoState.scriptLoading) { + // Wait for the script to finish loading + const checkInterval = setInterval(() => { + if (window.__marimoState.scriptLoaded) { + clearInterval(checkInterval); + resolve(); + } + }, 100); + return; + } + + window.__marimoState.scriptLoading = true; + + // Add Marimo config + const configScript = document.createElement('script'); + configScript.type = "text/x-marimo-snippets-config"; + configScript.textContent = ` +configureMarimoButtons({title: "Open in a marimo notebook"}); +configureMarimoIframes({height: "400px"}); + `; + document.body.appendChild(configScript); + + // Load Marimo script + const script = document.createElement('script'); + script.src = "https://cdn.jsdelivr.net/npm/@marimo-team/marimo-snippets@1"; + script.type = "text/javascript"; + script.onload = () => { + window.__marimoState.scriptLoaded = true; + window.__marimoState.scriptLoading = false; + console.log('Marimo script loaded successfully from CDN'); + + // Check if the script actually loaded the functions we expect + if (typeof configureMarimoButtons === 'function') { + console.log('Marimo functions are available'); + } else { + console.log('Warning: Marimo functions not found after script load'); + } + + // Trigger DOMContentLoaded for Marimo to process existing elements + setTimeout(() => { + console.log('Triggering initial DOMContentLoaded after script load'); + document.dispatchEvent(new Event('DOMContentLoaded')); + }, 100); + + resolve(); + }; + script.onerror = () => { + window.__marimoState.scriptLoading = false; + console.error('Failed to load Marimo script'); + resolve(); // Resolve anyway to not block + }; + document.body.appendChild(script); + }); + }; + + // Main processing function + const processMarimo = async () => { + try { + // Fetch the content + console.log(`Fetching Marimo content from: ${file}`); + const response = await fetch(file); + + if (!response.ok) { + throw new Error(`Failed to load: ${response.status} ${response.statusText}`); + } + + const content = await response.text(); + console.log(`Fetched ${content.length} characters from ${file}`); + + // Create marimo-iframe element + const marimoIframe = document.createElement('marimo-iframe'); + + // Parse markdown and create HTML elements + const codeBlockRegex = /```python\n([\s\S]*?)\n```/g; + let match; + let blockCount = 0; + + while ((match = codeBlockRegex.exec(content)) !== null) { + const pre = document.createElement('pre'); + const code = document.createElement('code'); + code.className = 'language-python'; + code.textContent = match[1]; + pre.appendChild(code); + marimoIframe.appendChild(pre); + blockCount++; + } + + console.log(`Created marimo-iframe with ${blockCount} code blocks`); + + // Load Marimo script first + await loadMarimoScript(); + + // Clear container and add marimo-iframe + if (containerRef.current) { + // Clear the loading message and remove loading styles + containerRef.current.innerHTML = ''; + containerRef.current.style.padding = ''; + containerRef.current.style.background = ''; + containerRef.current.style.borderRadius = ''; + + // Add the marimo-iframe + containerRef.current.appendChild(marimoIframe); + console.log('Added marimo-iframe to DOM, element exists:', !!document.querySelector('marimo-iframe')); + + // Trigger DOMContentLoaded for this new element + setTimeout(() => { + const marimoElements = document.querySelectorAll('marimo-iframe'); + console.log(`Found ${marimoElements.length} marimo-iframe elements before triggering DOMContentLoaded`); + + document.dispatchEvent(new Event('DOMContentLoaded')); + + // Check if successful + setTimeout(() => { + const frames = document.querySelectorAll('iframe[src*="marimo.app"]'); + if (frames.length > 0) { + console.log(`Success! Marimo created ${frames.length} iframe(s)`); + } else { + console.log('No Marimo iframes created. Checking marimo-iframe elements still exist...'); + const stillExists = document.querySelectorAll('marimo-iframe'); + console.log(`${stillExists.length} marimo-iframe elements still in DOM`); + } + }, 1000); + }, 200); + } + } catch (err) { + console.error('Error processing Marimo:', err); + if (containerRef.current) { + containerRef.current.innerHTML = ` +
+ Failed to load Marimo notebook: ${err.message} +
+ `; + } + } + }; + + // Start processing after a short delay to ensure component is mounted + const timer = setTimeout(processMarimo, 100); + + return () => clearTimeout(timer); + }, [file]); + + // Render container with initial loading message return ( -
- {children} +
+ Loading Marimo notebook...
); }; From 73e3a4a7ba7e27254ba5edb3670b844eda71e361 Mon Sep 17 00:00:00 2001 From: johndmulhausen Date: Tue, 11 Nov 2025 13:21:37 -0500 Subject: [PATCH 10/10] Enhance analytics.js by adding charset attribute and improving consent synchronization This commit updates the analytics.js file to include a charset attribute for the second script tag, ensuring proper character encoding. Additionally, the consent synchronization logic has been refactored for better readability and maintainability, streamlining the handling of consent categories and analytics loading based on user consent. These changes aim to improve the overall functionality and compliance with consent management practices. --- analytics.js | 165 ++++++++++++++++++++++++++------------------------- 1 file changed, 83 insertions(+), 82 deletions(-) diff --git a/analytics.js b/analytics.js index 6513834b16..2dbdedc3d7 100644 --- a/analytics.js +++ b/analytics.js @@ -12,6 +12,7 @@ script1.onload = () => { const script2 = document.createElement('script'); script2.src = "https://cdn.cookielaw.org/consent/29d3f242-6917-42f4-a828-bac6fba2e677/otSDKStub.js"; script2.type = "text/javascript"; +script2.charset = "UTF-8"; script2.setAttribute("data-domain-script", "29d3f242-6917-42f4-a828-bac6fba2e677"); document.head.appendChild(script2); @@ -21,97 +22,97 @@ script2.onload = () => { // Sync consent categories function wpConsentSync() { - if (typeof OnetrustActiveGroups === 'undefined') return; + if (typeof OnetrustActiveGroups === 'undefined') return; - const activeGroups = OnetrustActiveGroups || ''; - const hasGroup = (id) => activeGroups.includes(`,${id},`); - const isOptIn = activeGroups === ',C0001,'; + const activeGroups = OnetrustActiveGroups || ''; + const hasGroup = (id) => activeGroups.includes(`,${id},`); + const isOptIn = activeGroups === ',C0001,'; - window.wp_consent_type = isOptIn ? 'optin' : 'optout'; - document.dispatchEvent(new CustomEvent('wp_consent_type_defined')); + window.wp_consent_type = isOptIn ? 'optin' : 'optout'; + document.dispatchEvent(new CustomEvent('wp_consent_type_defined')); - const consentMap = { - 'statistics': hasGroup('C0002'), - 'statistics-anonymous': hasGroup('C0002'), - 'marketing': hasGroup('C0004') || hasGroup('C0005'), - 'functional': hasGroup('C0001') || hasGroup('C0003'), - 'preferences': false - }; + const consentMap = { + 'statistics': hasGroup('C0002'), + 'statistics-anonymous': hasGroup('C0002'), + 'marketing': hasGroup('C0004') || hasGroup('C0005'), + 'functional': hasGroup('C0001') || hasGroup('C0003'), + 'preferences': false + }; - if (typeof window.wp_set_consent === 'function') { - for (const [category, allowed] of Object.entries(consentMap)) { - window.wp_set_consent(category, allowed ? 'allow' : 'deny'); + if (typeof window.wp_set_consent === 'function') { + for (const [category, allowed] of Object.entries(consentMap)) { + window.wp_set_consent(category, allowed ? 'allow' : 'deny'); + } } - } - // 🚀 Load analytics only if marketing or statistics consent is given - if (consentMap.marketing || consentMap.statistics) { - loadSegment(); - loadClarity(); + // 🚀 Load analytics only if marketing or statistics consent is given + if (consentMap.marketing || consentMap.statistics) { + loadSegment(); + loadClarity(); + } } -} -// Load Microsoft Clarity Analytics -function loadClarity() { - (function(c,l,a,r,i,t,y){ - c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; - t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; - y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); - })(window, document, "clarity", "script", "a6rlxhvc58"); -} + // Load Microsoft Clarity Analytics + function loadClarity() { + (function(c,l,a,r,i,t,y){ + c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; + t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; + y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); + })(window, document, "clarity", "script", "a6rlxhvc58"); + } -// Load Segment Analytics -function loadSegment() { - !function(){ - var analytics = window.analytics = window.analytics || []; - if (!analytics.initialize) - if (analytics.invoked) - window.console && console.error && console.error("Segment snippet included twice."); - else { - analytics.invoked = !0; - analytics.methods = [ - "trackSubmit", "trackClick", "trackLink", "trackForm", "pageview", "identify", - "reset", "group", "track", "ready", "alias", "debug", "page", "once", "off", "on", - "addSourceMiddleware", "addIntegrationMiddleware", "setAnonymousId", "addDestinationMiddleware" - ]; - analytics.factory = function(method){ - return function(){ - if (window.analytics.initialized) return window.analytics[method].apply(window.analytics, arguments); - var args = Array.prototype.slice.call(arguments); - args.unshift(method); - analytics.push(args); - return analytics; + // Load Segment Analytics + function loadSegment() { + !function(){ + var analytics = window.analytics = window.analytics || []; + if (!analytics.initialize) + if (analytics.invoked) + window.console && console.error && console.error("Segment snippet included twice."); + else { + analytics.invoked = !0; + analytics.methods = [ + "trackSubmit", "trackClick", "trackLink", "trackForm", "pageview", "identify", + "reset", "group", "track", "ready", "alias", "debug", "page", "once", "off", "on", + "addSourceMiddleware", "addIntegrationMiddleware", "setAnonymousId", "addDestinationMiddleware" + ]; + analytics.factory = function(method){ + return function(){ + if (window.analytics.initialized) return window.analytics[method].apply(window.analytics, arguments); + var args = Array.prototype.slice.call(arguments); + args.unshift(method); + analytics.push(args); + return analytics; + } + }; + for (var i = 0; i < analytics.methods.length; i++) { + var key = analytics.methods[i]; + analytics[key] = analytics.factory(key); } - }; - for (var i = 0; i < analytics.methods.length; i++) { - var key = analytics.methods[i]; - analytics[key] = analytics.factory(key); - } - analytics.load = function(writeKey, options){ - var script = document.createElement("script"); - script.type = "text/javascript"; - script.async = true; - script.src = "https://wandb.ai/sa-docs.min.js"; // <-- Your self-hosted Segment build - var first = document.getElementsByTagName("script")[0]; - first.parentNode.insertBefore(script, first); - analytics._loadOptions = options; - }; - analytics._writeKey = "NYcqWZ8sgOCplYnItFyBaZ5ZRClWlVgl"; // <-- Your real write key - analytics.SNIPPET_VERSION = "4.16.1"; - analytics.load("NYcqWZ8sgOCplYnItFyBaZ5ZRClWlVgl"); + analytics.load = function(writeKey, options){ + var script = document.createElement("script"); + script.type = "text/javascript"; + script.async = true; + script.src = "https://wandb.ai/sa-docs.min.js"; // <-- Your self-hosted Segment build + var first = document.getElementsByTagName("script")[0]; + first.parentNode.insertBefore(script, first); + analytics._loadOptions = options; + }; + analytics._writeKey = "NYcqWZ8sgOCplYnItFyBaZ5ZRClWlVgl"; // <-- Your real write key + analytics.SNIPPET_VERSION = "4.16.1"; + analytics.load("NYcqWZ8sgOCplYnItFyBaZ5ZRClWlVgl"); - // 🚀 Fire analytics.page() when ready - analytics.ready(function() { - analytics.page(); - }); - } - }(); -} - -// Main hook called automatically by OneTrust -function OptanonWrapper() { - wpConsentSync(); - if (typeof Optanon !== 'undefined' && typeof Optanon.ShowBanner === 'function') { - Optanon.ShowBanner(); + // 🚀 Fire analytics.page() when ready + analytics.ready(function() { + analytics.page(); + }); + } + }(); } -} \ No newline at end of file + + // Main hook called automatically by OneTrust + function OptanonWrapper() { + wpConsentSync(); + if (typeof Optanon !== 'undefined' && typeof Optanon.ShowBanner === 'function') { + Optanon.ShowBanner(); + } + } \ No newline at end of file