wiki-plugin-livecode 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # wiki-plugin-livecode
2
+
3
+ Run a Livecode wasm stack as a lazy federated-wiki widget.
4
+
5
+ A `livecode` item renders a light poster card; clicking it boots the stack's
6
+ HTML5 standalone in an iframe. Because each standalone carries a ~6.4 MB
7
+ Emscripten engine plus real runtime memory, at most one widget is live at a
8
+ time, and a watchdog tears the frame down when the item leaves the lineup.
9
+
10
+ Item text (UPPERCASE commands, optional colon):
11
+
12
+ STACK https://wasm.livecode.wiki/assets/wasm/Hexagon/Hexagon.html
13
+ TITLE Hexagon
14
+ HEIGHT 420
15
+ BOOT click
16
+
17
+ `STACK` may be absolute or relative to the item's origin site. `BOOT auto`
18
+ boots on render — use sparingly. Messages posted by the stack surface as a
19
+ `livecodeMessage` jQuery event on the widget (the grounding for the WikiScript
20
+ stage dialect).
21
+
22
+ Design context: door two of Three Livecode Doors on
23
+ https://language.ide.earth — see also the Livecode Wasm page on
24
+ https://wasm.livecode.wiki.
@@ -0,0 +1,4 @@
1
+ (()=>{var h="https://wasm.livecode.wiki/assets/wasm/shared/stack.html",m=t=>{let e={stack:null,title:null,height:420,boot:"auto",fullscreen:"fixed",name:null,script:null},s=(t||"").split(`
2
+ `);for(let o=0;o<s.length;o++){let n=s[o].match(/^\s*script\s+"([^"]+)"/);if(n){e.script=s.slice(o).join(`
3
+ `),e.name||(e.name=n[1]),e.title||(e.title=n[1]);break}let i=s[o].trim().match(/^([A-Z]+):?\s+(.*)$/);if(!i)continue;let[,c,r]=i,l=r.trim();if(c==="STACK")e.stack=l;else if(c==="TITLE")e.title=l;else if(c==="HEIGHT")e.height=parseInt(l,10)||e.height;else if(c==="BOOT")e.boot=l.toLowerCase()==="click"?"click":"auto";else if(c==="FULLSCREEN"){let d=l.toLowerCase();e.fullscreen=["fixed","scale","resize"].includes(d)?d:"fixed"}else c==="NAME"&&(e.name=l)}if(e.script&&!e.stack&&(e.stack=h),!e.title&&e.stack){let o=e.stack.split("/").pop()||"";e.title=(o.split("?")[0]||"").replace(/\.html?$/i,"")||"Livecode stack"}return e},f=t=>String(t).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e]),v=(t,e)=>/^https?:\/\//i.test(e)?e:`//${t.parents(".page").data("site")||window.location.host}/${e.replace(/^\//,"")}`,u=t=>{let e=t.__livecode;e&&(clearInterval(e.watch),window.removeEventListener("message",e.onMessage),document.removeEventListener("fullscreenchange",e.onFsChange),e.frame.remove(),e.bar.remove(),e.poster.style.display="",t.__livecode=null)},g=(t,e,s,o)=>{if(t.__livecode)return;o+=(o.includes("?")?"&":"?")+"fs="+s.fullscreen,s.name&&(o+="&stack="+encodeURIComponent(s.name)),s.script&&(o+="&boot=script");let n=document.createElement("div");n.className="livecode-bar",n.style.cssText="display:flex;justify-content:space-between;align-items:center;font-size:12px;color:#666;padding:2px 4px 4px",n.innerHTML=`<span>${f(s.title)}</span><span><span class="livecode-full" style="cursor:pointer;color:#666;margin-right:12px" title="fullscreen (${f(s.fullscreen)})">&#x26F6; full</span><span class="livecode-stop" style="cursor:pointer;color:#b5511d">&#9632; stop</span></span>`;let i=document.createElement("iframe");i.className="livecode-frame",i.src=o,i.setAttribute("allow","fullscreen"),i.setAttribute("allowfullscreen",""),i.style.cssText=`width:100%;height:${s.height}px;border:1px solid #e0e0e0;border-radius:6px;background:#fff`;let c=a=>{try{i.contentWindow.postMessage({livecode:{command:a,mode:s.fullscreen}},"*")}catch{}},r=a=>{if(!t.__livecode||a.source!==i.contentWindow)return;let p=a.data&&a.data.livecode;p&&p.ready==="script"&&s.script&&i.contentWindow.postMessage({livecode:{bootScript:s.script}},"*"),t.dataset.lastMessage=JSON.stringify(a.data),window.$&&window.$(t).trigger("livecodeMessage",[a.data])};window.addEventListener("message",r);let l=()=>{t.__livecode&&(document.fullscreenElement===i?c("fullscreen"):document.fullscreenElement||c("fullscreenExit"))};document.addEventListener("fullscreenchange",l);let d=setInterval(()=>{document.body.contains(t)||u(t)},4e3);e.style.display="none",t.appendChild(n),t.appendChild(i),n.querySelector(".livecode-stop").addEventListener("click",()=>u(t)),n.querySelector(".livecode-full").addEventListener("click",()=>{i.requestFullscreen&&i.requestFullscreen()}),t.__livecode={poster:e,frame:i,bar:n,watch:d,onMessage:r,onFsChange:l}},w=(t,e)=>{let s=m(e.text||"");if(!s.stack){t.html('<div class="livecode-widget" style="border:1px dashed #ccc;border-radius:6px;padding:12px;color:#888;font-size:13px">livecode &mdash; no STACK given. Double click to edit, e.g.<br><code>STACK https://wasm.livecode.wiki/assets/wasm/shared/stack.html?zip=hexagon.zip</code></div>');return}t.html(`<div class="livecode-widget"><div class="livecode-poster" style="cursor:pointer;border:1px solid #e0d5c5;border-radius:6px;padding:14px 16px;background:linear-gradient(135deg,#fff7ee,#ffffff)"><div style="font-weight:bold;font-size:15px">&#9654; ${f(s.title)}</div><div style="font-size:12px;color:#888;margin-top:4px">Livecode wasm &mdash; ${s.script?"runs this item's script with":"loads"} the ${6.4} MB engine</div></div></div>`)},x=(t,e)=>{t.on("dblclick",()=>window.wiki.textEditor(t,e));let s=m(e.text||""),o=t.find(".livecode-widget")[0],n=t.find(".livecode-poster")[0];if(!s.stack||!o||!n)return;let i=v(t,s.stack),c=r=>{r&&r.stopPropagation(),g(o,n,s,i)};n.addEventListener("click",c),s.boot==="auto"&&c()};typeof window<"u"&&(window.plugins=window.plugins||{},window.plugins.livecode={emit:w,bind:x});})();
4
+ //# sourceMappingURL=livecode.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/client/livecode.js"],
4
+ "sourcesContent": ["// wiki-plugin-livecode \u2014 Livecode wasm stacks as wiki widgets.\n//\n// Item text DSL (UPPERCASE commands, per the house fedwiki-dsl convention):\n//\n// STACK wasm/Hexagon/Hexagon.html relative to the item's origin site\n// STACK https://site/assets/.../x.html or absolute\n// TITLE Hexagon poster label (default: file name)\n// HEIGHT 420 iframe height in px (default 420)\n// BOOT auto auto (default) or click\n// FULLSCREEN fixed fixed (default), scale, or resize\n// NAME MyStack stack name for the JS bridge\n//\n// Design (door two of Three Livecode Doors): a livecode item loads and\n// displays its stack by default. BOOT click opts a widget into a light\n// collapsible poster that boots on demand. Every widget runs independently;\n// a watchdog tears a frame down when its item leaves the lineup (fedwiki\n// has no plugin dispose hook \u2014 the htv pattern), and the stop control\n// collapses a running widget back to its poster.\n\nconst ENGINE_MB = 6.4\nconst SHARED_SHELL = 'https://wasm.livecode.wiki/assets/wasm/shared/stack.html'\n\nexport const parse = (text) => {\n const spec = { stack: null, title: null, height: 420, boot: 'auto', fullscreen: 'fixed', name: null, script: null }\n const lines = (text || '').split('\\n')\n for (let i = 0; i < lines.length; i++) {\n // a script-only stack begins `script \"Name\"` \u2014 everything from that\n // line on is the stack's script, booted directly by the shared shell\n const sm = lines[i].match(/^\\s*script\\s+\"([^\"]+)\"/)\n if (sm) {\n spec.script = lines.slice(i).join('\\n')\n if (!spec.name) spec.name = sm[1]\n if (!spec.title) spec.title = sm[1]\n break\n }\n const m = lines[i].trim().match(/^([A-Z]+):?\\s+(.*)$/)\n if (!m) continue\n const [, cmd, restRaw] = m\n const rest = restRaw.trim()\n if (cmd === 'STACK') spec.stack = rest\n else if (cmd === 'TITLE') spec.title = rest\n else if (cmd === 'HEIGHT') spec.height = parseInt(rest, 10) || spec.height\n else if (cmd === 'BOOT') spec.boot = rest.toLowerCase() === 'click' ? 'click' : 'auto'\n else if (cmd === 'FULLSCREEN') {\n const mode = rest.toLowerCase()\n spec.fullscreen = ['fixed', 'scale', 'resize'].includes(mode) ? mode : 'fixed'\n } else if (cmd === 'NAME') spec.name = rest\n }\n if (spec.script && !spec.stack) spec.stack = SHARED_SHELL\n if (!spec.title && spec.stack) {\n const leaf = spec.stack.split('/').pop() || ''\n spec.title = (leaf.split('?')[0] || '').replace(/\\.html?$/i, '') || 'Livecode stack'\n }\n return spec\n}\n\nconst escapeHtml = (s) =>\n String(s).replace(/[&<>\"']/g, (c) =>\n ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c])\n\nconst resolveUrl = (div, stack) => {\n if (/^https?:\\/\\//i.test(stack)) return stack\n const site = div.parents('.page').data('site') || window.location.host\n return `//${site}/${stack.replace(/^\\//, '')}`\n}\n\n// ---- per-widget lifecycle ---------------------------------------------\n\nconst stopWidget = (widget) => {\n const inst = widget.__livecode\n if (!inst) return\n clearInterval(inst.watch)\n window.removeEventListener('message', inst.onMessage)\n document.removeEventListener('fullscreenchange', inst.onFsChange)\n inst.frame.remove()\n inst.bar.remove()\n inst.poster.style.display = ''\n widget.__livecode = null\n}\n\nconst boot = (widget, poster, spec, url) => {\n if (widget.__livecode) return\n\n // arm the shell's fullscreen mode from load \u2014 deterministic, no message race\n url += (url.includes('?') ? '&' : '?') + 'fs=' + spec.fullscreen\n if (spec.name) {\n url += '&stack=' + encodeURIComponent(spec.name)\n }\n if (spec.script) {\n url += '&boot=script'\n }\n\n const bar = document.createElement('div')\n bar.className = 'livecode-bar'\n bar.style.cssText =\n 'display:flex;justify-content:space-between;align-items:center;' +\n 'font-size:12px;color:#666;padding:2px 4px 4px'\n bar.innerHTML =\n `<span>${escapeHtml(spec.title)}</span>` +\n '<span>' +\n `<span class=\"livecode-full\" style=\"cursor:pointer;color:#666;margin-right:12px\" ` +\n `title=\"fullscreen (${escapeHtml(spec.fullscreen)})\">&#x26F6; full</span>` +\n `<span class=\"livecode-stop\" style=\"cursor:pointer;color:#b5511d\">&#9632; stop</span>` +\n '</span>'\n\n const frame = document.createElement('iframe')\n frame.className = 'livecode-frame'\n frame.src = url\n frame.setAttribute('allow', 'fullscreen')\n frame.setAttribute('allowfullscreen', '')\n frame.style.cssText =\n `width:100%;height:${spec.height}px;border:1px solid #e0e0e0;` +\n 'border-radius:6px;background:#fff'\n\n const tell = (command) => {\n try {\n frame.contentWindow.postMessage({ livecode: { command, mode: spec.fullscreen } }, '*')\n } catch (e) { /* frame gone */ }\n }\n\n const onMessage = (event) => {\n if (!widget.__livecode || event.source !== frame.contentWindow) return\n const d = event.data && event.data.livecode\n if (d && d.ready === 'script' && spec.script) {\n frame.contentWindow.postMessage({ livecode: { bootScript: spec.script } }, '*')\n }\n widget.dataset.lastMessage = JSON.stringify(event.data)\n if (window.$) window.$(widget).trigger('livecodeMessage', [event.data])\n }\n window.addEventListener('message', onMessage)\n\n const onFsChange = () => {\n if (!widget.__livecode) return\n if (document.fullscreenElement === frame) tell('fullscreen')\n else if (!document.fullscreenElement) tell('fullscreenExit')\n }\n document.addEventListener('fullscreenchange', onFsChange)\n\n // watchdog: fedwiki never tells a plugin its item left the lineup\n const watch = setInterval(() => {\n if (!document.body.contains(widget)) stopWidget(widget)\n }, 4000)\n\n poster.style.display = 'none'\n widget.appendChild(bar)\n widget.appendChild(frame)\n bar.querySelector('.livecode-stop').addEventListener('click', () => stopWidget(widget))\n bar.querySelector('.livecode-full').addEventListener('click', () => {\n if (frame.requestFullscreen) frame.requestFullscreen()\n })\n widget.__livecode = { poster, frame, bar, watch, onMessage, onFsChange }\n}\n\n// ---- fedwiki plugin surface -------------------------------------------\n\nexport const emit = (div, item) => {\n const spec = parse(item.text || '')\n if (!spec.stack) {\n div.html(\n '<div class=\"livecode-widget\" style=\"border:1px dashed #ccc;border-radius:6px;' +\n 'padding:12px;color:#888;font-size:13px\">livecode &mdash; no STACK given. ' +\n 'Double click to edit, e.g.<br><code>STACK https://wasm.livecode.wiki/assets/wasm/shared/stack.html?zip=hexagon.zip</code></div>')\n return\n }\n div.html(\n '<div class=\"livecode-widget\">' +\n '<div class=\"livecode-poster\" style=\"cursor:pointer;border:1px solid #e0d5c5;' +\n 'border-radius:6px;padding:14px 16px;background:linear-gradient(135deg,#fff7ee,#ffffff)\">' +\n `<div style=\"font-weight:bold;font-size:15px\">&#9654; ${escapeHtml(spec.title)}</div>` +\n `<div style=\"font-size:12px;color:#888;margin-top:4px\">Livecode wasm &mdash; ` +\n `${spec.script ? \"runs this item's script with\" : 'loads'} the ${ENGINE_MB} MB engine</div>` +\n '</div></div>')\n}\n\nexport const bind = (div, item) => {\n div.on('dblclick', () => window.wiki.textEditor(div, item))\n const spec = parse(item.text || '')\n const widget = div.find('.livecode-widget')[0]\n const poster = div.find('.livecode-poster')[0]\n if (!spec.stack || !widget || !poster) return\n const url = resolveUrl(div, spec.stack)\n const start = (event) => {\n if (event) event.stopPropagation()\n boot(widget, poster, spec, url)\n }\n poster.addEventListener('click', start)\n if (spec.boot === 'auto') start()\n}\n\nif (typeof window !== 'undefined') {\n window.plugins = window.plugins || {}\n window.plugins['livecode'] = { emit, bind }\n}\n"],
5
+ "mappings": "MAoBA,IAAMA,EAAe,2DAERC,EAASC,GAAS,CAC7B,IAAMC,EAAO,CAAE,MAAO,KAAM,MAAO,KAAM,OAAQ,IAAK,KAAM,OAAQ,WAAY,QAAS,KAAM,KAAM,OAAQ,IAAK,EAC5GC,GAASF,GAAQ,IAAI,MAAM;AAAA,CAAI,EACrC,QAASG,EAAI,EAAGA,EAAID,EAAM,OAAQC,IAAK,CAGrC,IAAMC,EAAKF,EAAMC,CAAC,EAAE,MAAM,wBAAwB,EAClD,GAAIC,EAAI,CACNH,EAAK,OAASC,EAAM,MAAMC,CAAC,EAAE,KAAK;AAAA,CAAI,EACjCF,EAAK,OAAMA,EAAK,KAAOG,EAAG,CAAC,GAC3BH,EAAK,QAAOA,EAAK,MAAQG,EAAG,CAAC,GAClC,KACF,CACA,IAAMC,EAAIH,EAAMC,CAAC,EAAE,KAAK,EAAE,MAAM,qBAAqB,EACrD,GAAI,CAACE,EAAG,SACR,GAAM,CAAC,CAAEC,EAAKC,CAAO,EAAIF,EACnBG,EAAOD,EAAQ,KAAK,EAC1B,GAAID,IAAQ,QAASL,EAAK,MAAQO,UACzBF,IAAQ,QAASL,EAAK,MAAQO,UAC9BF,IAAQ,SAAUL,EAAK,OAAS,SAASO,EAAM,EAAE,GAAKP,EAAK,eAC3DK,IAAQ,OAAQL,EAAK,KAAOO,EAAK,YAAY,IAAM,QAAU,QAAU,eACvEF,IAAQ,aAAc,CAC7B,IAAMG,EAAOD,EAAK,YAAY,EAC9BP,EAAK,WAAa,CAAC,QAAS,QAAS,QAAQ,EAAE,SAASQ,CAAI,EAAIA,EAAO,OACzE,MAAWH,IAAQ,SAAQL,EAAK,KAAOO,EACzC,CAEA,GADIP,EAAK,QAAU,CAACA,EAAK,QAAOA,EAAK,MAAQH,GACzC,CAACG,EAAK,OAASA,EAAK,MAAO,CAC7B,IAAMS,EAAOT,EAAK,MAAM,MAAM,GAAG,EAAE,IAAI,GAAK,GAC5CA,EAAK,OAASS,EAAK,MAAM,GAAG,EAAE,CAAC,GAAK,IAAI,QAAQ,YAAa,EAAE,GAAK,gBACtE,CACA,OAAOT,CACT,EAEMU,EAAcC,GAClB,OAAOA,CAAC,EAAE,QAAQ,WAAaC,IAC5B,CAAE,IAAK,QAAS,IAAK,OAAQ,IAAK,OAAQ,IAAK,SAAU,IAAK,OAAQ,GAAGA,CAAC,CAAC,EAE1EC,EAAa,CAACC,EAAKC,IACnB,gBAAgB,KAAKA,CAAK,EAAUA,EAEjC,KADMD,EAAI,QAAQ,OAAO,EAAE,KAAK,MAAM,GAAK,OAAO,SAAS,IAClD,IAAIC,EAAM,QAAQ,MAAO,EAAE,CAAC,GAKxCC,EAAcC,GAAW,CAC7B,IAAMC,EAAOD,EAAO,WACfC,IACL,cAAcA,EAAK,KAAK,EACxB,OAAO,oBAAoB,UAAWA,EAAK,SAAS,EACpD,SAAS,oBAAoB,mBAAoBA,EAAK,UAAU,EAChEA,EAAK,MAAM,OAAO,EAClBA,EAAK,IAAI,OAAO,EAChBA,EAAK,OAAO,MAAM,QAAU,GAC5BD,EAAO,WAAa,KACtB,EAEME,EAAO,CAACF,EAAQG,EAAQpB,EAAMqB,IAAQ,CAC1C,GAAIJ,EAAO,WAAY,OAGvBI,IAAQA,EAAI,SAAS,GAAG,EAAI,IAAM,KAAO,MAAQrB,EAAK,WAClDA,EAAK,OACPqB,GAAO,UAAY,mBAAmBrB,EAAK,IAAI,GAE7CA,EAAK,SACPqB,GAAO,gBAGT,IAAMC,EAAM,SAAS,cAAc,KAAK,EACxCA,EAAI,UAAY,eAChBA,EAAI,MAAM,QACR,8GAEFA,EAAI,UACF,SAASZ,EAAWV,EAAK,KAAK,CAAC,mHAGTU,EAAWV,EAAK,UAAU,CAAC,qHAInD,IAAMuB,EAAQ,SAAS,cAAc,QAAQ,EAC7CA,EAAM,UAAY,iBAClBA,EAAM,IAAMF,EACZE,EAAM,aAAa,QAAS,YAAY,EACxCA,EAAM,aAAa,kBAAmB,EAAE,EACxCA,EAAM,MAAM,QACV,qBAAqBvB,EAAK,MAAM,gEAGlC,IAAMwB,EAAQC,GAAY,CACxB,GAAI,CACFF,EAAM,cAAc,YAAY,CAAE,SAAU,CAAE,QAAAE,EAAS,KAAMzB,EAAK,UAAW,CAAE,EAAG,GAAG,CACvF,MAAY,CAAmB,CACjC,EAEM0B,EAAaC,GAAU,CAC3B,GAAI,CAACV,EAAO,YAAcU,EAAM,SAAWJ,EAAM,cAAe,OAChE,IAAMK,EAAID,EAAM,MAAQA,EAAM,KAAK,SAC/BC,GAAKA,EAAE,QAAU,UAAY5B,EAAK,QACpCuB,EAAM,cAAc,YAAY,CAAE,SAAU,CAAE,WAAYvB,EAAK,MAAO,CAAE,EAAG,GAAG,EAEhFiB,EAAO,QAAQ,YAAc,KAAK,UAAUU,EAAM,IAAI,EAClD,OAAO,GAAG,OAAO,EAAEV,CAAM,EAAE,QAAQ,kBAAmB,CAACU,EAAM,IAAI,CAAC,CACxE,EACA,OAAO,iBAAiB,UAAWD,CAAS,EAE5C,IAAMG,EAAa,IAAM,CAClBZ,EAAO,aACR,SAAS,oBAAsBM,EAAOC,EAAK,YAAY,EACjD,SAAS,mBAAmBA,EAAK,gBAAgB,EAC7D,EACA,SAAS,iBAAiB,mBAAoBK,CAAU,EAGxD,IAAMC,EAAQ,YAAY,IAAM,CACzB,SAAS,KAAK,SAASb,CAAM,GAAGD,EAAWC,CAAM,CACxD,EAAG,GAAI,EAEPG,EAAO,MAAM,QAAU,OACvBH,EAAO,YAAYK,CAAG,EACtBL,EAAO,YAAYM,CAAK,EACxBD,EAAI,cAAc,gBAAgB,EAAE,iBAAiB,QAAS,IAAMN,EAAWC,CAAM,CAAC,EACtFK,EAAI,cAAc,gBAAgB,EAAE,iBAAiB,QAAS,IAAM,CAC9DC,EAAM,mBAAmBA,EAAM,kBAAkB,CACvD,CAAC,EACDN,EAAO,WAAa,CAAE,OAAAG,EAAQ,MAAAG,EAAO,IAAAD,EAAK,MAAAQ,EAAO,UAAAJ,EAAW,WAAAG,CAAW,CACzE,EAIaE,EAAO,CAACjB,EAAKkB,IAAS,CACjC,IAAMhC,EAAOF,EAAMkC,EAAK,MAAQ,EAAE,EAClC,GAAI,CAAChC,EAAK,MAAO,CACfc,EAAI,KACF,uRAEiI,EACnI,MACF,CACAA,EAAI,KACF,yPAGwDJ,EAAWV,EAAK,KAAK,CAAC,qFAE3EA,EAAK,OAAS,+BAAiC,OAAO,QAAQ,GAAS,8BAC5D,CAClB,EAEaiC,EAAO,CAACnB,EAAKkB,IAAS,CACjClB,EAAI,GAAG,WAAY,IAAM,OAAO,KAAK,WAAWA,EAAKkB,CAAI,CAAC,EAC1D,IAAMhC,EAAOF,EAAMkC,EAAK,MAAQ,EAAE,EAC5Bf,EAASH,EAAI,KAAK,kBAAkB,EAAE,CAAC,EACvCM,EAASN,EAAI,KAAK,kBAAkB,EAAE,CAAC,EAC7C,GAAI,CAACd,EAAK,OAAS,CAACiB,GAAU,CAACG,EAAQ,OACvC,IAAMC,EAAMR,EAAWC,EAAKd,EAAK,KAAK,EAChCkC,EAASP,GAAU,CACnBA,GAAOA,EAAM,gBAAgB,EACjCR,EAAKF,EAAQG,EAAQpB,EAAMqB,CAAG,CAChC,EACAD,EAAO,iBAAiB,QAASc,CAAK,EAClClC,EAAK,OAAS,QAAQkC,EAAM,CAClC,EAEI,OAAO,OAAW,MACpB,OAAO,QAAU,OAAO,SAAW,CAAC,EACpC,OAAO,QAAQ,SAAc,CAAE,KAAAH,EAAM,KAAAE,CAAK",
6
+ "names": ["SHARED_SHELL", "parse", "text", "spec", "lines", "i", "sm", "m", "cmd", "restRaw", "rest", "mode", "leaf", "escapeHtml", "s", "c", "resolveUrl", "div", "stack", "stopWidget", "widget", "inst", "boot", "poster", "url", "bar", "frame", "tell", "command", "onMessage", "event", "d", "onFsChange", "watch", "emit", "item", "bind", "start"]
7
+ }
package/factory.json ADDED
@@ -0,0 +1 @@
1
+ {"name": "Livecode", "title": "Run a Livecode wasm stack as a lazy, self tearing down widget", "category": "format"}
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "wiki-plugin-livecode",
3
+ "version": "0.4.0",
4
+ "description": "Run a Livecode wasm stack as a lazy wiki widget — poster card, click to boot, one live widget at a time, clean teardown.",
5
+ "type": "module",
6
+ "main": "client/livecode.js",
7
+ "files": ["client", "pages", "factory.json"],
8
+ "scripts": {
9
+ "build": "node scripts/build.js",
10
+ "test": "node --test"
11
+ },
12
+ "dependencies": {},
13
+ "devDependencies": {
14
+ "esbuild": "^0.25.0"
15
+ },
16
+ "author": "David Bovill",
17
+ "license": "MIT",
18
+ "repository": { "type": "git", "url": "git+https://github.com/Hitchhikers-Guide-to-the-Galaxy/wiki-plugin-livecode.git" },
19
+ "bugs": { "url": "https://github.com/Hitchhikers-Guide-to-the-Galaxy/wiki-plugin-livecode/issues" },
20
+ "homepage": "https://github.com/Hitchhikers-Guide-to-the-Galaxy/wiki-plugin-livecode#readme"
21
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "title": "About Livecode Plugin",
3
+ "story": [
4
+ {
5
+ "type": "markdown",
6
+ "id": "bb6d3678f2754a6c",
7
+ "text": "The Livecode plugin runs a Livecode wasm stack as a wiki widget. A `livecode` item loads and displays its stack by default; add `BOOT click` for a collapsible poster card that boots on demand. Every widget runs independently — the stop control collapses a running widget back to its poster, and a watchdog tears a frame down when its item leaves the lineup."
8
+ },
9
+ {
10
+ "type": "livecode",
11
+ "id": "3b8ba788403d6699",
12
+ "text": "STACK https://wasm.livecode.wiki/assets/wasm/shared/stack.html?zip=hexagon.zip\nTITLE Hexagon\nHEIGHT 460"
13
+ },
14
+ {
15
+ "type": "markdown",
16
+ "id": "4b7e9d2c8f0a3615",
17
+ "text": "# Commands\nUPPERCASE first word, optional colon, one per line:\n\n- STACK — the standalone's shell page: absolute `https://...` or a path relative to the item's origin site\n- TITLE — the poster label; defaults to the shell file name\n- HEIGHT — iframe height in pixels; defaults to 420\n- BOOT — `auto` (default) loads and displays on render; `click` shows a collapsible poster instead\n- FULLSCREEN — `fixed` (default), `scale`, or `resize`; how the stack behaves when the full control is used — see [[Full Screen Wasm]]\n- NAME — the stack's name, needed only by `resize` mode for the JavaScript bridge call"
18
+ },
19
+ {
20
+ "type": "markdown",
21
+ "id": "e5b09c3a71d8f246",
22
+ "text": "# Script only stacks\nAn item whose text is a script only stack — beginning `script \"Name\"` — boots that script directly: the shell builds the standalone payload in the browser from the item text, so no export is needed. Command lines above the script line still apply, and the stack's name becomes the widget's NAME for the JavaScript bridge. For now the script runs aboard a small host stack, until a blank host is exported. Live:"
23
+ },
24
+ {
25
+ "type": "livecode",
26
+ "id": "9c2e6f80b5a3d417",
27
+ "text": "script \"Simple Test\"\non openstack\n create button \"Hello World\"\nend openstack"
28
+ },
29
+ {
30
+ "type": "markdown",
31
+ "id": "5c8f0a1d9e2b4736",
32
+ "text": "# Design\nDoor two of [[Three Livecode Doors]]: one shared engine pair per Livecode version is cached by the browser, so many widgets cost one engine download plus a small `standalone.zip` each. Messages the stack posts surface as a `livecodeMessage` event on the widget — the grounding for the [[WikiScript]] stage dialect. See [[Livecode Wasm]] for the export anatomy and the trimmed shell, and [[Livecode Wasm Windows]] for the script boot design."
33
+ }
34
+ ],
35
+ "journal": [
36
+ {
37
+ "type": "create",
38
+ "item": {
39
+ "title": "About Livecode Plugin",
40
+ "story": [
41
+ {
42
+ "type": "markdown",
43
+ "id": "bb6d3678f2754a6c",
44
+ "text": "The Livecode plugin runs a Livecode wasm stack as a wiki widget. A `livecode` item loads and displays its stack by default; add `BOOT click` for a collapsible poster card that boots on demand. Every widget runs independently — the stop control collapses a running widget back to its poster, and a watchdog tears a frame down when its item leaves the lineup."
45
+ }
46
+ ]
47
+ },
48
+ "date": 1784390165000,
49
+ "provenance": "Authored by an AI agent — Claude Code (claude-fable-5) via the [[fedwiki-create-plugin]] skill. [https://claude.com/claude-code Claude Code]"
50
+ }
51
+ ],
52
+ "plugin": "livecode"
53
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "title": "Wasm Widget Demo",
3
+ "story": [
4
+ {
5
+ "type": "markdown",
6
+ "id": "c3d94a71e5b2860f",
7
+ "text": "Several Livecode widgets, one engine. Each item below boots a different stack through the shared shell at `wasm.livecode.wiki/assets/wasm/shared/` — the engine pair, the icu data and the shell live at one URL each, so the browser pays for them once. Boot the first widget and watch the network: 7.2 MB, measured. Boot the next: only its stack zip."
8
+ },
9
+ {
10
+ "type": "livecode",
11
+ "id": "1a5e83c7d9f04b26",
12
+ "text": "STACK https://wasm.livecode.wiki/assets/wasm/shared/stack.html?zip=hexagon.zip\nTITLE Hexagon\nHEIGHT 460\nBOOT click"
13
+ },
14
+ {
15
+ "type": "livecode",
16
+ "id": "7b2c60e9a4d8135f",
17
+ "text": "STACK https://wasm.livecode.wiki/assets/wasm/shared/stack.html?zip=panel.zip\nTITLE Panel\nHEIGHT 460\nBOOT click"
18
+ },
19
+ {
20
+ "type": "livecode",
21
+ "id": "e8f1472b90c5d63a",
22
+ "text": "STACK https://wasm.livecode.wiki/assets/wasm/shared/stack.html?zip=svg.zip\nTITLE Svg\nHEIGHT 460\nFULLSCREEN scale\nBOOT click"
23
+ },
24
+ {
25
+ "type": "markdown",
26
+ "id": "52a9d6c48e07b3f1",
27
+ "text": "These three carry `BOOT click` so the network story stays legible — boot them one at a time and watch: after the first boot each costs its zip alone. The Hexagon is 85 KB, the Svg 166 KB, the Panel 248 KB. Widgets without `BOOT click` simply load and display where they stand. The widget on [[About Livecode Plugin]] uses the same shared shell, so the cache carries across pages too.\n\n- [https://wasm.livecode.wiki/many-wasm-widgets.html Many Wasm Widgets] — the measurements and the lineup story"
28
+ }
29
+ ],
30
+ "journal": [
31
+ {
32
+ "type": "create",
33
+ "item": {
34
+ "title": "Wasm Widget Demo",
35
+ "story": [
36
+ {
37
+ "type": "markdown",
38
+ "id": "c3d94a71e5b2860f",
39
+ "text": "Several Livecode widgets, one engine. Each item below boots a different stack through the shared shell at `wasm.livecode.wiki/assets/wasm/shared/` — the engine pair, the icu data and the shell live at one URL each, so the browser pays for them once. Boot the first widget and watch the network: 7.2 MB, measured. Boot the next: only its stack zip."
40
+ }
41
+ ]
42
+ },
43
+ "date": 1784390165000,
44
+ "provenance": "Authored by an AI agent — Claude Code (claude-fable-5) via the [[fedwiki-create-plugin]] skill. [https://claude.com/claude-code Claude Code]"
45
+ }
46
+ ],
47
+ "plugin": "livecode"
48
+ }