Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Classroom mode

Classroom mode layers a teaching workflow on the collaboration machinery: an instructor’s roster shows who else is in the session, a student can follow the instructor’s viewport live, and the instructor can broadcast their current view or release (“unlock”) a student to work independently again. It adds no new wire message and no reticle-sync field (ADR 0111): the roster is built from the existing Awareness presence map, and following rides the same live-published Presence.viewport the read-only viewer already uses (ADR 0038).

Roles and the roster

crate::classroom::ClassroomState (crates/reticle-app/src/classroom.rs) is egui-free: a roster of every other known peer, classified instructor or student, each with a locally-tracked follow flag, plus the instructor’s last broadcast viewport. sync_roster rebuilds the roster from Awareness every frame, reusing the same identity/color/name resolution (crate::viewer::participants) the session chip’s avatar row already uses, so a classroom peer reads exactly like any other collaborator’s presence, with a role label layered on top.

Following the instructor

A student’s “Follow instructor” toggle does not add a second camera-follow path: it flips the same ViewerSession follow flag the collaboration chapter’s session chip already drives, so the existing per-frame sync_camera snaps the student’s camera to the instructor’s live viewport (ADR 0038). Turning follow off leaves the student’s camera where it is, free to pan and zoom independently until they follow again.

Bring everyone, and unlock

The instructor’s Bring everyone here records their current camera viewport as the broadcast target and marks every known student as following, so a following student’s next camera sync lands exactly there. Unlock (per student row, or the palette’s classroom.unlock_student, which targets the first currently-following student in roster order) clears one student’s follow flag without touching anyone else’s. Both are ordinary, pure state transitions on ClassroomState, unit-tested against Awareness values built directly in the test (there is no byte-shape contract fixture here, unlike the F1-F6 producer/consumer pairs: nothing downstream depends on a frozen wire record).

What this depends on, honestly

Today the app publishes presence from exactly one identity (crate::livesync::SHARER_ACTOR), and a read-only viewer never publishes at all, by design (ADR 0038). That means an instructor’s live roster is genuinely empty until a future lane wires a write-capable “join and publish my own presence” path; the classroom panel renders an honest empty state naming this rather than a fabricated row. A student’s half already works end to end over whatever relay is configured, because it only depends on the instructor’s already-flowing viewport. Either way, a classroom that spans more than one machine still needs a reachable relay: the share server default stays 127.0.0.1:3030 (crate::share::DEFAULT_SERVER), and a deployed public relay is operator-owned, tracked as backlog item H1 (scratch/campaign/v82-backlog.md). This module does not change that default and does not attempt to work around it.