(host)
| 217 | ]; |
| 218 | |
| 219 | function initLoop(host) { |
| 220 | if (!host) return; |
| 221 | host.innerHTML = ""; |
| 222 | host.classList.add("lc-loop"); |
| 223 | |
| 224 | const grid = htmlEl("div", { class: "lc-loop__grid" }); |
| 225 | const cards = LOOP_VERBS.map((v, i) => { |
| 226 | const card = htmlEl("div", { class: "lc-loop__card", "aria-label": v.label }); |
| 227 | card.appendChild(htmlEl("div", { class: "lc-loop__step" }, "step " + (i + 1))); |
| 228 | card.appendChild(htmlEl("div", { class: "lc-loop__verb" }, [ |
| 229 | htmlEl("span", { class: "lc-loop__prompt" }, "$ "), |
| 230 | v.label, |
| 231 | ])); |
| 232 | card.appendChild(htmlEl("div", { class: "lc-loop__desc" }, v.desc)); |
| 233 | grid.appendChild(card); |
| 234 | return card; |
| 235 | }); |
| 236 | host.appendChild(grid); |
| 237 | |
| 238 | host.appendChild(htmlEl("div", { class: "lc-loop__foot" }, [ |
| 239 | htmlEl("span", { class: "lc-loop__arrow" }, "↻"), |
| 240 | htmlEl("span", { class: "lc-muted" }, "scaffold → eval → deploy → observe → repeat"), |
| 241 | ])); |
| 242 | |
| 243 | // Cycle the active class through cards. |
| 244 | let active = 0; |
| 245 | function setActive(i) { |
| 246 | cards.forEach((c, idx) => c.classList.toggle("lc-loop__card--active", idx === i)); |
| 247 | } |
| 248 | setActive(0); |
| 249 | |
| 250 | let timer = null; |
| 251 | function tick() { |
| 252 | active = (active + 1) % cards.length; |
| 253 | setActive(active); |
| 254 | } |
| 255 | function start() { |
| 256 | if (timer) return; |
| 257 | timer = setInterval(tick, 1600); |
| 258 | } |
| 259 | function restart() { |
| 260 | if (timer) clearInterval(timer); |
| 261 | timer = setInterval(tick, 1600); |
| 262 | } |
| 263 | |
| 264 | // Click any card to jump to it (and restart the rotation from there). |
| 265 | cards.forEach((card, idx) => { |
| 266 | card.style.cursor = "pointer"; |
| 267 | card.setAttribute("role", "button"); |
| 268 | card.setAttribute("tabindex", "0"); |
| 269 | card.addEventListener("click", () => { |
| 270 | active = idx; |
| 271 | setActive(active); |
| 272 | restart(); |
| 273 | }); |
| 274 | card.addEventListener("keydown", (e) => { |
| 275 | if (e.key === "Enter" || e.key === " ") { |
| 276 | e.preventDefault(); |
no test coverage detected