app
Widget Registry
Public Made by Adomby adom
The reviewed registry of widgets renderable from plain markdown on any wiki page
← Commit history
adom-stock: work inline in a wiki README (adom/wiki#229)
A bare ::adom-stock{} inline defaults to the host page's own stock route (the frame route fills src in server side), and requests carry the reader's session (credentials same-origin; the opaque frame still sends none and keeps using the minted cap).
1 file changed
+11−3
widgets/stock-widget.js+11−3@@ -1,6 +1,7 @@ /* <adom-stock>: live distributor stock and price for a component page (adom/wiki#186). MIT. * Data: an inline <script type="application/json"> child first, else fetch(src).- * src is the page's stock route: /api/pages/OWNER/SLUG/stock */+ * src is the page's stock route: /api/pages/OWNER/SLUG/stock. Inline in a wiki README a bare ::adom-stock{}+ * defaults to the host page's own route (the sandboxed frame route fills it in server side). */ (() => { const esc = s => String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); const CSS = `@font-face{font-family:'Satoshi';src:url('https://adom.inc/fonts/satoshi/satoshi-400-normal.woff2') format('woff2');font-weight:400;font-display:swap}@@ -27,7 +28,7 @@ a.pill:hover{border-color:var(--accent)}.pill .v{color:var(--muted);font-weight: if (!this.querySelector('script[type="application/json"]') && document.readyState === 'loading') await new Promise(r => document.addEventListener('DOMContentLoaded', r, { once: true })); const inline = this.querySelector('script[type="application/json"]'); if (inline) { try { data = JSON.parse(inline.textContent); } catch (e) { err = 'bad inline data'; } }- const src = this.getAttribute('src');+ const src = this.getAttribute('src') || this.ownRoute(); if (!data && src) ({ data, err } = await this.load(src, false)); // Nothing cached yet and the reader is signed in: they asked, so spend the lookup now. if (!err && data && src && data.refresh === 'declined' && data.signed_in !== false && !Object.values(data.vendors || {}).some(Boolean)) ({ data, err } = await this.load(src, true));@@ -37,12 +38,19 @@ a.pill:hover{border-color:var(--accent)}.pill .v{color:var(--muted);font-weight: e.preventDefault(); a.textContent = 'checking'; const r = await this.load(src, true); this.render(r.data, r.err); }); }+ // Inline in a wiki README (not the sandboxed frame at /OWNER/SLUG/widget/NAME): the host page's own stock route.+ ownRoute() {+ if (/^\/[^/]+\/[^/]+\/widget\/[^/]+\/?$/.test(location.pathname)) return null;+ const m = location.pathname.match(/^\/([^/]+)\/([^/]+)/);+ return m ? `/api/pages/${m[1]}/${m[2]}/stock` : null;+ } // fresh=false reads the wiki's cache only; fresh=true lets the wiki spend a vendor lookup (counted against the daily budget). async load(src, fresh) { // The frame is opaque-origin (no session); the src carries the user cap the wiki minted for a signed-in reader.+ // Inline, the reader's own session goes with the request instead. let cap = ''; try { cap = new URL(src, 'https://wiki.adom.inc').searchParams.get('cap') || ''; } catch (e) {} const headers = fresh ? { 'X-Adom-Widget': 'stock', ...(cap ? { 'X-Adom-Cap': cap } : {}) } : {};- try { const r = await fetch(src, { credentials: 'omit', headers }); const data = await r.json(); return { data, err: r.ok ? null : (data && data.error ? data.error : `stock route answered ${r.status}`) }; }+ try { const r = await fetch(src, { credentials: 'same-origin', headers }); const data = await r.json(); return { data, err: r.ok ? null : (data && data.error ? data.error : `stock route answered ${r.status}`) }; } catch (e) { return { data: null, err: 'could not reach the stock route (' + (e && e.message ? String(e.message).slice(0, 60) : 'blocked') + ')' }; } } render(d, err) {