@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,249 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="es">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<style>
|
|
7
|
+
* {
|
|
8
|
+
box-sizing: border-box
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
html, body {
|
|
12
|
+
width: 100%;
|
|
13
|
+
height: 100%;
|
|
14
|
+
margin: 0;
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
background: transparent;
|
|
17
|
+
font-family: Inter, Arial, sans-serif
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
body {
|
|
21
|
+
color: #fff
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.game {
|
|
25
|
+
position: relative;
|
|
26
|
+
width: 100%;
|
|
27
|
+
height: 100%
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.letters {
|
|
31
|
+
position: absolute;
|
|
32
|
+
inset: 0;
|
|
33
|
+
aspect-ratio: 1 / 1;
|
|
34
|
+
height: 900px;
|
|
35
|
+
left: calc(50% - 450px);
|
|
36
|
+
top: calc(50% - 450px);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.letter {
|
|
40
|
+
position: absolute;
|
|
41
|
+
left: 50%;
|
|
42
|
+
top: 50%;
|
|
43
|
+
display: grid;
|
|
44
|
+
place-items: center;
|
|
45
|
+
width: 4.25rem;
|
|
46
|
+
height: 4.25rem;
|
|
47
|
+
border: .22rem solid #8cddff;
|
|
48
|
+
border-radius: 50%;
|
|
49
|
+
color: #fff;
|
|
50
|
+
background: linear-gradient(145deg, #198ee8, #0758b4);
|
|
51
|
+
box-shadow: 0 .55rem 1.2rem #0008, inset 0 .14rem .4rem #ffffff4a, 0 0 1rem #159cff55;
|
|
52
|
+
font-size: 1.75rem;
|
|
53
|
+
font-weight: 950;
|
|
54
|
+
transform: translate(-50%, -50%);
|
|
55
|
+
transition: background .3s ease, border-color .3s ease, box-shadow .3s ease, transform .3s ease
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.letter.correct {
|
|
59
|
+
border-color: #a9ffd1;
|
|
60
|
+
background: linear-gradient(145deg, #24cf75, #087b42);
|
|
61
|
+
box-shadow: 0 .55rem 1.2rem #0008, inset 0 .14rem .4rem #ffffff52, 0 0 1.4rem #20e77d99
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.letter.failed {
|
|
65
|
+
border-color: #ffc1c6;
|
|
66
|
+
background: linear-gradient(145deg, #f34657, #a80e23);
|
|
67
|
+
box-shadow: 0 .55rem 1.2rem #0008, inset 0 .14rem .4rem #ffffff45, 0 0 1.4rem #ff334d88
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.answers-label {
|
|
71
|
+
color: #8bcce9;
|
|
72
|
+
font-size: .86rem;
|
|
73
|
+
font-weight: 850;
|
|
74
|
+
letter-spacing: .32em
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.clock, .answers-holder {
|
|
78
|
+
--progress: 1;
|
|
79
|
+
position: absolute;
|
|
80
|
+
display: grid;
|
|
81
|
+
place-items: center;
|
|
82
|
+
width: 10rem;
|
|
83
|
+
height: 10rem;
|
|
84
|
+
margin: 1.35rem auto 1rem;
|
|
85
|
+
border-radius: 50%;
|
|
86
|
+
background: conic-gradient(white, calc(var(--progress) * 1turn), #132b45 0);
|
|
87
|
+
filter: drop-shadow(0 0 1.5rem #159cff55);
|
|
88
|
+
bottom: 0;
|
|
89
|
+
left: 2rem;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.clock::before {
|
|
93
|
+
content: "";
|
|
94
|
+
position: absolute;
|
|
95
|
+
inset: .8rem;
|
|
96
|
+
border-radius: inherit;
|
|
97
|
+
background: radial-gradient(circle at 50% 35%, orange, #ff4400 72%);
|
|
98
|
+
border: .1rem solid #a5e7ff55;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.seconds {
|
|
102
|
+
position: relative;
|
|
103
|
+
font-size: 5rem;
|
|
104
|
+
font-weight: 950;
|
|
105
|
+
font-variant-numeric: tabular-nums;
|
|
106
|
+
line-height: 1
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.clock-status {
|
|
110
|
+
min-height: 1.3rem;
|
|
111
|
+
color: #62d2ff;
|
|
112
|
+
font-size: .9rem;
|
|
113
|
+
font-weight: 850;
|
|
114
|
+
letter-spacing: .28em;
|
|
115
|
+
margin: 1rem;
|
|
116
|
+
position: absolute;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.divider {
|
|
120
|
+
height: .1rem;
|
|
121
|
+
margin: 2rem 0;
|
|
122
|
+
background: linear-gradient(90deg, transparent, #7cdcff66, transparent)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#answers {
|
|
126
|
+
background: #00aa00;
|
|
127
|
+
width: 5rem;
|
|
128
|
+
height: 5rem;
|
|
129
|
+
border-radius: 10000px;
|
|
130
|
+
line-height: 5rem;
|
|
131
|
+
text-align: center;
|
|
132
|
+
font-weight: bold;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.answers-holder {
|
|
136
|
+
left: 11rem;
|
|
137
|
+
width: 6rem;
|
|
138
|
+
height: 6rem;
|
|
139
|
+
color: white;
|
|
140
|
+
font-size: 2.5rem;
|
|
141
|
+
bottom: 1.5rem;
|
|
142
|
+
z-index: -1;
|
|
143
|
+
}
|
|
144
|
+
</style>
|
|
145
|
+
</head>
|
|
146
|
+
<body>
|
|
147
|
+
<main class="game">
|
|
148
|
+
<section class="halo">
|
|
149
|
+
<div class="letters" id="letters"></div>
|
|
150
|
+
</section>
|
|
151
|
+
|
|
152
|
+
<div class="clock" id="clock">
|
|
153
|
+
<div class="seconds" id="seconds">60</div>
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<div class="answers-holder">
|
|
157
|
+
<div class="answers" id="answers">0</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<div class="clock-status" id="clock-status">PAUSA</div>
|
|
161
|
+
|
|
162
|
+
</main>
|
|
163
|
+
|
|
164
|
+
<script>
|
|
165
|
+
const
|
|
166
|
+
LETTERS = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "Ñ", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"],
|
|
167
|
+
$ = id => document.getElementById(id),
|
|
168
|
+
cells = new Map();
|
|
169
|
+
|
|
170
|
+
let
|
|
171
|
+
correct = new Set(),
|
|
172
|
+
failed = new Set(),
|
|
173
|
+
remaining = Number($("seconds").textContent),
|
|
174
|
+
total = remaining,
|
|
175
|
+
running = false,
|
|
176
|
+
startedAt = 0,
|
|
177
|
+
startedWith = remaining,
|
|
178
|
+
animationGeneration = 0;
|
|
179
|
+
|
|
180
|
+
LETTERS.forEach((letter, index) => {
|
|
181
|
+
const
|
|
182
|
+
angle = (index / LETTERS.length) * Math.PI * 2 - Math.PI / 2,
|
|
183
|
+
cell = document.createElement("div");
|
|
184
|
+
|
|
185
|
+
cell.className = "letter";
|
|
186
|
+
cell.textContent = letter;
|
|
187
|
+
cell.style.left = `${50 + Math.cos(angle) * 43}%`;
|
|
188
|
+
cell.style.top = `${50 + Math.sin(angle) * 43}%`;
|
|
189
|
+
$("letters").append(cell);
|
|
190
|
+
cells.set(letter, cell)
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
function renderLetters() {
|
|
194
|
+
cells.forEach((cell, letter) => {
|
|
195
|
+
cell.classList.toggle("correct", correct.has(letter));
|
|
196
|
+
cell.classList.toggle("failed", !correct.has(letter) && failed.has(letter));
|
|
197
|
+
});
|
|
198
|
+
$("answers").textContent = correct.size
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function renderClock() {
|
|
202
|
+
$("seconds").textContent = Math.max(0, Math.ceil(remaining));
|
|
203
|
+
$("clock").style.setProperty("--progress", total > 0 ? Math.max(0, remaining / total) : 0);
|
|
204
|
+
$("clock-status").textContent = remaining <= 0 ? "FIN" : running ? "EN MARCHA" : "PAUSA"
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function readElapsed(now = performance.now()) {
|
|
208
|
+
if (running) remaining = Math.max(0, startedWith - (now - startedAt) / 1000);
|
|
209
|
+
return remaining
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function animateClock(generation) {
|
|
213
|
+
if (!running || generation !== animationGeneration) return;
|
|
214
|
+
readElapsed();
|
|
215
|
+
if (remaining <= 0) running = false;
|
|
216
|
+
renderClock();
|
|
217
|
+
if (running) requestAnimationFrame(() => animateClock(generation))
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function setSeconds(value) {
|
|
221
|
+
remaining = Math.max(0, Number(value) || 0);
|
|
222
|
+
total = Math.max(remaining, 1);
|
|
223
|
+
startedWith = remaining;
|
|
224
|
+
startedAt = performance.now();
|
|
225
|
+
renderClock()
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function setClockStatus(status) {
|
|
229
|
+
const shouldRun = status === "running" && remaining > 0;
|
|
230
|
+
readElapsed();
|
|
231
|
+
running = shouldRun;
|
|
232
|
+
animationGeneration++;
|
|
233
|
+
if (running) {
|
|
234
|
+
startedWith = remaining;
|
|
235
|
+
startedAt = performance.now();
|
|
236
|
+
requestAnimationFrame(() => animateClock(animationGeneration))
|
|
237
|
+
} else renderClock()
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
window.addEventListener("nebular-overlay:update", ({detail}) => {
|
|
241
|
+
if (Array.isArray(detail.game?.correct)) correct = new Set(detail.game.correct.map(String));
|
|
242
|
+
if (Array.isArray(detail.game?.failed)) failed = new Set(detail.game.failed.map(String));
|
|
243
|
+
if (Array.isArray(detail.game?.correct) || Array.isArray(detail.game?.failed)) renderLetters();
|
|
244
|
+
if (detail.timer?.seconds != null) setSeconds(detail.timer.seconds);
|
|
245
|
+
if (detail.timer?.status != null) setClockStatus(detail.timer.status)
|
|
246
|
+
});
|
|
247
|
+
</script>
|
|
248
|
+
</body>
|
|
249
|
+
</html>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"instructions": "Set the available time, mark letters as correct or failed as answers are given, and use the clock control to run or pause the countdown. Press Update after each game-state change. Use Reset game to clear all answers, restore 60 seconds and pause the clock, then press Update.",
|
|
3
|
+
"meta": {
|
|
4
|
+
"timer/seconds": {
|
|
5
|
+
"type": "number",
|
|
6
|
+
"phase": "edit",
|
|
7
|
+
"label": "Time", "hint": "Enter seconds",
|
|
8
|
+
"container": "small",
|
|
9
|
+
"min": 0,
|
|
10
|
+
"max": 600,
|
|
11
|
+
"default": 60
|
|
12
|
+
},
|
|
13
|
+
"timer/status": {
|
|
14
|
+
"type": "choose",
|
|
15
|
+
"phase": "play",
|
|
16
|
+
"label": "Clock", "hint": "Start or pause the game clock",
|
|
17
|
+
"choose": [
|
|
18
|
+
"running",
|
|
19
|
+
"paused"
|
|
20
|
+
],
|
|
21
|
+
"default": "paused"
|
|
22
|
+
},
|
|
23
|
+
"game/correct": {
|
|
24
|
+
"type": "multi",
|
|
25
|
+
"phase": "play",
|
|
26
|
+
"container": "big",
|
|
27
|
+
"label": "Correct answers", "hint": "Letters answered correctly",
|
|
28
|
+
"choose": [
|
|
29
|
+
"A", "B", "C", "D", "E", "F", "G", "H", "I",
|
|
30
|
+
"J", "K", "L", "M", "N", "Ñ", "O", "P", "Q",
|
|
31
|
+
"R", "S", "T", "U", "V", "W", "X", "Y", "Z"
|
|
32
|
+
],
|
|
33
|
+
"cols": 9,
|
|
34
|
+
"rows": 3,
|
|
35
|
+
"responsive": true,
|
|
36
|
+
"minCellWidth": "2.54rem",
|
|
37
|
+
"default": []
|
|
38
|
+
},
|
|
39
|
+
"game/failed": {
|
|
40
|
+
"type": "multi",
|
|
41
|
+
"phase": "play",
|
|
42
|
+
"container": "big",
|
|
43
|
+
"label": "Failed answers", "hint": "Letters answered incorrectly",
|
|
44
|
+
"choose": [
|
|
45
|
+
"A", "B", "C", "D", "E", "F", "G", "H", "I",
|
|
46
|
+
"J", "K", "L", "M", "N", "Ñ", "O", "P", "Q",
|
|
47
|
+
"R", "S", "T", "U", "V", "W", "X", "Y", "Z"
|
|
48
|
+
],
|
|
49
|
+
"cols": 9,
|
|
50
|
+
"rows": 3,
|
|
51
|
+
"responsive": true,
|
|
52
|
+
"minCellWidth": "2.54rem",
|
|
53
|
+
"default": []
|
|
54
|
+
},
|
|
55
|
+
"game/reset": {
|
|
56
|
+
"type": "reset",
|
|
57
|
+
"phase": "play",
|
|
58
|
+
"icon": "undo",
|
|
59
|
+
"text": "Reset game", "hint": "Clear all answers and return the clock to its initial state",
|
|
60
|
+
"values": {
|
|
61
|
+
"timer/seconds": 60,
|
|
62
|
+
"timer/status": "paused",
|
|
63
|
+
"game/correct": [],
|
|
64
|
+
"game/failed": []
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Quiz Deck</title>
|
|
6
|
+
<style>
|
|
7
|
+
* { box-sizing: border-box; }
|
|
8
|
+
html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; background: transparent; }
|
|
9
|
+
body { --accent: #7c5cff; color: white; font-family: Inter, Arial, sans-serif; }
|
|
10
|
+
.deck { position: absolute; left: 5%; right: 5%; bottom: 5%; display: grid; grid-template-columns: 230px 1fr; min-height: 235px; border-radius: 25px; overflow: hidden; background: rgba(12,13,25,.96); border: 2px solid #ffffff22; box-shadow: 0 22px 65px #000a; }
|
|
11
|
+
.rail { display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 20px; background: linear-gradient(155deg, var(--accent), color-mix(in srgb, var(--accent) 45%, #080918)); text-align: center; }
|
|
12
|
+
.count { font-size: 67px; line-height: 1; font-weight: 950; }
|
|
13
|
+
.total { margin-top: 8px; font-size: 18px; font-weight: 800; letter-spacing: .12em; opacity: .82; }
|
|
14
|
+
.title { margin-top: 20px; font-size: 18px; font-weight: 950; letter-spacing: .1em; }
|
|
15
|
+
.card { display: flex; flex-direction: column; justify-content: center; padding: 28px 42px; }
|
|
16
|
+
.eyebrow { margin-bottom: 12px; color: var(--accent); font-size: 20px; font-weight: 900; letter-spacing: .18em; }
|
|
17
|
+
.copy { font-size: 44px; line-height: 1.14; font-weight: 850; text-wrap: balance; transition: opacity .18s ease, transform .18s ease; }
|
|
18
|
+
.answer .eyebrow::after { content: "ANSWER"; }
|
|
19
|
+
.question .eyebrow::after { content: "QUESTION"; }
|
|
20
|
+
.answer .copy { color: color-mix(in srgb, var(--accent) 70%, white); }
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<section class="deck" id="deck">
|
|
25
|
+
<aside class="rail"><div class="count" id="current">1</div><div class="total" id="total">OF 3</div><div class="title" id="title">THE BIG QUIZ</div></aside>
|
|
26
|
+
<main class="card question" id="card"><div class="eyebrow"></div><div class="copy" id="copy">What is the largest ocean on Earth?</div></main>
|
|
27
|
+
</section>
|
|
28
|
+
<script>
|
|
29
|
+
const state = {
|
|
30
|
+
questions: [
|
|
31
|
+
{question: "What is the largest ocean on Earth?", answer: "The Pacific Ocean"},
|
|
32
|
+
{question: "How many sides does a dodecagon have?", answer: "12"},
|
|
33
|
+
{question: "Which element has the symbol Au?", answer: "Gold"}
|
|
34
|
+
],
|
|
35
|
+
current: 1, display: "question"
|
|
36
|
+
};
|
|
37
|
+
function render() {
|
|
38
|
+
const count = state.questions.length;
|
|
39
|
+
const index = Math.max(0, Math.min(count - 1, Number(state.current || 1) - 1));
|
|
40
|
+
const item = state.questions[index] || {question: "Ready for the next question?", answer: ""};
|
|
41
|
+
const answer = state.display === "answer";
|
|
42
|
+
document.getElementById("current").textContent = count ? index + 1 : 0;
|
|
43
|
+
document.getElementById("total").textContent = `OF ${count}`;
|
|
44
|
+
document.getElementById("copy").textContent = answer ? item.answer : item.question;
|
|
45
|
+
document.getElementById("card").className = `card ${answer ? "answer" : "question"}`;
|
|
46
|
+
}
|
|
47
|
+
window.addEventListener("nebular-overlay:update", ({detail = {}}) => {
|
|
48
|
+
if (Array.isArray(detail.questions)) state.questions = detail.questions;
|
|
49
|
+
if (detail.control?.current != null) state.current = detail.control.current;
|
|
50
|
+
if (detail.control?.display != null) state.display = detail.control.display;
|
|
51
|
+
if (detail.content?.title != null) document.getElementById("title").textContent = detail.content.title;
|
|
52
|
+
if (detail.style?.accent != null) document.body.style.setProperty("--accent", detail.style.accent);
|
|
53
|
+
render();
|
|
54
|
+
});
|
|
55
|
+
render();
|
|
56
|
+
</script>
|
|
57
|
+
</body>
|
|
58
|
+
</html>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"instructions": "Build the question-and-answer deck, select the current question number and publish it in question mode. Switch Display to answer and press Update when the answer should be revealed.",
|
|
3
|
+
"meta": {
|
|
4
|
+
"content/title": {"type": "string", "phase": "edit", "label": "Quiz title", "hint": "Enter quiz title", "default": "THE BIG QUIZ"},
|
|
5
|
+
"questions": {
|
|
6
|
+
"type": "set",
|
|
7
|
+
"phase": "edit",
|
|
8
|
+
"label": "Question deck", "hint": "Ordered collection of questions and possible answers",
|
|
9
|
+
"max": 20,
|
|
10
|
+
"container": "big",
|
|
11
|
+
"default": [
|
|
12
|
+
{"question": "What is the largest ocean on Earth?", "answer": "The Pacific Ocean"},
|
|
13
|
+
{"question": "How many sides does a dodecagon have?", "answer": "12"},
|
|
14
|
+
{"question": "Which element has the symbol Au?", "answer": "Gold"}
|
|
15
|
+
],
|
|
16
|
+
"meta": {
|
|
17
|
+
"question": {"type": "string", "phase": "edit", "label": "Question", "hint": "Enter question"},
|
|
18
|
+
"answer": {"type": "string", "phase": "edit", "label": "Answer", "hint": "Enter answer"}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"control/current": {"label": "Current question", "phase": "play", "type": "selector", "set": "questions"},
|
|
22
|
+
"control/display": {"type": "choose", "phase": "play", "label": "Display", "hint": "Choose which part of the current card viewers see", "choose": ["question", "answer"], "default": "question"},
|
|
23
|
+
"style/accent": {"type": "color", "phase": "edit", "label": "Accent", "hint": "Accent colour used by the quiz graphic", "default": "#7c5cff"}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>True or False</title>
|
|
6
|
+
<style>
|
|
7
|
+
* { box-sizing: border-box; }
|
|
8
|
+
html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; background: transparent; }
|
|
9
|
+
body { --accent: #19d3ae; color: #fff; font-family: Inter, Arial, sans-serif; }
|
|
10
|
+
.panel { position: absolute; left: 5%; right: 5%; bottom: 5%; padding: 25px 30px 28px; border-radius: 26px; background: rgba(7,17,22,.96); border: 2px solid #ffffff24; box-shadow: 0 24px 64px #000a; transition: opacity .35s ease, transform .35s ease; }
|
|
11
|
+
.panel.hidden { opacity: 0; transform: translateY(35px); }
|
|
12
|
+
.header { display: flex; align-items: center; gap: 18px; margin-bottom: 16px; }
|
|
13
|
+
.category { padding: 9px 16px; border-radius: 999px; color: #071613; background: var(--accent); font-size: 20px; font-weight: 950; letter-spacing: .12em; }
|
|
14
|
+
.statement { font-size: 39px; line-height: 1.14; font-weight: 850; text-wrap: balance; }
|
|
15
|
+
.choices { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
|
16
|
+
.choice { display: grid; place-items: center; height: 82px; border: 2px solid #ffffff22; border-radius: 17px; background: #ffffff0a; font-size: 34px; font-weight: 950; letter-spacing: .08em; transition: .35s ease; }
|
|
17
|
+
.revealed .choice:not(.correct) { opacity: .25; }
|
|
18
|
+
.revealed .choice.correct { color: #071613; background: var(--accent); border-color: white; box-shadow: 0 0 32px color-mix(in srgb, var(--accent) 50%, transparent); }
|
|
19
|
+
.explanation { display: none; margin-top: 15px; color: #d9fff7; font-size: 25px; font-weight: 650; }
|
|
20
|
+
.revealed .explanation { display: block; }
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<section class="panel" id="panel">
|
|
25
|
+
<div class="header"><span class="category" id="category">SCIENCE</span><div class="statement" id="statement">Lightning never strikes the same place twice.</div></div>
|
|
26
|
+
<div class="choices"><div class="choice" data-choice="true">TRUE</div><div class="choice" data-choice="false">FALSE</div></div>
|
|
27
|
+
<div class="explanation" id="explanation">Lightning can strike the same place repeatedly.</div>
|
|
28
|
+
</section>
|
|
29
|
+
<script>
|
|
30
|
+
const state = {
|
|
31
|
+
questions: [
|
|
32
|
+
{category: "SCIENCE", statement: "Lightning never strikes the same place twice.", correct: "false", explanation: "Lightning can strike the same place repeatedly."},
|
|
33
|
+
{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."},
|
|
34
|
+
{category: "NATURE", statement: "Octopuses have three hearts.", correct: "true", explanation: "Two pump blood to the gills and one pumps it around the body."}
|
|
35
|
+
],
|
|
36
|
+
current: 1,
|
|
37
|
+
timing: {questionSeconds: 8, answerSeconds: 4},
|
|
38
|
+
timers: []
|
|
39
|
+
};
|
|
40
|
+
const dom = {
|
|
41
|
+
body: document.body,
|
|
42
|
+
panel: document.getElementById("panel"),
|
|
43
|
+
category: document.getElementById("category"),
|
|
44
|
+
statement: document.getElementById("statement"),
|
|
45
|
+
explanation: document.getElementById("explanation"),
|
|
46
|
+
choices: [...document.querySelectorAll("[data-choice]")]
|
|
47
|
+
};
|
|
48
|
+
const seconds = (value, fallback) => Math.max(.25, Number(value) || fallback);
|
|
49
|
+
function clearSequence() {
|
|
50
|
+
state.timers.forEach(clearTimeout);
|
|
51
|
+
state.timers = [];
|
|
52
|
+
}
|
|
53
|
+
function startCurrentQuestion() {
|
|
54
|
+
clearSequence();
|
|
55
|
+
const index = Math.max(0, Math.min(state.questions.length - 1, Number(state.current || 1) - 1));
|
|
56
|
+
const question = state.questions[index];
|
|
57
|
+
if (!question) { dom.panel.classList.add("hidden"); return; }
|
|
58
|
+
|
|
59
|
+
dom.category.textContent = question.category ?? "TRUE OR FALSE";
|
|
60
|
+
dom.statement.textContent = question.statement ?? "";
|
|
61
|
+
dom.explanation.textContent = question.explanation ?? "";
|
|
62
|
+
dom.choices.forEach(el => el.classList.toggle("correct", el.dataset.choice === question.correct));
|
|
63
|
+
dom.panel.classList.remove("revealed", "hidden");
|
|
64
|
+
|
|
65
|
+
state.timers.push(setTimeout(() => {
|
|
66
|
+
dom.panel.classList.add("revealed");
|
|
67
|
+
state.timers.push(setTimeout(() => dom.panel.classList.add("hidden"), seconds(state.timing.answerSeconds, 4) * 1000));
|
|
68
|
+
}, seconds(state.timing.questionSeconds, 8) * 1000));
|
|
69
|
+
}
|
|
70
|
+
window.addEventListener("nebular-overlay:update", ({detail = {}}) => {
|
|
71
|
+
let shouldStart = false;
|
|
72
|
+
if (Array.isArray(detail.questions)) { state.questions = detail.questions; shouldStart = true; }
|
|
73
|
+
if (detail.control?.current != null) { state.current = detail.control.current; shouldStart = true; }
|
|
74
|
+
if (detail.timing?.questionSeconds != null) state.timing.questionSeconds = detail.timing.questionSeconds;
|
|
75
|
+
if (detail.timing?.answerSeconds != null) state.timing.answerSeconds = detail.timing.answerSeconds;
|
|
76
|
+
if (detail.style?.accent != null) dom.body.style.setProperty("--accent", detail.style.accent);
|
|
77
|
+
if (shouldStart) startCurrentQuestion();
|
|
78
|
+
});
|
|
79
|
+
</script>
|
|
80
|
+
</body>
|
|
81
|
+
</html>
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<style>
|
|
4
|
+
body {
|
|
5
|
+
font-family: sans-serif;
|
|
6
|
+
margin: 1.5rem;
|
|
7
|
+
color: white;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
h1 {
|
|
11
|
+
font-weight: bold;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#logo {
|
|
15
|
+
position: absolute;
|
|
16
|
+
right: 1rem;
|
|
17
|
+
top: 1rem;
|
|
18
|
+
display: none;
|
|
19
|
+
width: 10rem;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#logo.on {
|
|
23
|
+
display: block;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
</style>
|
|
27
|
+
<script>
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
window.addEventListener("nebular-overlay:update", ({detail}) => {
|
|
31
|
+
|
|
32
|
+
const
|
|
33
|
+
programElement = document.getElementById("program"),
|
|
34
|
+
logoElement = document.getElementById("logo");
|
|
35
|
+
|
|
36
|
+
if (typeof detail.program === "string") programElement.textContent = detail.program;
|
|
37
|
+
if (detail.logo) logoElement.classList.toggle("on", detail.logo === "on");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
</script>
|
|
41
|
+
</head>
|
|
42
|
+
<body>
|
|
43
|
+
<h1 id="program"></h1>
|
|
44
|
+
<img id="logo" src="nebular.medallion.transparent.png">
|
|
45
|
+
</body>
|
|
46
|
+
</html>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"instructions": "Empty overlay with the program name",
|
|
3
|
+
"meta": {
|
|
4
|
+
"program": {"type": "string", "default": "", "hint": "Type the program name", "label": "Program Name"},
|
|
5
|
+
"logo": {"type": "choose", "default": "off", "hint": "Logo ON/OFF", "label": "Logo Visibility", "choose": ["on", "off"]}
|
|
6
|
+
}
|
|
7
|
+
}
|