1
Fork 0

swap codapi out for htmx

This commit is contained in:
Jan-Erik Rediger 2024-06-03 20:31:01 +02:00
parent 0d379f956c
commit feef2336d6
10 changed files with 169 additions and 106 deletions

View file

@ -220,20 +220,17 @@ fn handle_cors(serv_req: *http::server_request) void = {
)!;
};
fn json_wrap(res: (*json::value | void)) (*json::value | badrequest) = {
return match (res) {
case let payload: *json::value => return payload;
case void => return badrequest;
};
};
fn handle_exec(buf: *io::stream, serv_req: *http::server_request) (void | badrequest) = {
let request = serv_req.request;
let payload = match (request.body) {
case void => return badrequest;
case let body: io::handle =>
let payload = json::load(body)!;
let payload = match (json::load(body)) {
case let j: json::value => yield j;
case let err: json::error =>
return badrequest;
};
yield payload;
};
@ -244,15 +241,11 @@ fn handle_exec(buf: *io::stream, serv_req: *http::server_request) (void | badreq
case => return badrequest;
};
let files = json_wrap(json::get(&payload, "files"))?;
let files = match (*files) {
case let o: json::object => yield o;
case => return badrequest;
let code = match (json::get(&payload, "code")) {
case let payload: *json::value => yield payload;
case void => return badrequest;
};
let code = json_wrap(json::get(&files, ""))?;
let code = match (*code) {
case let c: str => yield c;
case => return badrequest;

View file

@ -1,4 +1,19 @@
import {CodeJar} from "./codejar.js"
import hljs from "./highlight.min.js"
import "./htmx.js"
htmx.defineExtension('json-enc', {
onEvent: function (name, evt) {
if (name === "htmx:configRequest") {
evt.detail.headers['Content-Type'] = "application/json";
}
},
encodeParameters : function(xhr, parameters, elt) {
xhr.overrideMimeType('text/json');
return (JSON.stringify(parameters));
}
});
function highlight(editor) {
editor.textContent = editor.textContent;
@ -47,9 +62,7 @@ if (location.hash) {
jar.updateCode(defaultCode);
}
let shareLink = document.createElement("a");
shareLink.href = "#";
shareLink.onclick = (e) => {
document.getElementById("share-button").onclick = (e) => {
e.preventDefault();
let code = document.querySelector('.editor').innerText;
location.hash = base64Encode(code);
@ -57,16 +70,44 @@ shareLink.onclick = (e) => {
if (navigator.clipboard) {
let text = location.href;
navigator.clipboard.writeText(text).then(function() {
document.querySelector("codapi-status").innerText = "✓ Link copied!"
document.getElementById("status").innerText = "✓ Link copied!"
}, function(err) {
document.querySelector("codapi-status").innerText = "✓ Link changed. Copy the URL now."
document.getElementById("status").innerText = "✓ Link changed. Copy the URL now."
});
} else {
document.querySelector("codapi-status").innerText = "✓ Link changed. Copy the URL now."
document.getElementById("status").innerText = "✓ Link changed. Copy the URL now."
}
};
let shareText = document.createTextNode("Share");
shareLink.appendChild(shareText);
let editLink = document.querySelector("codapi-toolbar a");
editLink.insertAdjacentElement('afterend', shareLink);
window.getCode = function getCode() {
return jar.toString();
}
let statusElem = document.getElementById("status");
document.body.addEventListener('htmx:beforeRequest', (event) => {
let elem = document.getElementById("htmx-indicator");
elem.style.display = "inline";
});
document.body.addEventListener('htmx:afterRequest', (event) => {
let elem = document.getElementById("htmx-indicator");
elem.style.display = "none";
});
document.body.addEventListener('htmx:beforeSwap', (event) => {
let detail = event.detail;
if (detail.isError) {
statusElem.innerText = "✘ Failed";
return;
}
detail.shouldSwap = false;
statusElem.innerText = "✓ Done";
let x = detail.xhr;
let resp = JSON.parse(x.response);
let output = document.querySelector(".code-output code.stderr");
output.innerText = resp.stderr;
output = document.querySelector(".code-output code.stdout");
output.innerText = resp.stdout;
});

View file

@ -1 +0,0 @@
(()=>{var e=window.codapi??{};e.version="0.19.0";e.engines=e.engines??{};e.settings=e.settings??{};window.codapi=e;var t=class extends HTMLElement{connectedCallback(){this.ready||(e.settings.url=this.getAttribute("url"),this.ready=!0)}attributeChangedCallback(n,d,s){e.settings[n]=s}};window.customElements.get("codapi-settings")||customElements.define("codapi-settings",t);})();

View file

@ -1,29 +0,0 @@
codapi-snippet{
--toolbar-gap: 1em;
--toolbar-margin-bottom: 1em;
display:block
}
codapi-toolbar{display:flex;
flex-wrap:wrap;
align-items:center;
gap:var(--toolbar-gap);
margin-bottom:var(--toolbar-margin-bottom)
}
codapi-status{display:block;
white-space:nowrap
}
codapi-output{position:relative;
display:block
}
codapi-output[hidden]{display:none
}
codapi-output a[href="#close"]{position:absolute;
top:.25em;
right:.25em;
display:inline-block;
width:1.5em;
height:1.5em;
line-height:1.5em;
text-align:center
}

View file

@ -1,28 +0,0 @@
(()=>{var B=Object.defineProperty;var U=(n,t,e)=>t in n?B(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var L=(n,t,e)=>(U(n,typeof t!="symbol"?t+"":t,e),e);var W=/^([^:]+):(@?\S+)$/,w=class n{constructor(t,e,s){this.title=t,this.type=e,this.value=s}static parse(t){let e=W.exec(t);if(!e)return null;let s=e[1].replaceAll("_"," "),i=e[2][0]=="@"?"event":"command",o=e[2][0]=="@"?e[2].slice(1):e[2];return new n(s,i,o)}};var _=document.createElement("template");_.innerHTML=`
<button hidden>Run</button>
<a href="#edit" hidden>Edit</a>
<codapi-status></codapi-status>
`;var A=class extends HTMLElement{constructor(){super()}connectedCallback(){this.ready||(this.render(),this.listen(),this.ready=!0)}render(){this.appendChild(_.content.cloneNode(!0)),this.run=this.querySelector("button"),this.edit=this.querySelector("a"),this.status=this.querySelector("codapi-status");let t=this.getAttribute("actions");this.addActions(t?t.split(" "):null)}listen(){this.run.addEventListener("click",t=>{this.dispatchEvent(new Event("run"))}),this.edit.addEventListener("click",t=>{t.preventDefault(),this.dispatchEvent(new Event("edit"))})}addActions(t){if(t)for(let e of t){let s=w.parse(e);if(!s)continue;let i=this.createButton(s);this.insertBefore(i,this.status)}}createButton(t){let e=document.createElement("a");return e.addEventListener("click",s=>{s.preventDefault();let i=new CustomEvent(t.type,{detail:t.value});this.dispatchEvent(i)}),e.innerText=t.title,e.href="#"+t.value,e}showRunning(){this.run.setAttribute("disabled",""),this.status.showRunning()}showFinished(t){this.run.removeAttribute("disabled"),this.status.showFinished(t)}showStatus(t){this.run.removeAttribute("disabled"),this.status.showMessage(t)}get runnable(){return!this.run.hasAttribute("hidden")}set runnable(t){t?this.run.removeAttribute("hidden"):this.run.setAttribute("hidden","")}get editable(){return!this.edit.hasAttribute("hidden")}set editable(t){t?this.edit.removeAttribute("hidden"):this.edit.setAttribute("hidden","")}};window.customElements.get("codapi-toolbar")||customElements.define("codapi-toolbar",A);var T={running:"Running...",failed:"\u2718 Failed",done:"\u2713 Done"},k=class extends HTMLElement{showRunning(){let t=this.getAttribute("running")||T.running;this.innerHTML=`
<svg width="1em" height="1em" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z"><animateTransform attributeName="transform" type="rotate" dur="0.75s" values="0 12 12;360 12 12" repeatCount="indefinite"/></path></svg>
${t}
`}showFinished(t){if(!t.ok){this.innerText=this.getAttribute("failed")||T.failed;return}let e=this.getAttribute("done")||T.done;e=e.replace("$DURATION",t.duration),this.innerHTML=`
${e}
<codapi-ref>by <a href="https://codapi.org/">codapi</a></codapi-ref>`}showMessage(t){this.innerText=t}};window.customElements.get("codapi-status")||customElements.define("codapi-status",k);function V(n){if(!n.length)return document.createTextNode("");let t=document.createElement("table"),e=document.createElement("thead"),s=document.createElement("tbody"),i=document.createElement("tr");return Object.keys(n[0]).forEach(o=>{let r=document.createElement("th");r.textContent=o,i.appendChild(r)}),e.appendChild(i),n.forEach(o=>{let r=document.createElement("tr");Object.values(o).forEach(u=>{let x=document.createElement("td");x.textContent=u,r.appendChild(x)}),s.appendChild(r)}),t.appendChild(e),t.appendChild(s),t}var $={asTable:V};var f="---";function J(n,t){let e=n.indexOf(t);return e>=0?[n.slice(0,e),n.slice(e+t.length)]:[n,""]}function Z(n){if(n.endsWith(f))return"";let t=n.lastIndexOf(`
${f}
`);return t!==-1?n.slice(t+f.length+2):n.startsWith(f)?n.slice(f.length+1):n}var c={cut:J,tail:Z,HORIZONTAL_RULE:f};var d={text:"text",table:"table",svg:"svg",html:"html",iframe:"iframe",dom:"dom",hidden:"hidden"},K="ok",M=document.createElement("template");M.innerHTML=`
<a href="#close">\u2715</a>
<pre><code></code></pre>
`;var G={[d.text]:(n,t)=>{let e=n.stdout||n.stderr,s=t?c.tail(e):e;return document.createTextNode(s||K)},[d.table]:n=>{if(!n.stdout)return document.createTextNode(n.stderr);let t=typeof n.stdout=="object"?n.stdout:JSON.parse(n.stdout);return $.asTable(t)},[d.svg]:n=>{if(!n.stdout)return document.createTextNode(n.stderr);let t=new DOMParser().parseFromString(n.stdout,"image/svg+xml");return t.querySelector("parsererror")?document.createTextNode(n.stdout):t.documentElement},[d.html]:n=>{if(!n.stdout)return document.createTextNode(n.stderr);let t=new DOMParser().parseFromString(n.stdout,"text/html");if(t.querySelector("parsererror"))return document.createTextNode(n.stdout);let e=document.createDocumentFragment();return Array.from(t.body.childNodes).forEach(s=>e.appendChild(s)),e},[d.iframe]:n=>{if(!n.stdout)return document.createTextNode(n.stderr);let t=new DOMParser().parseFromString(n.stdout,"text/html");if(t.querySelector("parsererror"))return document.createTextNode(n.stdout);let e=document.createElement("iframe");return e.width="100%",e.srcdoc=t.documentElement.outerHTML,e.onload=()=>{let s=e.contentDocument.body,i=e.contentDocument.documentElement,o=Math.max(s.scrollHeight,s.offsetHeight,i.clientHeight,i.scrollHeight,i.offsetHeight);e.style.height=o+"px"},e},[d.dom]:n=>n.stdout?n.stdout:document.createTextNode(n.stderr),[d.hidden]:n=>!n.stdout&&n.stderr?document.createTextNode(n.stderr):null},C=class extends HTMLElement{constructor(){super()}connectedCallback(){this.ready||(this.appendChild(M.content.cloneNode(!0)),this.close=this.querySelector("a"),this.output=this.querySelector("pre > code"),this.close.addEventListener("click",t=>{t.preventDefault(),this.hide()}),this.ready=!0)}fadeOut(){this.style.opacity=.4}fadeIn(){setTimeout(()=>{this.style.opacity=""},100)}showResult(t){let e=G[this.mode](t,this.tail);if(this.output.innerHTML="",!e){this.hide();return}this.output.appendChild(e),this.show()}showMessage(t){this.output.innerText=t,t?this.show():this.hide()}showError(t){let e=t.message+(t.stack?`
${t.stack}`:"");this.showMessage(e)}show(){this.removeAttribute("hidden")}hide(){this.setAttribute("hidden","")}get mode(){return this.getAttribute("mode")||d.text}set mode(t){t in d||(t=d.text),this.setAttribute("mode",t)}get tail(){return this.hasAttribute("tail")}set tail(t){t?this.setAttribute("tail",""):this.removeAttribute("tail")}};window.customElements.get("codapi-output")||customElements.define("codapi-output",C);var p={off:"off",basic:"basic",external:"external"},E=class extends EventTarget{constructor(t,e,s){super(),this.el=t,this.mode=e,this.executeFunc=s,this.fixFormatting(),this.listen()}listen(){if(this.mode!=p.off){if(this.mode==p.external){this.el.addEventListener("keydown",this.handleExecute.bind(this));return}this.el.contentEditable="true",this.el.addEventListener("keydown",this.handleIndent.bind(this)),this.el.addEventListener("keydown",this.handleHide.bind(this)),this.el.addEventListener("keydown",this.handleExecute.bind(this)),this.el.addEventListener("paste",this.onPaste.bind(this)),this.onFocus=this.initEditor.bind(this),this.el.addEventListener("focus",this.onFocus)}}initEditor(t){let e=t.target;e.innerHTML.includes("</span>")&&(e.textContent=e.textContent),e.removeEventListener("focus",this.onFocus),delete this.onFocus}handleIndent(t){t.key=="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1," ".repeat(4)))}handleHide(t){t.key=="Escape"&&(t.preventDefault(),this.dispatchEvent(new Event("hide")))}handleExecute(t){!t.ctrlKey&&!t.metaKey||t.key!=="Enter"&&t.key!=="NumpadEnter"||(t.preventDefault(),this.executeFunc())}onPaste(t){t.preventDefault();let e=(t.originalEvent||t).clipboardData.getData("text/plain");document.execCommand("insertText",!1,e)}fixFormatting(){setTimeout(()=>{this.el.innerHTML=this.el.innerHTML.replaceAll("<br>",`
`).replace(/[\u00A0]/g," ")},0)}focusEnd(){this.el.focus();let t=window.getSelection();t.selectAllChildren(this.el),t.collapseToEnd()}get isEmpty(){return this.el.textContent.trim()==""}get value(){return this.el.textContent.trim()}set value(t){this.el.textContent=t}};var a=window.codapi??{};a.version="0.19.7";a.engines=a.engines??{};a.settings=a.settings??{};window.codapi=a;var S=class extends HTMLElement{connectedCallback(){this.ready||(a.settings.url=this.getAttribute("url"),this.ready=!0)}attributeChangedCallback(t,e,s){a.settings[t]=s}};window.customElements.get("codapi-settings")||customElements.define("codapi-settings",S);async function y(n,t={}){let{timeout:e=1e4}=t,s=new AbortController,i=setTimeout(()=>s.abort(),e),o=await fetch(n,{...t,signal:s.signal});return clearTimeout(i),o}var Y="https://api.codapi.org/v1",z="Something is wrong with Codapi.",Q={400:"Bad request. Something is wrong with the request, not sure what.",404:"Unknown sandbox or command.",403:"Forbidden. Your domain is probably not allowed on Codapi.",413:"Request is too large. Try submitting less code.",429:"Too many requests. Try again in a few seconds."};async function X(n){try{let t=`${a.settings.url||Y}/exec`,e=await y(t,{method:"POST",headers:{accept:"application/json","content-type":"application/json"},body:JSON.stringify(n)});if(!e.ok){let s=Q[e.status]||z;return{ok:!1,duration:0,stdout:"",stderr:`${e.status} - ${s}`}}return await e.json()}catch(t){throw new Error("request failed",{cause:t})}}var H={init:()=>{},exec:X};async function tt(n){try{let t=et(n.files[""]),[e,s]=await nt(t);return{ok:!0,duration:s,stdout:e,stderr:""}}catch(t){return{ok:!1,duration:0,stdout:"",stderr:t.toString()}}}function et(n){let t=n.split(`
`),e=0,s=t[0].split(" ").filter(l=>l),[i,o]=s.length>=2?s:["GET",s[0]];e+=1;let r=[];for(let l=e;l<t.length;l++){let m=t[l].trim();if(m.startsWith("?")||m.startsWith("&"))r.push(m),e+=1;else break}let u={};for(let l=e;l<t.length;l++){let m=t[l].trim();if(m==="")break;let[I,P]=c.cut(m,":");u[I.trim()]=P.trim(),e+=1}let x=t.slice(e+1).join(`
`);return{method:i,url:o+r.join(""),headers:u,body:x}}async function nt(n){let t=new Date,e=await st(n),s=await it(e),i=new Date-t;return[s,i]}async function st(n){let t={method:n.method,headers:n.headers,body:n.body||void 0};return await y(n.url,t)}async function it(n){let t="HTTP/1.1",e=await n.text(),s=[`${t} ${n.status} ${n.statusText}`];for(let i of n.headers.entries())s.push(`${i[0]}: ${i[1]}`);return e&&s.push("",e),s.join(`
`)}var N={exec:tt};var ot=async function(){}.constructor;async function rt(n){try{let t=[];ct(t);let[e,s]=await at(n.files[""]);return{ok:!0,duration:s,stdout:e??t.join(`
`),stderr:""}}catch(t){return{ok:!1,duration:0,stdout:"",stderr:t.toString()}}finally{ut()}}async function at(n){let t=new ot(n),e=new Date,s=await t(),i=new Date-e;return[s,i]}function ct(n){let t=new Proxy(console,{get(e,s){return s==="log"||s==="error"||s==="warn"||s==="info"||s==="debug"?(...i)=>{let o=i.map(r=>dt(r)).join(" ");n.push(o),e[s](...i)}:e[s]}});window._console=window.console,window.console=t,window.addEventListener("error",e=>console.log(e.error))}function ut(){window.console=window._console,delete window._console}function dt(n){switch(typeof n){case"undefined":return"undefined";case"object":return JSON.stringify(n);default:return n.toString()}}var F={exec:rt};var O={javascript:F.exec,fetch:N.exec};async function lt(n){try{return ht(n)(n)}catch(t){return{ok:!1,duration:0,stdout:"",stderr:t.toString()}}}function ht(n){if(!(n.sandbox in O))throw Error(`unknown sandbox: ${n.sandbox}`);if(n.command!="run")throw Error(`unknown command: ${n.sandbox}.${n.command}`);return O[n.sandbox]}var R={init:()=>{},exec:lt};var mt="codapi",ft="run",b=class{constructor({engine:t,sandbox:e,command:s,template:i,files:o}){let[r,u]=c.cut(e||"",":");this.engineName=t||mt,this.sandbox=r,this.version=u,this.command=s||ft,this.template=i,this.files=o}get engine(){let t=a.engines[this.engineName];if(!t)throw new Error(`unknown engine: ${this.engineName}`);return t}async execute(t,e){e=await this.prepare(e);let s=await this.loadFiles();return await this.engine.exec({sandbox:this.sandbox,version:this.version,command:t||this.command,files:{"":e,...s}})}async prepare(t){if(!this.template)return t;let e="##CODE##",[s,i]=await D(this.template,pt);return t=i.replace(e,()=>t),t}async loadFiles(){if(!this.files)return{};let t={};for(let e of this.files){let[s,i]=c.cut(e,":"),[o,r]=await D(s,bt);t[i||o]=r}return t}};async function D(n,t){if(n[0]=="#"){let o=n.slice(1),r=document.getElementById(o);if(!r)throw new Error(`element ${n} not found`);let u=r.code||r.textContent.trim();return[o,u]}let e=n.split("/").pop(),s=await fetch(n);if(s.status!=200)throw new Error(`file ${n} not found`);let i=await t(s);return[e,i]}async function pt(n){return await n.text()}async function bt(n){if(n.headers.get("content-type")=="application/octet-stream"){let e=await n.blob();return await gt(e)}else return await n.text()}function gt(n){return new Promise(t=>{let e=new FileReader;e.readAsDataURL(n),e.onloadend=function(){t(e.result)}})}var xt={codapi:H,browser:R};a.engines={...a.engines,...xt};var h=c.HORIZONTAL_RULE,wt={javascript:`console.log("${h}");`,lua:`print("${h}")`,php:`echo "${h}"`,python:`print("${h}")`,r:`cat("${h}
")`,ruby:`puts "${h}"`,typescript:`console.log("${h}");`,shell:`echo "${h}"`,sql:`select '${h}';`};function Et(n){return wt[n]||""}var j={hr:Et};var yt={fallback:"\u2718 Failed, using fallback"},g={unknown:"unknown",running:"running",failed:"failed",succeded:"succeded"},q=document.createElement("template");q.innerHTML=`
<codapi-toolbar></codapi-toolbar>
<codapi-output hidden></codapi-output>
`;var v=class extends HTMLElement{constructor(){super(),this.ready=!1,this.executor=null,this._snippet=null,this._toolbar=null,this._output=null,this._fallback=null}connectedCallback(){if(this.ready)return;let t=parseInt(this.getAttribute("init-delay"),10)||0;At(()=>{this.init(),this.render(),this.listen(),this.ready=!0,this.dispatchEvent(new Event("load"))},t)}init(){let t=this.getAttribute("files");this.executor=this.hasAttribute("sandbox")?new b({engine:this.getAttribute("engine"),sandbox:this.getAttribute("sandbox"),command:this.getAttribute("command"),template:this.getAttribute("template"),files:t?t.split(" "):null}):null,this.dependsOn=this.getAttribute("depends-on"),this.state=g.unknown}render(){this.appendChild(q.content.cloneNode(!0));let t=this.findCodeElement();this.snippet=new E(t,this.editor,this.execute.bind(this)),this._toolbar=this.querySelector("codapi-toolbar");let e=this.getAttribute("actions");this._toolbar.runnable=this.executor!=null,this._toolbar.addActions(e?e.split(" "):null);let s=this._toolbar.querySelector("codapi-status");this.hasAttribute("status-running")&&s.setAttribute("running",this.getAttribute("status-running")),this.hasAttribute("status-failed")&&s.setAttribute("failed",this.getAttribute("status-failed")),this.hasAttribute("status-done")&&s.setAttribute("done",this.getAttribute("status-done")),this._output=this.querySelector("codapi-output"),this._output.mode=this.getAttribute("output-mode"),this._output.tail=this.hasAttribute("output-tail"),this.hasAttribute("output")&&(this._fallback=this.extractFallback(this.getAttribute("output")))}listen(){this._toolbar.addEventListener("run",t=>{this.execute()}),this._toolbar.addEventListener("command",t=>{this.execute(t.detail)}),this._toolbar.addEventListener("event",t=>{this.dispatchEvent(new Event(t.detail))}),this.editor==p.basic&&(this._toolbar.editable=!0,this._toolbar.addEventListener("edit",t=>{this.snippet.focusEnd()})),this.snippet.addEventListener("hide",t=>{this._output.hide()})}findCodeElement(){if(!this.selector){let e=this.previousElementSibling||this.parentElement.previousElementSibling;return e.querySelector("code")||e}let t;if(this.selector.startsWith("@prev")){let e=this.previousElementSibling,[s,i]=c.cut(this.selector," ");t=e.querySelector(i)}else t=document.querySelector(this.selector);if(!t)throw Error(`element not found: ${this.selector}`);return t}extractFallback(t){let e=this.findOutputElement(t),i=(e.querySelector("code")||e).textContent.trim();return e.parentElement.removeChild(e),{ok:!1,duration:0,stdout:i,stderr:""}}findOutputElement(t){if(!t||t=="@next")return this.nextElementSibling||this.parentElement.nextElementSibling;let e;if(t.startsWith("@next")){let s=this.nextElementSibling,[i,o]=c.cut(t," ");e=s.querySelector(o)}else e=document.querySelector(t);if(!e)throw Error(`element not found: ${t}`);return e}async execute(t=void 0){if(this.executor){if(this.snippet.isEmpty){this._output.showMessage("(empty)");return}try{let e=vt(this);this.dispatchEvent(new CustomEvent("execute",{detail:e})),this.state=g.running,this._toolbar.showRunning(),this._output.fadeOut();let s=await this.executor.execute(t,e);this.state=s.ok?g.succeded:g.failed,this._toolbar.showFinished(s),this._output.showResult(s),this.dispatchEvent(new CustomEvent("result",{detail:s}))}catch(e){this._fallback?(this._toolbar.showStatus(yt.fallback),this._output.showResult(this._fallback)):(this._toolbar.showFinished({}),this._output.showMessage(e.message)),console.error(e),this.state=g.failed,this.dispatchEvent(new CustomEvent("error",{detail:e}))}finally{this._output.fadeIn()}}}attributeChangedCallback(t,e,s){switch(t){case"engine":case"sandbox":case"command":case"template":case"files":let i=this.getAttribute("files");this.executor=new b({engine:this.getAttribute("engine"),sandbox:this.getAttribute("sandbox"),command:this.getAttribute("command"),template:this.getAttribute("template"),files:i?i.split(" "):null});break}}showStatus(t){this._toolbar.showStatus(t)}get syntax(){return this.getAttribute("syntax")||this.getAttribute("sandbox")}set syntax(t){this.setAttribute("syntax",t)}get selector(){return this.getAttribute("selector")}set selector(t){this.setAttribute("selector",t)}get editor(){return this.getAttribute("editor")||p.off}set editor(t){this.setAttribute("editor",t)}get code(){return this.snippet.value}set code(t){this.snippet.value=t}get state(){return this.getAttribute("state")}set state(t){this.setAttribute("state",t)}};L(v,"observedAttributes",["sandbox","engine","command","template","files"]);function vt(n){let t=new Set,e=[],s=(o,r)=>{if(!o)throw new Error(`#${r} dependency not found`);t.has(r)||(t.add(r),o.dependsOn&&o.dependsOn.split(" ").forEach(u=>{s(document.getElementById(u),u)}),e.push(o.code))};s(n,n.id);let i=n.hasAttribute("output-tail")?j.hr(n.syntax):"";return i?e.join(`
${i}
`):e.join(`
`)}function At(n,t){if(t<=0){n();return}setTimeout(n,t)}window.customElements.get("codapi-snippet")||customElements.define("codapi-snippet",v);})();

View file

@ -345,4 +345,5 @@ disableAutodetect:!0,illegal:"</",contains:[].concat(_,p,m,[c,{
begin:e.IDENT_RE+"::",keywords:u},{className:"class",
beginKeywords:"enum class struct union",end:/[{;:<>=]/,contains:[{
beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c,
strings:l,keywords:u}}}})();hljs.registerLanguage("c",e)})();
strings:l,keywords:u}}}})();hljs.registerLanguage("c",e)})();
export default hljs;

1
frontend/htmx.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -4,10 +4,6 @@
<meta charset="utf-8" />
<title>Hare Playground</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="codapi.js"></script>
<script src="codapi-settings.js"></script>
<script src="highlight.min.js"></script>
<link rel="stylesheet" href="codapi.css" />
<link rel="stylesheet" href="github.min.css" />
<link rel="stylesheet" href="writ.min.css" />
<link rel="stylesheet" href="style.css" />
@ -21,10 +17,45 @@
<a href="https://docs.harelang.org/">Standard Library Reference</a>
</span>
<div class="editor language-c">Sorry, this playground requires JavaScript. :(</div>
<section class="toolbar">
<button
hx-post="/v1/exec"
hx-target=".code-output code"
hx-swap="innerHTML"
hx-vals="js:{code: getCode()}"
hx-ext="json-enc"
>
Run
</button>
<codapi-settings url="/v1"></codapi-settings>
<codapi-snippet sandbox="hare" editor="external" selector=".editor"></codapi-snippet>
<button id="share-button">
Share
</button>
<span id="htmx-indicator">
<svg width="1em" height="1em" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z"><animateTransform attributeName="transform" type="rotate" dur="0.75s" values="0 12 12;360 12 12" repeatCount="indefinite"/></path></svg>
</span>
<span id="status" class="status"></span>
</section>
<section class="code">
<div class="editor language-c">Sorry, this playground requires JavaScript. :(</div>
<div class="code-output">
<div class="std-output">
Standard Error
</div>
<pre><code class="stderr"></code></pre>
<div class="std-output">
Standard Output
</div>
<pre><code class="stdout"></code></pre>
</div>
</div>
</section>
<footer>
<p>

1
frontend/running.svg Normal file
View file

@ -0,0 +1 @@
<svg width="1em" height="1em" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z"><animateTransform attributeName="transform" type="rotate" dur="0.75s" values="0 12 12;360 12 12" repeatCount="indefinite"/></path></svg>

After

Width:  |  Height:  |  Size: 404 B

View file

@ -13,6 +13,9 @@ pre {
background-color: transparent;
border: none;
}
.code-output pre {
margin: 0;
}
body > footer {
font-family: sans-serif;
@ -28,11 +31,7 @@ a, a:visited {
color: #008bf5;
}
codapi-snippet {
display: block;
margin: 10px 0;
}
codapi-snippet button {
button {
padding: 0;
border: none;
background: none;
@ -40,19 +39,16 @@ codapi-snippet button {
color: #008bf5;
font-size: 16px;
cursor: pointer;
margin-right: 10px;
}
codapi-toolbar button::after {
button[hx-post]::after {
content: " ▶";
}
codapi-toolbar a,
codapi-toolbar a:visited {
color: #008bf5;
button#share-button {
border-bottom: 1px solid rgba(0, 139, 245, 0.2);
text-decoration: none;
font-size: 16px;
}
.editor {
.editor, div.code-output {
border-radius: 6px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);
font-family: 'Source Code Pro', monospace;
@ -67,7 +63,47 @@ codapi-toolbar a:visited {
tab-size: 8;
}
codapi-output {
div.code-output {
position: relative;
display: block;
margin: 0;
}
.code-output a[href="#close"] {
position: absolute;
top: .25em;
right: .25em;
display: inline-block;
width: 1.5em;
height: 1.5em;
line-height: 1.5em;
text-align: center;
}
section.code {
display: flex;
flex-direction: row;
gap: 1em;
@media (max-width: 800px) {
flex-direction: column;
}
}
@media (min-width: 801px) {
div.editor, div.code-output {
width: 50vw;
}
}
div.editor, div.executor {
flex-grow: 1;
}
#htmx-indicator {
display:none;
}
.toolbar {
border-radius: 6px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);
font-family: 'Source Code Pro', monospace;
@ -78,8 +114,25 @@ codapi-output {
padding: 10px;
resize: none !important;
tab-size: 4;
margin-bottom: 10px;
}
codapi-output pre {
margin: 0;
.std-output {
color: #bbb;
text-align: center;
display: flex;
}
.std-output::before, .std-output::after {
flex: 1 1 auto;
margin: auto;
margin-right: auto;
border-top: 1px solid #bbb;
content: '';
box-sizing: border-box;
}
.std-output::before {
margin-right: 2%;
}
.std-output::after {
margin-left: 2%;
}