PageSourceSearch

https://littlebirdelectronics.com.au/assets/controllers/courses/lesson_controller-0964d738.js

js littlebirdelectronics.com.au collected 2026-09-25 14:09:13 UTC 2,537 bytes, 68 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3// Lesson reader page. Owns:
4//   - the A−/A+ font-scale buttons in the topbar (mutates --book-fs)
5//   - the ☀/☾ light/dark theme toggle
6//   - timing the page (so the "Mark complete" POST can include time spent)
7// All of the actual content rendering happens server-side via
8// Courses::LessonBodyRenderer.
9export default class extends Controller {
10  static values = {
11    courseSlug: String,
12    lessonSlug: String
13  }
14
15  connect() {
16    this.startedAt = performance.now()
17    const saved = parseFloat(localStorage.getItem("courses.fontScale") || "1")
18    if (Number.isFinite(saved) && saved !== 1) {
19      this.element.style.setProperty("--book-fs", String(saved))
20    }
21    try {
22      const savedTheme = localStorage.getItem("courses.theme")
23      if (savedTheme === "dark" || savedTheme === "light") {
24        document.body.dataset.theme = savedTheme
25      }
26    } catch (_) {}
27  }
28
29  stampTimeSpent(event) {
30    const form = event.currentTarget.closest("form")
31    if (!form) return
32    const seconds = Math.round((performance.now() - this.startedAt) / 1000)
33    const input = document.createElement("input")
34    input.type = "hidden"
35    input.name = "time_spent_seconds"
36    input.value = String(seconds)
37    form.appendChild(input)
38  }
39
40  increaseFont() { this.bumpFont(+0.05) }
41  decreaseFont() { this.bumpFont(-0.05) }
42
43  bumpFont(delta) {
44    const cur = parseFloat(getComputedStyle(this.element).getPropertyValue("--book-fs")) || 1
45    const next = Math.min(1.25, Math.max(0.85, +(cur + delta).toFixed(2)))
46    this.element.style.setProperty("--book-fs", String(next))
47    try { localStorage.setItem("courses.fontScale", String(next)) } catch (_) {}
48  }
49
50  toggleTheme(event) {
51    // Flip `data-theme` on `<body>` so every rule reading `--book-*` rebinds
52    // in one go — see `.lb-courses-body[data-theme="dark"]` in
53    // `app/views/layouts/courses/_styles.html.erb`.
54    const next = document.body.dataset.theme === "dark" ? "light" : "dark"
55    document.body.dataset.theme = next
56    const btn = event?.currentTarget
57    if (btn) btn.textContent = next === "dark" ? "☾" : "☀"
58    try { localStorage.setItem("courses.theme", next) } catch (_) {}
59  }
60
61  toggleTutor(event) {
62    // Tutor drawer is a TODO from book.jsx — for now flip `data-on` on the
63    // button so the active styling lights up (see `.lb-iconbtn-tutor`).
64    const btn = event?.currentTarget
65    if (!btn) return
66    btn.dataset.on = btn.dataset.on === "true" ? "false" : "true"
67  }
68}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.