@nebularstreams/overlay-sdk 1.0.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/package.json +44 -0
- package/preview/assets/images/favicon.ico +0 -0
- package/preview/assets/images/favicon.png +0 -0
- package/preview/assets/images/login-right.webp +0 -0
- package/preview/assets/images/nebular.color.100h.png +0 -0
- package/preview/assets/images/nebular.color.white.100h.png +0 -0
- package/preview/assets/images/nebular.medallion.transparent..png +0 -0
- package/preview/assets/images/testcard.png +0 -0
- package/preview/assets/webfonts/fa-brands-400.woff2 +0 -0
- package/preview/assets/webfonts/fa-solid-900.woff2 +0 -0
- package/preview/bundle.min.js +6 -0
- package/preview/index.css +28 -0
- package/preview/index.html +23 -0
- package/readme.md +132 -0
- package/skill/nebular-overlay/SKILL.md +56 -0
- package/skill/nebular-overlay/agents/openai.yaml +4 -0
- package/skill/nebular-overlay/references/examples.md +327 -0
- package/skill/nebular-overlay/references/manifest.md +153 -0
- package/skill/nebular-overlay/references/runtime-and-design.md +88 -0
- package/skill/nebular-overlay/scripts/validate-templates.mjs +244 -0
- package/templates/all/broadcast/breaking-news/index.html +100 -0
- package/templates/all/broadcast/breaking-news/manifest.json +9 -0
- package/templates/all/broadcast/empty/index.html +46 -0
- package/templates/all/broadcast/empty/manifest.json +7 -0
- package/templates/all/broadcast/empty/nebular.medallion.transparent.png +0 -0
- package/templates/all/broadcast/event-countdown/index.html +160 -0
- package/templates/all/broadcast/event-countdown/logo.png +0 -0
- package/templates/all/broadcast/event-countdown/manifest.json +33 -0
- package/templates/all/broadcast/lower-third/index.html +77 -0
- package/templates/all/broadcast/lower-third/manifest.json +8 -0
- package/templates/all/broadcast/newsticker/index.html +153 -0
- package/templates/all/broadcast/newsticker/manifest.json +21 -0
- package/templates/all/broadcast/newsticker-frames/background-label-green.png +0 -0
- package/templates/all/broadcast/newsticker-frames/background-label-orange.png +0 -0
- package/templates/all/broadcast/newsticker-frames/banner.png +0 -0
- package/templates/all/broadcast/newsticker-frames/index.html +229 -0
- package/templates/all/broadcast/newsticker-frames/manifest.json +20 -0
- package/templates/all/broadcast/weather/index.html +382 -0
- package/templates/all/broadcast/weather/manifest.json +104 -0
- package/templates/all/engagement/call-to-action/index.html +116 -0
- package/templates/all/engagement/call-to-action/manifest.json +12 -0
- package/templates/all/engagement/chat/index.html +121 -0
- package/templates/all/engagement/chat/manifest.json +32 -0
- package/templates/all/engagement/post/index.html +132 -0
- package/templates/all/engagement/post/manifest.json +31 -0
- package/templates/all/gambling/bingo/index.html +227 -0
- package/templates/all/gambling/bingo/manifest.json +144 -0
- package/templates/all/gambling/keno/index.html +202 -0
- package/templates/all/gambling/keno/manifest.json +129 -0
- package/templates/all/gambling/lottery/index.html +159 -0
- package/templates/all/gambling/lottery/manifest.json +95 -0
- package/templates/all/gambling/prize-wall/index.html +256 -0
- package/templates/all/gambling/prize-wall/manifest.json +108 -0
- package/templates/all/gambling/roulette/index.html +200 -0
- package/templates/all/gambling/roulette/manifest.json +78 -0
- package/templates/all/gaming/battleship/index.html +578 -0
- package/templates/all/gaming/battleship/manifest.json +279 -0
- package/templates/all/gaming/gta/mission/index.html +121 -0
- package/templates/all/gaming/gta/mission/manifest.json +38 -0
- package/templates/all/sports/football/scoreboard/index.html +115 -0
- package/templates/all/sports/football/scoreboard/manifest.json +54 -0
- package/templates/all/sports/tennis/scoreboard/index.html +120 -0
- package/templates/all/sports/tennis/scoreboard/manifest.json +92 -0
- package/templates/all/trivia/audience-poll/index.html +60 -0
- package/templates/all/trivia/audience-poll/manifest.json +26 -0
- package/templates/all/trivia/classic-quiz/index.html +49 -0
- package/templates/all/trivia/classic-quiz/manifest.json +15 -0
- package/templates/all/trivia/lightning-round/index.html +126 -0
- package/templates/all/trivia/lightning-round/manifest.json +27 -0
- package/templates/all/trivia/pasapalabra/index.html +249 -0
- package/templates/all/trivia/pasapalabra/manifest.json +68 -0
- package/templates/all/trivia/quiz-deck/index.html +58 -0
- package/templates/all/trivia/quiz-deck/manifest.json +25 -0
- package/templates/all/trivia/true-or-false/index.html +81 -0
- package/templates/all/trivia/true-or-false/manifest.json +27 -0
- package/templates/basic/broadcast/empty/index.html +46 -0
- package/templates/basic/broadcast/empty/manifest.json +7 -0
- package/templates/basic/broadcast/empty/nebular.medallion.transparent.png +0 -0
- package/templates/basic/broadcast/event-countdown/index.html +160 -0
- package/templates/basic/broadcast/event-countdown/logo.png +0 -0
- package/templates/basic/broadcast/event-countdown/manifest.json +33 -0
- package/templates/basic/broadcast/lower-third/index.html +77 -0
- package/templates/basic/broadcast/lower-third/manifest.json +8 -0
- package/templates/basic/broadcast/newsticker/index.html +153 -0
- package/templates/basic/broadcast/newsticker/manifest.json +21 -0
- package/templates/basic/broadcast/weather/index.html +382 -0
- package/templates/basic/broadcast/weather/manifest.json +104 -0
- package/templates/basic/engagement/call-to-action/index.html +116 -0
- package/templates/basic/engagement/call-to-action/manifest.json +12 -0
- package/templates/basic/engagement/chat/index.html +121 -0
- package/templates/basic/engagement/chat/manifest.json +32 -0
- package/templates/basic/engagement/post/index.html +132 -0
- package/templates/basic/engagement/post/manifest.json +31 -0
- package/templates/basic/sports/football/scoreboard/index.html +115 -0
- package/templates/basic/sports/football/scoreboard/manifest.json +54 -0
- package/templates/basic/sports/tennis/scoreboard/index.html +120 -0
- package/templates/basic/sports/tennis/scoreboard/manifest.json +92 -0
- package/templates/debug/testing/security-probe/forbidden.js +1 -0
- package/templates/debug/testing/security-probe/forbidden.json +1 -0
- package/templates/debug/testing/security-probe/forbidden.mp4 +1 -0
- package/templates/debug/testing/security-probe/forbidden.svg +1 -0
- package/templates/debug/testing/security-probe/index.html +367 -0
- package/templates/debug/testing/security-probe/manifest.json +13 -0
- package/templates/empty/broadcast/empty/index.html +46 -0
- package/templates/empty/broadcast/empty/manifest.json +7 -0
- package/templates/empty/broadcast/empty/nebular.medallion.transparent.png +0 -0
- package/templates/games/gambling/bingo/index.html +227 -0
- package/templates/games/gambling/bingo/manifest.json +56 -0
- package/templates/games/gambling/keno/index.html +202 -0
- package/templates/games/gambling/keno/manifest.json +129 -0
- package/templates/games/gambling/lottery/index.html +159 -0
- package/templates/games/gambling/lottery/manifest.json +95 -0
- package/templates/games/gambling/prize-wall/index.html +256 -0
- package/templates/games/gambling/prize-wall/manifest.json +108 -0
- package/templates/games/gambling/roulette/index.html +200 -0
- package/templates/games/gambling/roulette/manifest.json +78 -0
- package/templates/games/gaming/battleship/index.html +578 -0
- package/templates/games/gaming/battleship/manifest.json +279 -0
- package/templates/games/gaming/gta/mission/index.html +121 -0
- package/templates/games/gaming/gta/mission/manifest.json +38 -0
- package/templates/games/trivia/audience-poll/index.html +60 -0
- package/templates/games/trivia/audience-poll/manifest.json +26 -0
- package/templates/games/trivia/classic-quiz/index.html +49 -0
- package/templates/games/trivia/classic-quiz/manifest.json +15 -0
- package/templates/games/trivia/lightning-round/index.html +126 -0
- package/templates/games/trivia/lightning-round/manifest.json +27 -0
- package/templates/games/trivia/pasapalabra/index.html +249 -0
- package/templates/games/trivia/pasapalabra/manifest.json +68 -0
- package/templates/games/trivia/quiz-deck/index.html +58 -0
- package/templates/games/trivia/quiz-deck/manifest.json +25 -0
- package/templates/games/trivia/true-or-false/index.html +81 -0
- package/templates/games/trivia/true-or-false/manifest.json +27 -0
- package/tool/cli.cjs +7 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"instructions": "Build the question set, then select a question number and press Update. The statement appears, its answer is revealed automatically, and the overlay then hides until another question is selected.",
|
|
3
|
+
"meta": {
|
|
4
|
+
"questions": {
|
|
5
|
+
"type": "set",
|
|
6
|
+
"phase": "edit",
|
|
7
|
+
"label": "Questions", "hint": "Statements and their true-or-false answers",
|
|
8
|
+
"max": 30,
|
|
9
|
+
"container": "big",
|
|
10
|
+
"default": [
|
|
11
|
+
{"category": "SCIENCE", "statement": "Lightning never strikes the same place twice.", "correct": "false", "explanation": "Lightning can strike the same place repeatedly."},
|
|
12
|
+
{"category": "GEOGRAPHY", "statement": "The Great Wall of China is visible from the Moon with the naked eye.", "correct": "false", "explanation": "It is far too narrow to be distinguished from the Moon."},
|
|
13
|
+
{"category": "NATURE", "statement": "Octopuses have three hearts.", "correct": "true", "explanation": "Two pump blood to the gills and one pumps it around the body."}
|
|
14
|
+
],
|
|
15
|
+
"meta": {
|
|
16
|
+
"category": {"type": "string", "phase": "edit", "label": "Category", "hint": "Enter category"},
|
|
17
|
+
"statement": {"type": "string", "phase": "edit", "label": "Statement", "hint": "Enter statement"},
|
|
18
|
+
"correct": {"type": "choose", "phase": "edit", "label": "Correct answer", "hint": "Whether the statement is true or false", "choose": ["true", "false"], "default": "true"},
|
|
19
|
+
"explanation": {"type": "string", "phase": "edit", "label": "Explanation", "hint": "Enter explanation"}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"control/current": {"type": "selector", "phase": "play", "label": "Question to show", "set": "questions", "default": 1},
|
|
23
|
+
"timing/questionSeconds": {"type": "number", "phase": "edit", "label": "Question time", "hint": "Enter seconds", "min": 1, "max": 300, "default": 10},
|
|
24
|
+
"timing/answerSeconds": {"type": "number", "phase": "edit", "label": "Answer time", "hint": "Enter seconds", "min": 1, "max": 60, "default": 10},
|
|
25
|
+
"style/accent": {"type": "color", "phase": "edit", "label": "Accent", "hint": "Accent colour used by the game graphic", "default": "#19d3ae"}
|
|
26
|
+
}
|
|
27
|
+
}
|
package/tool/cli.cjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*!
|
|
3
|
+
* Nebular Overlay SDK
|
|
4
|
+
* Copyright (c) 2026 Nebular Streams
|
|
5
|
+
* All rights reserved.
|
|
6
|
+
*/
|
|
7
|
+
"use strict";var e=require("node:fs"),t=require("node:os"),n=require("node:path"),o=require("node:child_process"),r=require("node:http");const i=o,s=e,a=r,c=n,l="127.0.0.1",p=/^[A-Za-z0-9_-]+$/,f=/^[A-Za-z0-9_.-]+$/,u=new Set([".css",".jpg",".jpeg",".png",".webp",".otf",".ttf",".woff",".woff2"]),d={".css":"text/css; charset=utf-8",".html":"text/html; charset=utf-8",".jpg":"image/jpeg",".jpeg":"image/jpeg",".json":"application/json; charset=utf-8",".png":"image/png",".webp":"image/webp",".otf":"font/otf",".ttf":"font/ttf",".woff":"font/woff",".woff2":"font/woff2",".js":"text/javascript; charset=utf-8"};function h(e,t){const n=new Error(t);return n.status=e,n}function m(e,t){const n=c.relative(e,t);return""===n||!n.startsWith("..")&&!c.isAbsolute(n)}function w(e,t=""){const n=c.join(e,t);return s.existsSync(c.join(n,"index.html"))?t?[t.split(c.sep).join("/")]:[]:s.readdirSync(n,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.name.startsWith(".")&&"node_modules"!==e.name).flatMap(n=>w(e,c.join(t,n.name)))}function y(e,t,n){const o=JSON.stringify(n);e.writeHead(t,{"Content-Type":d[".json"],"Content-Length":Buffer.byteLength(o),"Cache-Control":"no-store","X-Content-Type-Options":"nosniff"}),e.end(o)}function v(e,t,n){e.writeHead(t,{"Content-Type":"text/plain; charset=utf-8","Content-Length":Buffer.byteLength(n),"Cache-Control":"no-store","X-Content-Type-Options":"nosniff"}),e.end(n)}function g(e,t,n,o,r={}){t.writeHead(200,{"Content-Type":d[o]||"application/octet-stream","Content-Length":n.length,"Cache-Control":"no-store","X-Content-Type-Options":"nosniff",...r}),"HEAD"===e.method?t.end():t.end(n)}function $(e){let t;try{t=s.statSync(e)}catch(e){if("ENOENT"===e.code)throw h(404,"File not found");throw e}if(!t.isFile())throw h(404,"File not found");if(t.size>5242880)throw h(413,"File exceeds 5 MiB");return s.readFileSync(e)}function S(e){try{return e.split("/").filter(Boolean).map(decodeURIComponent)}catch{throw h(400,"Invalid URL path")}}function x(e){return e.length>0&&e.every(e=>f.test(e)&&!e.startsWith("."))}function j(e,t){const n=e.toLowerCase().lastIndexOf("</body>");return n>=0?`${e.slice(0,n)}${t}${e.slice(n)}`:`${e}${t}`}function C(e,t){return new Promise((n,o)=>{e.once("error",o),e.listen(t,l,()=>{e.removeListener("error",o),n()})})}const b=e,E=t,k=n,O=async function(e=".",t=[]){const n=c.resolve(e);if(!s.existsSync(n)||!s.statSync(n).isDirectory())throw new Error(`Template directory does not exist: ${e}`);const{port:o,shouldOpen:r}=function(e){let t=4173,n=!0;for(let o=0;o<e.length;o++){const r=e[o];if("--no-open"===r)n=!1;else if("--port"===r)t=Number(e[++o]);else{if(!r.startsWith("--port="))throw new Error(`Unknown preview option: ${r}`);t=Number(r.slice(7))}}if(!Number.isInteger(t)||t<1||t>65534)throw new Error("Preview port must be an integer between 1 and 65534");return{port:t,shouldOpen:n}}(t),f=o+1,d=`http://${l}:${o}`,b=`http://${l}:${f}`,E=c.resolve(__dirname,"..","preview"),k=function(e,t){return a.createServer((n,o)=>{try{if(!["GET","HEAD"].includes(n.method))throw h(405,"Method not allowed");const r=new URL(n.url,"http://preview.invalid");if(!r.pathname.startsWith("/templates/"))throw h(404,"Not found");const i=S(r.pathname.slice(10)),s=w(e).sort((e,t)=>t.length-e.length).find(e=>e.split("/").every((e,t)=>i[t]===e));if(!s)throw h(404,"Template not found");const a=s.split("/");let l=i.slice(a.length);if(0===l.length&&(l=["index.html"]),!x(l))throw h(400,"Invalid template resource path");const p=c.extname(l.at(-1)).toLowerCase(),f=1===l.length&&"index.html"===l[0];if(!f&&!u.has(p))throw h(404,"Unsupported template resource");const d=c.join(e,...a),y=c.resolve(d,...l);if(!m(d,y))throw h(400,"Invalid template resource path");let v=$(y);f&&(v=Buffer.from(j(v.toString("utf8"),function(e){return`<script>\n\t\t(() => {\n\t\t const previewOrigin = ${JSON.stringify(e)};\n\t\t window.addEventListener("message", (event) => {\n\t\t\tif (event.source !== window.parent || event.origin !== previewOrigin) return;\n\t\t\tif (event.data?.type !== "nebular-overlay:update") return;\n\t\t\twindow.dispatchEvent(new CustomEvent("nebular-overlay:update", {detail: event.data.values || {}}));\n\t\t });\n\t\t})();\n\t<\/script>`}(t)))),g(n,o,v,p,{"Content-Security-Policy":["default-src 'none'","script-src 'unsafe-inline'","style-src 'self' 'unsafe-inline'","img-src 'self' data:","font-src 'self'","connect-src 'none'","frame-src 'none'","media-src 'none'","object-src 'none'","worker-src 'none'","base-uri 'none'","form-action 'none'"].join("; "),"Permissions-Policy":"camera=(), microphone=(), geolocation=(), display-capture=(), usb=(), serial=(), hid=(), clipboard-read=()","Referrer-Policy":"no-referrer"})}catch(e){v(o,e.status||500,e.status?e.message:"Preview server error")}})}(n,d),O=function(e,t,n){return a.createServer((o,r)=>{try{if(!["GET","HEAD"].includes(o.method))throw h(405,"Method not allowed");const i=new URL(o.url,"http://preview.invalid");if("/api/templates"===i.pathname)return y(r,200,{templates:w(e).sort()});if("/api/manifest"===i.pathname){const t=i.searchParams.get("id");if(!function(e){return"string"==typeof e&&e.length>0&&e.split("/").every(e=>p.test(e))}(t))throw h(400,"Invalid template id");const n=c.resolve(e,...t.split("/"));if(!m(e,n)||!s.existsSync(c.join(n,"index.html")))throw h(404,"Template not found");return y(r,200,JSON.parse($(c.join(n,"manifest.json")).toString("utf8")))}const a="/"===i.pathname?"index.html":i.pathname.slice(1),l=S(a);if(!x(l))throw h(400,"Invalid application resource path");const f=c.resolve(t,...l);if(!m(t,f))throw h(400,"Invalid application resource path");const u=c.extname(f).toLowerCase();let d=$(f);if("index.html"===a){const e=`<script>window.__NEBULAR_OVERLAY_PREVIEW__=${JSON.stringify({templateOrigin:n})};<\/script>`;d=Buffer.from(j(d.toString("utf8"),e))}g(o,r,d,u)}catch(e){if(e instanceof SyntaxError)return v(r,422,`Invalid template manifest: ${e.message}`);v(r,e.status||500,e.status?e.message:"Preview server error")}})}(n,E,b);try{await C(k,f),await C(O,o)}catch(e){throw k.close(),O.close(),e}console.log(`Previewing ${n}`),console.log(`Application: ${d}`),console.log(`Templates: ${b}`),console.log("Press Ctrl-C to stop."),r&&function(e){let t,n;"darwin"===process.platform?[t,n]=["open",[e]]:"win32"===process.platform?[t,n]=["cmd",["/c","start","",e]]:[t,n]=["xdg-open",[e]];const o=i.spawn(t,n,{detached:!0,stdio:"ignore"});o.on("error",()=>{}),o.unref()}(d),await new Promise(e=>{let t=!1;const n=()=>{if(t)return;t=!0;let n=2;const o=()=>{n-=1,0===n&&e()};O.close(o),k.close(o)};process.once("SIGINT",n),process.once("SIGTERM",n)})},I="nebular-overlay-sdk",L="nebular-overlay",T="studio.nebular.tv",_=k.resolve(__dirname,".."),D=new Set(["init","preview","skill"]);function N(){return`Usage:\n ${I} init <destination> <template>\n ${I} preview [directory] [--port <port>] [--no-open]\n ${I} skill install [--force]\n\t\nInstructions: \n - Using the SDK: https://${T}/doc/sdk/\n - Creating templates: https://${T}/doc/sdk/templates\n`}function P(e,t=1){const n=new Error(e);throw n.exitCode=t,n}function U(){return b.readdirSync(k.join(_,"templates"),{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name).sort()}function R(e,t){e&&t||P(`Usage: ${I} init <destination> <template>\nAvailable templates: ${U().join(", ")}`);const n=k.resolve(e),o=k.join(_,"templates",t);b.existsSync(n)&&P(`Destination already exists: ${e}`),b.existsSync(o)&&b.statSync(o).isDirectory()||P(`Unknown template: ${t}\nAvailable templates: ${U().join(", ")}`),b.cpSync(o,n,{recursive:!0,errorOnExist:!0}),console.log(`Initialized ${t} template in ${n}`)}async function A(e=".",...t){e.startsWith("--")&&(t.unshift(e),e="."),console.log("-----------------------------------\nNebular Overlay SDK v1.0.0\n(c) 2026 Nebular Streams <software@nebular.tv>\nAll Rights Reserved\n-----------------------------------"),await O(e,t)}function H(e,...t){"install"!==e&&P(`Usage: ${I} skill install [--force]`),function(e){const t=e.filter(e=>"--force"!==e);t.length&&P(`Unknown skill install option: ${t[0]}`);const n=e.includes("--force"),o=k.join(_,"skill",L),r=process.env.CODEX_HOME?k.resolve(process.env.CODEX_HOME):k.join(E.homedir(),".codex"),i=k.join(r,"skills"),s=k.join(i,L);b.existsSync(k.join(o,"SKILL.md"))||P(`Packaged skill is missing: ${o}`),b.existsSync(s)&&!n&&P(`Skill is already installed at ${s}\nRun again with --force to replace it.`),b.mkdirSync(i,{recursive:!0});const a=k.join(i,`.${L}-install-${process.pid}-${Date.now()}`);if(b.cpSync(o,a,{recursive:!0,errorOnExist:!0}),b.existsSync(s)){const e=k.join(i,`.${L}-backup-${process.pid}-${Date.now()}`);b.renameSync(s,e);try{b.renameSync(a,s),b.rmSync(e,{recursive:!0,force:!0})}catch(t){throw b.existsSync(a)&&b.rmSync(a,{recursive:!0,force:!0}),b.existsSync(s)||b.renameSync(e,s),t}}else b.renameSync(a,s);console.log(`Installed ${L} skill in ${s}`),console.log("Restart or refresh Codex, then invoke it with $nebular-overlay.")}(t)}(async function(e){if(e.includes("--help")||e.includes("-h")||0===e.length)return void console.log(N());const[t,n]=function(e){if(D.has(e[0]))return[e[0],e.slice(1)];P(`Unknown command: ${e[0]}\n\n${N()}`)}(e);await{init:R,preview:A,skill:H}[t](...n)})(process.argv.slice(2)).catch(e=>{console.error(`nebular-overlay: ${e.message}`),process.exitCode=e.exitCode||1}),module.exports={};
|