pondpilot-widget
Version:
Transform static SQL code blocks into interactive snippets powered by DuckDB WASM
7 lines • 25.1 kB
JavaScript
/**
* PondPilot Widget v1.0.0
* Transform static SQL code blocks into interactive snippets
* https://github.com/pondpilot/pondpilot-widget
* (c) 2025 PondPilot - MIT License
*/
!function(){"use strict";const t=function(){const t=["SELECT","FROM","WHERE","AND","OR","NOT","IN","EXISTS","BETWEEN","LIKE","IS","NULL","ORDER","BY","GROUP","HAVING","UNION","ALL","LIMIT","OFFSET","FETCH","FIRST","NEXT","ONLY","ROWS","INSERT","INTO","VALUES","UPDATE","SET","DELETE","CREATE","ALTER","DROP","TABLE","INDEX","VIEW","TRIGGER","FUNCTION","PROCEDURE","DATABASE","SCHEMA","PRIMARY","KEY","FOREIGN","REFERENCES","UNIQUE","CHECK","DEFAULT","CONSTRAINT","CASCADE","RESTRICT","IF","CASE","WHEN","THEN","ELSE","END","JOIN","INNER","LEFT","RIGHT","FULL","OUTER","CROSS","ON","AS","DISTINCT","WITH","RECURSIVE","CAST","CONVERT","COALESCE","NULLIF","GREATEST","LEAST","COUNT","SUM","AVG","MIN","MAX","ROUND","FLOOR","CEIL","ABS","SIGN","MOD","SQRT","POWER","EXP","LOG","LN","CONCAT","LENGTH","SUBSTRING","REPLACE","TRIM","UPPER","LOWER","CURRENT_DATE","CURRENT_TIME","CURRENT_TIMESTAMP","EXTRACT","DATE_ADD","DATE_SUB","DATEDIFF","NOW","CURDATE","CURTIME"];function e(e){const n=[],o=e.length;let i=0;for(;i<o;)if(/\s/.test(e[i])){let t=i;for(;t<o&&/\s/.test(e[t]);)t++;n.push({name:"whitespace",content:e.slice(i,t)}),i=t}else if("-"!==e[i]||"-"!==e[i+1])if("/"!==e[i]||"*"!==e[i+1])if("'"!==e[i]&&'"'!==e[i])if(/\d/.test(e[i])){let t=i;for(;t<o&&/[\d.]/.test(e[t]);)t++;n.push({name:"number",content:e.slice(i,t)}),i=t}else if(/[a-zA-Z_]/.test(e[i])){let r=i;for(;r<o&&/[a-zA-Z0-9_]/.test(e[r]);)r++;const s=e.slice(i,r),l=s.toUpperCase();t.includes(l)?n.push({name:"keyword",content:s}):n.push({name:"identifier",content:s}),i=r}else if(/[(),.;=<>!+\-*/]/.test(e[i]))n.push({name:"special",content:e[i]}),i++;else if("`"!==e[i])n.push({name:"other",content:e[i]}),i++;else{let t=i+1;for(;t<o&&"`"!==e[t];)t++;t<o&&t++,n.push({name:"identifier",content:e.slice(i,t)}),i=t}else{const t=e[i];let r=i+1;for(;r<o&&e[r]!==t;)"\\"===e[r]&&r++,r++;r<o&&r++,n.push({name:"string",content:e.slice(i,r)}),i=r}else{let t=i+2;for(;t<o-1&&("*"!==e[t]||"/"!==e[t+1]);)t++;t<o-1&&(t+=2),n.push({name:"comment",content:e.slice(i,t)}),i=t}else{let t=i+2;for(;t<o&&"\n"!==e[t];)t++;n.push({name:"comment",content:e.slice(i,t)}),i=t}return n}return{highlight:function(t,n={}){const o=e(t);return n.html?o.map((t=>{const e="sql-hl-"+t.name,n=t.content.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");return"whitespace"===t.name?n:`<span class="${e}">${n}</span>`})).join(""):t},getSegments:e}}(),e=150,n=500,o=300,i=200,r={MODULE_LOADING:10,FETCHING_BUNDLES:20,SELECTING_BUNDLE:30,CREATING_WORKER:40,INITIALIZING_DB:60,LOADING_MODULE:80,CREATING_CONNECTION:90,COMPLETE:100},s={selector:"pre.pondpilot-snippet, .pondpilot-snippet pre",baseUrl:window.PONDPILOT_BASE_URL||"https://app.pondpilot.io",theme:"light",autoInit:!0,duckdbVersion:"1.29.1-dev68.0",duckdbCDN:"https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm"};let l=null,a=null,d=null;const p=`\n .pondpilot-widget {\n position: relative;\n margin: 1em 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: #f8f9fa;\n border-radius: 6px;\n overflow: hidden;\n }\n\n .pondpilot-widget.dark {\n background: #1a1b26;\n }\n\n /* Button container for better layout control */\n .pondpilot-button-container {\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 10;\n display: flex;\n gap: 4px;\n pointer-events: none;\n }\n\n .pondpilot-button-container > * {\n pointer-events: auto;\n }\n\n /* Minimal floating run button */\n .pondpilot-run-button {\n position: static;\n padding: 4px 12px;\n background: rgba(59, 130, 246, 0.9);\n color: white;\n border: none;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n backdrop-filter: blur(8px);\n white-space: nowrap;\n }\n\n .pondpilot-run-button:hover {\n background: rgba(37, 99, 235, 0.95);\n transform: translateY(-1px);\n }\n\n .pondpilot-run-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* Reset button in editor */\n .pondpilot-reset-button {\n position: static;\n padding: 4px 8px;\n background: rgba(107, 114, 128, 0.1);\n color: #6b7280;\n border: none;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n opacity: 0;\n visibility: hidden;\n white-space: nowrap;\n }\n\n .pondpilot-widget:hover .pondpilot-reset-button.show,\n .pondpilot-reset-button.show:hover {\n opacity: 1;\n visibility: visible;\n }\n\n .pondpilot-reset-button:hover {\n background: rgba(107, 114, 128, 0.2);\n color: #374151;\n }\n\n .pondpilot-widget.dark .pondpilot-reset-button:hover {\n background: rgba(255, 255, 255, 0.1);\n color: #e5e7eb;\n }\n\n /* Clean editor */\n .pondpilot-editor {\n position: relative;\n background: transparent;\n min-height: 60px;\n }\n\n .pondpilot-editor pre {\n margin: 0;\n padding: 16px;\n padding-right: 90px;\n background: transparent;\n border: none;\n font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;\n font-size: 13px;\n line-height: 1.6;\n color: #24292e;\n white-space: pre-wrap;\n word-wrap: break-word;\n tab-size: 2;\n }\n\n /* Responsive button spacing for smaller screens */\n @media (max-width: 480px) {\n .pondpilot-editor pre {\n padding-right: 16px;\n padding-top: 48px;\n }\n \n .pondpilot-button-container {\n top: 8px;\n right: 8px;\n left: 8px;\n justify-content: flex-end;\n flex-wrap: wrap;\n }\n }\n\n .pondpilot-widget.dark .pondpilot-editor pre {\n color: #e1e4e8;\n }\n\n .pondpilot-editor[contenteditable="true"] {\n outline: none;\n }\n\n .pondpilot-editor[contenteditable="true"]:focus-within {\n background: rgba(59, 130, 246, 0.05);\n }\n\n /* Subtle output */\n .pondpilot-output {\n background: rgba(0, 0, 0, 0.02);\n border-top: 1px solid rgba(0, 0, 0, 0.06);\n max-height: ${o}px;\n overflow: auto;\n display: none;\n font-size: 12px;\n }\n\n .pondpilot-widget.dark .pondpilot-output {\n background: rgba(255, 255, 255, 0.02);\n border-top-color: rgba(255, 255, 255, 0.06);\n }\n\n .pondpilot-output.show {\n display: block;\n }\n\n .pondpilot-output-content {\n padding: 16px;\n }\n\n /* Clean tables */\n .pondpilot-output table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n }\n\n .pondpilot-output th,\n .pondpilot-output td {\n text-align: left;\n padding: 8px 12px;\n border-bottom: 1px solid rgba(0, 0, 0, 0.06);\n }\n\n .pondpilot-widget.dark .pondpilot-output th,\n .pondpilot-widget.dark .pondpilot-output td {\n border-bottom-color: rgba(255, 255, 255, 0.06);\n }\n\n .pondpilot-output th {\n font-weight: 600;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: #6b7280;\n }\n\n .pondpilot-widget.dark .pondpilot-output th {\n color: #9ca3af;\n }\n\n .pondpilot-output td {\n color: #1f2937;\n }\n\n .pondpilot-widget.dark .pondpilot-output td {\n color: #e5e7eb;\n }\n\n /* Minimal error */\n .pondpilot-error {\n color: #dc2626;\n padding: 16px;\n font-size: 12px;\n font-family: monospace;\n }\n\n /* Simple loading */\n .pondpilot-loading {\n text-align: center;\n padding: 24px;\n color: #6b7280;\n font-size: 12px;\n }\n\n /* Loading progress */\n .pondpilot-progress {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 24px;\n }\n\n .pondpilot-progress-bar {\n width: 200px;\n height: 4px;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 2px;\n overflow: hidden;\n }\n\n .pondpilot-widget.dark .pondpilot-progress-bar {\n background: rgba(255, 255, 255, 0.1);\n }\n\n .pondpilot-progress-fill {\n height: 100%;\n background: #3b82f6;\n border-radius: 2px;\n transition: width 0.3s ease;\n }\n\n .pondpilot-progress-text {\n font-size: 12px;\n color: #6b7280;\n }\n\n /* Results footer */\n .pondpilot-results-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid rgba(0, 0, 0, 0.06);\n }\n\n .pondpilot-widget.dark .pondpilot-results-footer {\n border-top-color: rgba(255, 255, 255, 0.06);\n }\n\n .pondpilot-results-info {\n font-size: 11px;\n color: #6b7280;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n /* Duck logo watermark */\n .pondpilot-duck {\n position: absolute;\n bottom: 8px;\n right: 8px;\n width: 20px;\n height: 16px;\n opacity: 0.1;\n transition: opacity 0.2s;\n cursor: pointer;\n z-index: 5;\n color: inherit;\n display: block;\n }\n\n .pondpilot-widget:hover .pondpilot-duck {\n opacity: 0.2;\n }\n\n .pondpilot-duck:hover {\n opacity: 0.3;\n transform: scale(1.1);\n }\n\n /* Subtle branding */\n .pondpilot-powered {\n position: absolute;\n bottom: 4px;\n right: 32px;\n font-size: 10px;\n color: #9ca3af;\n opacity: 0;\n transition: opacity 0.2s;\n }\n\n .pondpilot-widget:hover .pondpilot-powered {\n opacity: 1;\n }\n\n .pondpilot-powered a {\n color: inherit;\n text-decoration: none;\n }\n\n .pondpilot-powered a:hover {\n color: #3b82f6;\n }\n\n /* SQL syntax highlighting */\n .sql-hl-keyword {\n color: #0969da;\n font-weight: 600;\n }\n\n .pondpilot-widget.dark .sql-hl-keyword {\n color: #7ee787;\n }\n\n .sql-hl-string {\n color: #032f62;\n }\n\n .pondpilot-widget.dark .sql-hl-string {\n color: #a5d6ff;\n }\n\n .sql-hl-number {\n color: #0550ae;\n }\n\n .pondpilot-widget.dark .sql-hl-number {\n color: #79c0ff;\n }\n\n .sql-hl-comment {\n color: #6e7781;\n font-style: italic;\n }\n\n .pondpilot-widget.dark .sql-hl-comment {\n color: #8b949e;\n }\n\n .sql-hl-special {\n color: #cf222e;\n }\n\n .pondpilot-widget.dark .sql-hl-special {\n color: #ff7b72;\n }\n\n .sql-hl-identifier {\n color: #953800;\n }\n\n .pondpilot-widget.dark .sql-hl-identifier {\n color: #ffa657;\n }\n `;function c(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}class u{constructor(t,e={}){this.element=t,this.options={...s,...e},this.originalCode=this.extractCode(),this.init()}extractCode(){const t="PRE"===this.element.tagName?this.element:this.element.querySelector("pre");return(t.querySelector("code")||t).textContent.trim()}init(){this.widget=document.createElement("div"),this.widget.className=`pondpilot-widget ${this.options.theme}`,this.editor=this.createEditor(),this.widget.appendChild(this.editor),this.buttonContainer=this.createButtonContainer(),this.widget.appendChild(this.buttonContainer),this.output=this.createOutput(),this.widget.appendChild(this.output),!1!==this.options.showPoweredBy&&(this.poweredBy=this.createPoweredBy(),this.widget.appendChild(this.poweredBy),this.duck=this.createDuckLogo(),this.widget.appendChild(this.duck)),this.element.parentNode.replaceChild(this.widget,this.element),this.duckdbReady=!1}createButtonContainer(){const t=document.createElement("div");return t.className="pondpilot-button-container",this.resetButton=this.createResetButton(),t.appendChild(this.resetButton),this.runButton=this.createRunButton(),t.appendChild(this.runButton),t}createRunButton(){const t=document.createElement("button");return t.className="pondpilot-run-button",t.textContent="Run",t.setAttribute("aria-label","Run SQL query"),t.onclick=()=>this.run(),t}createResetButton(){const t=document.createElement("button");return t.className="pondpilot-reset-button",t.textContent="Reset",t.setAttribute("aria-label","Reset to original SQL"),t.onclick=()=>this.reset(),t}getCursorOffset(t,e){const n=e.cloneRange();return n.selectNodeContents(t),n.setEnd(e.endContainer,e.endOffset),n.toString().length}setCursorOffset(t,e){const n=this.getTextNodes(t);let o=0;for(const t of n){const n=t.textContent.length;if(o+n>=e){const n=document.createRange();n.setStart(t,e-o),n.collapse(!0);const i=window.getSelection();i.removeAllRanges(),i.addRange(n);break}o+=n}}getTextNodes(t){const e=[],n=document.createTreeWalker(t,NodeFilter.SHOW_TEXT,null,!1);let o;for(;o=n.nextNode();)e.push(o);return e}createEditor(){const o=document.createElement("div");o.className="pondpilot-editor";const i=document.createElement("pre");if(i.innerHTML=t.highlight(this.originalCode,{html:!0}),o.appendChild(i),this.currentCode=this.originalCode,!1!==this.options.editable){i.contentEditable=!0,i.spellcheck=!1,i.setAttribute("role","textbox"),i.setAttribute("aria-label","SQL editor"),i.setAttribute("aria-multiline","true");const o=function(t,e){let n;return function(...o){clearTimeout(n),n=setTimeout((()=>{clearTimeout(n),t(...o)}),e)}}(((e,n)=>{i.innerHTML=t.highlight(e,{html:!0}),this.setCursorOffset(i,n),e!==this.originalCode?this.resetButton.classList.add("show"):this.resetButton.classList.remove("show")}),e);i.addEventListener("input",(()=>{const e=i.textContent;this.currentCode=e;const r=window.getSelection().getRangeAt(0),s=this.getCursorOffset(i,r);e.length<n?(i.innerHTML=t.highlight(e,{html:!0}),this.setCursorOffset(i,s),e!==this.originalCode?this.resetButton.classList.add("show"):this.resetButton.classList.remove("show")):o(e,s)}))}return o.addEventListener("keydown",(t=>{(t.ctrlKey||t.metaKey)&&"Enter"===t.key&&(t.preventDefault(),this.run())})),o}createOutput(){const t=document.createElement("div");t.className="pondpilot-output";const e=document.createElement("div");return e.className="pondpilot-output-content",t.appendChild(e),t}createPoweredBy(){const t=document.createElement("div");t.className="pondpilot-powered";const e=document.createElement("a");let n=this.options.baseUrl;try{const t=new URL(n);"http:"!==t.protocol&&"https:"!==t.protocol&&(n=s.baseUrl)}catch(t){n=s.baseUrl}return e.href=n,e.target="_blank",e.rel="noopener",e.textContent="PondPilot",t.appendChild(e),t}createDuckLogo(){const t=document.createElement("a");let e=this.options.baseUrl;try{const t=new URL(e);"http:"!==t.protocol&&"https:"!==t.protocol&&(e=s.baseUrl)}catch(t){e=s.baseUrl}return t.href=e,t.target="_blank",t.rel="noopener",t.className="pondpilot-duck",t.title="Open in PondPilot",t.innerHTML='<svg viewBox="0 0 51 42" fill="currentColor" xmlns="http://www.w3.org/2000/svg">\n <path d="M13.5 42C6.04416 42 3.25905e-07 35.9558 0 28.5C-3.25905e-07 21.0442 6.04415 15 13.5 15H25.5C32.9558 15 39 21.0442 39 28.5C39 35.9558 32.9558 42 25.5 42H13.5Z" fill-opacity="0.32"/>\n <path d="M31.5 27C24.0442 27 18 20.9558 18 13.5C18 6.04416 24.0442 3.25905e-07 31.5 0C38.9558 -3.25905e-07 45 6.04416 45 13.5C45 20.9558 38.9558 27 31.5 27Z"/>\n <path d="M43.5 15C44.3284 15 45 14.3284 45 13.5C45 12.6716 44.3284 12 43.5 12C42.6716 12 42 12.6716 42 13.5C42 14.3284 42.6716 15 43.5 15Z"/>\n <path d="M31.5 15C32.3284 15 33 14.3284 33 13.5C33 12.6716 32.3284 12 31.5 12C30.6716 12 30 12.6716 30 13.5C30 14.3284 30.6716 15 31.5 15Z"/>\n <path d="M37.5 24C35.0147 24 33 21.9853 33 19.5C33 17.0147 35.0147 15 37.5 15L46.5 15C48.9853 15 51 17.0147 51 19.5C51 21.9853 48.9853 24 46.5 24H37.5Z"/>\n <path d="M30.8908 28.971C30.7628 30.9568 29.94 32.9063 28.4223 34.424C25.1074 37.7388 19.733 37.7388 16.4181 34.424L10.418 28.4238L16.4179 22.4239C19.7327 19.1091 25.1072 19.1091 28.422 22.4239C30.2181 24.22 31.0411 26.6208 30.8908 28.971Z" fill-opacity="0.32"/>\n </svg>',t}async processRelativeParquetPaths(t){this.registeredFiles||(this.registeredFiles=new Set);let e=t;const n=[...t.matchAll(/FROM\s+['"]?([^'";\s]+\.parquet)['"]?/gi)];for(const t of n){const e=t[1];if(!e.startsWith("http://")&&!e.startsWith("https://")&&!this.registeredFiles.has(e)){const t=this.resolveRelativePath(e);try{await l.registerFileURL(e,t,d.DuckDBDataProtocol.HTTP,!1),this.registeredFiles.add(e)}catch(t){console.error(`Failed to register file ${e}:`,t)}}}return e}resolveRelativePath(t){const e=window.location.href;try{if(e.startsWith("file://")){const e=window.location.hostname||"localhost",n=`http://${e}:${window.location.port||"8080"}/`;return new URL(t,n).href}return new URL(t,e).href}catch(n){const o=t.replace(/^\/+/,"");if("localhost"===window.location.hostname||"127.0.0.1"===window.location.hostname){const t="file:"===window.location.protocol?"http:":window.location.protocol,e=window.location.port||"8080";return`${t}//${window.location.hostname}:${e}/${o}`}return e.substring(0,e.lastIndexOf("/")+1)+o}}async initDuckDB(){try{this.runButton.textContent="Loading...",this.runButton.disabled=!0,this.widget.setAttribute("aria-busy","true"),this.showProgress("Initializing DuckDB...",0),a||(a=this.createSharedDuckDB(((t,e)=>{this.showProgress(e,t)}))),await a,this.showProgress("Creating connection...",r.CREATING_CONNECTION),this.conn=await l.connect(),this.duckdbReady=!0,this.runButton.textContent="Run",this.runButton.disabled=!1,this.output.classList.remove("show"),this.widget.setAttribute("aria-busy","false")}catch(t){console.error("Failed to initialize DuckDB:",t),this.runButton.textContent="Error",this.showError("Failed to initialize DuckDB: "+t.message),this.widget.setAttribute("aria-busy","false")}}showProgress(t,e){const n=this.output.querySelector(".pondpilot-output-content"),o=Math.min(100,Math.max(0,e));n.innerHTML=`\n <div class="pondpilot-progress" role="status" aria-live="polite">\n <div class="pondpilot-progress-text">${c(t)}</div>\n <div class="pondpilot-progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${o}" aria-label="Loading progress">\n <div class="pondpilot-progress-fill" style="width: ${o}%"></div>\n </div>\n </div>\n `,this.output.classList.add("show")}async createSharedDuckDB(t){try{this.progressCallback=t||(()=>{}),this.progressCallback(r.MODULE_LOADING,"Loading DuckDB module...");const e=`${s.duckdbCDN}@${s.duckdbVersion}/+esm`;d=await import(e);const duckdb=d;this.progressCallback(r.FETCHING_BUNDLES,"Fetching bundles...");const n=duckdb.getJsDelivrBundles();this.progressCallback(r.SELECTING_BUNDLE,"Selecting best bundle...");const o=await duckdb.selectBundle(n);let i;this.progressCallback(r.CREATING_WORKER,"Creating worker...");try{i=new Worker(o.mainWorker)}catch(t){try{const t=URL.createObjectURL(new Blob([`importScripts("${o.mainWorker}");`],{type:"text/javascript"}));i=new Worker(t)}catch(t){throw new Error("Failed to create worker. This may be due to Content Security Policy restrictions. Please ensure your site allows worker-src 'self' blob: or use a CSP-compatible hosting setup.")}}const a=new duckdb.ConsoleLogger(duckdb.LogLevel.WARNING);return this.progressCallback(r.INITIALIZING_DB,"Initializing database..."),l=new duckdb.AsyncDuckDB(a,i),this.progressCallback(r.LOADING_MODULE,"Loading PondPilot module..."),await l.instantiate(o.mainModule,o.pthreadWorker),l}catch(t){throw t}}async run(){const t=this.currentCode||this.editor.querySelector("pre").textContent.trim();if(!t)return;if(!this.duckdbReady&&(await this.initDuckDB(),!this.duckdbReady))return;this.runButton.disabled=!0,this.runButton.textContent="Running...",this.output.classList.add("show"),this.widget.setAttribute("aria-busy","true");this.output.querySelector(".pondpilot-output-content").innerHTML='<div class="pondpilot-loading">Running query...</div>';const e=performance.now();try{const n=await this.processRelativeParquetPaths(t),o=performance.now(),r=await this.conn.query(n),s=Math.round(performance.now()-o),l=r.toArray().map((t=>t.toJSON())),a=performance.now()-e,d=Math.max(0,i-a);d>0&&await new Promise((t=>setTimeout(t,d))),this.displayResults(l,s),this.runButton.textContent="Run"}catch(t){const n=performance.now()-e,o=Math.max(0,i-n);o>0&&await new Promise((t=>setTimeout(t,o))),this.showError(t.message),this.runButton.textContent="Run"}finally{this.runButton.disabled=!1,this.widget.setAttribute("aria-busy","false")}}displayResults(t,e){const n=this.output.querySelector(".pondpilot-output-content");if(0===t.length)return void(n.innerHTML='<div style="text-align: center; color: #6b7280;">No results</div>');const o=document.createElement("table"),i=document.createElement("thead"),r=document.createElement("tr");Object.keys(t[0]).forEach((t=>{const e=document.createElement("th");e.textContent=t,r.appendChild(e)})),i.appendChild(r),o.appendChild(i);const s=document.createElement("tbody");t.forEach((t=>{const e=document.createElement("tr");Object.values(t).forEach((t=>{const n=document.createElement("td");n.textContent=null===t?"null":String(t),e.appendChild(n)})),s.appendChild(e)})),o.appendChild(s),n.innerHTML="",n.appendChild(o);const l=document.createElement("div");l.className="pondpilot-results-footer";const a=document.createElement("div");a.className="pondpilot-results-info",a.textContent=`${t.length} rows • ${e}ms`,l.appendChild(a),n.appendChild(l),this.resetButton.classList.add("show")}reset(){this.editor.querySelector("pre").innerHTML=t.highlight(this.originalCode,{html:!0}),this.output.classList.remove("show"),this.runButton.textContent="Run",this.resetButton.classList.remove("show"),this.currentCode=this.originalCode}showError(t){const e=this.output.querySelector(".pondpilot-output-content");let n=t,o="";t.includes("no such table")||t.includes("does not exist")?o="Tip: Make sure to CREATE TABLE before querying it.":t.includes("syntax error")||t.includes("Parser Error")?o="Tip: Check your SQL syntax. Common issues: missing semicolon, typos in keywords.":t.includes("no such column")?o="Tip: Check column names for typos and ensure they exist in the table.":t.includes("SharedArrayBuffer")||t.includes("COOP")||t.includes("COEP")?(n="Browser security error",o="Your browser requires special headers for DuckDB WASM. Try using Chrome or Firefox, or contact your site administrator."):t.includes("Out of Memory")?(n="Memory limit exceeded",o="Tip: Try using LIMIT to reduce result size, or process data in smaller chunks."):t.includes("No files found that match the pattern")?(n="File not found",o="Tip: For relative paths, ensure the file is accessible via HTTP from the same server. Try using a full URL like 'http://localhost:8080/file.parquet'."):t.includes("HTTP")&&t.includes("404")&&(n="File not found (404)",o="Tip: The file could not be loaded from the resolved URL. Check that the file exists and is served by your web server."),e.innerHTML=`\n <div class="pondpilot-error" role="alert" aria-live="assertive">\n <div>${c(n)}</div>\n ${o?`<div style="margin-top: 8px; opacity: 0.8; font-size: 11px;">${c(o)}</div>`:""}\n </div>\n `,this.output.classList.add("show"),this.resetButton.classList.add("show")}async cleanup(){this.conn&&(await this.conn.close(),this.conn=null),this.highlightDebounced&&(this.highlightDebounced=null)}destroy(){this.cleanup().catch(console.error),h.delete(this.widget),this.widget=null,this.editor=null,this.output=null,this.runButton=null,this.resetButton=null}}const h=new WeakMap;function g(){if(!document.getElementById("pondpilot-widget-styles")){const t=document.createElement("style");t.id="pondpilot-widget-styles",t.textContent=p,document.head.appendChild(t)}document.querySelectorAll(s.selector).forEach((t=>{if(!t.dataset.pondpilotWidget){t.dataset.pondpilotWidget="true";const e=new u(t);h.set(e.widget,e)}}))}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",g):g();const m=new MutationObserver((t=>{t.forEach((t=>{t.removedNodes.forEach((t=>{if(t.nodeType===Node.ELEMENT_NODE){(t.classList?.contains("pondpilot-widget")?[t]:t.querySelectorAll?.(".pondpilot-widget")||[]).forEach((t=>{const e=h.get(t);e&&e.destroy()}))}}))}))}));document.body?m.observe(document.body,{childList:!0,subtree:!0}):document.addEventListener("DOMContentLoaded",(()=>{m.observe(document.body,{childList:!0,subtree:!0})})),window.PondPilot={init:g,Widget:u,config:s,destroy:()=>{document.querySelectorAll(".pondpilot-widget").forEach((t=>{const e=h.get(t);e&&e.destroy()})),m.disconnect()}}}();