diff --git a/book.toml b/book.toml
index c5a99ea3..32e85843 100644
--- a/book.toml
+++ b/book.toml
@@ -13,8 +13,8 @@ class = "bob"
[output.html]
curly-quotes = true
-additional-js = ["ga4.js"]
-additional-css = ["svgbob.css"]
+additional-js = ["ga4.js", "speaker-notes.js"]
+additional-css = ["svgbob.css", "speaker-notes.css"]
git-repository-url = "https://github.com/google/comprehensive-rust"
edit-url-template = "https://github.com/google/comprehensive-rust/edit/main/{path}"
diff --git a/speaker-notes.css b/speaker-notes.css
new file mode 100644
index 00000000..43d8f3e9
--- /dev/null
+++ b/speaker-notes.css
@@ -0,0 +1,24 @@
+.content details {
+ background: var(--sidebar-bg);
+ color: var(--sidebar-fg) !important;
+ border-radius: 0.25em;
+ padding: 0.25em;
+}
+
+.content details summary h4 {
+ display: inline-block;
+ list-style: none;
+ font-weight: normal;
+ font-style: italic;
+ margin: 0.5em 0.25em;
+ cursor: pointer;
+}
+
+.content details summary h4:target::before {
+ margin-left: -40px;
+ width: 40px;
+}
+
+.content details summary a {
+ margin-left: 0.5em;
+}
diff --git a/speaker-notes.js b/speaker-notes.js
new file mode 100644
index 00000000..ff92ba57
--- /dev/null
+++ b/speaker-notes.js
@@ -0,0 +1,226 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function() {
+ let notes = document.querySelector("details");
+ // Create an unattached DOM node for the code below.
+ if (!notes) {
+ notes = document.createElement("details");
+ }
+ let popIn = document.createElement("button");
+
+ // Mark the speaker note window defunct. This means that it will no longer
+ // show the notes.
+ function markDefunct() {
+ const main = document.querySelector("main");
+ const h4 = document.createElement("h4");
+ h4.append("(You can close this window now.)");
+ main.replaceChildren(h4);
+ window.location.hash = "#speaker-notes-defunct";
+ }
+
+ // Update the window. This shows/hides controls as necessary for regular and
+ // speaker note pages.
+ function applyState() {
+ if (window.location.hash == "#speaker-notes-open") {
+ if (getState() != "popup") {
+ markDefunct();
+ }
+ return;
+ }
+
+ switch (getState()) {
+ case "popup":
+ popIn.classList.remove("hidden");
+ notes.classList.add("hidden");
+ break;
+ case "inline-open":
+ popIn.classList.add("hidden");
+ notes.open = true;
+ notes.classList.remove("hidden");
+ break;
+ case "inline-closed":
+ popIn.classList.add("hidden");
+ notes.open = false;
+ notes.classList.remove("hidden");
+ break;
+ }
+ }
+
+ // Get the state of the speaker note window: "inline-open", "inline-closed",
+ // or "popup".
+ function getState() {
+ return window.localStorage["speakerNotes"] || "inline-open";
+ }
+
+ // Set the state of the speaker note window. Call applyState as needed
+ // afterwards.
+ function setState(state) {
+ window.localStorage["speakerNotes"] = state;
+ }
+
+ // Create controls for a regular page.
+ function setupRegularPage() {
+ // Create pop-in button.
+ popIn.setAttribute("id", "speaker-notes-toggle");
+ popIn.setAttribute("type", "button");
+ popIn.setAttribute("title", "Close speaker notes");
+ popIn.setAttribute("aria-label", "Close speaker notes");
+ popIn.classList.add("icon-button");
+ let i = document.createElement("i");
+ i.classList.add("fa", "fa-window-close-o");
+ popIn.append(i);
+ popIn.addEventListener("click", (event) => {
+ setState("inline-open");
+ applyState();
+ });
+ document.querySelector(".left-buttons").append(popIn);
+
+ // Create speaker notes.
+ notes.addEventListener("toggle", (event) => {
+ setState(notes.open ? "inline-open" : "inline-closed");
+ });
+
+ let summary = document.createElement("summary");
+ notes.insertBefore(summary, notes.firstChild);
+
+ let h4 = document.createElement("h4");
+ h4.setAttribute("id", "speaker-notes");
+ h4.append("Speaker Notes");
+ h4.addEventListener("click", (event) => {
+ // Update fragment as if we had clicked a link. A regular a element would
+ // result in double-fire of the event.
+ window.location.hash = "#speaker-notes";
+ });
+ summary.append(h4);
+
+ // Create pop-out button.
+ let popOutLocation = new URL(window.location.href);
+ popOutLocation.hash = "#speaker-notes-open";
+ let popOut = document.createElement("a");
+ popOut.setAttribute("href", popOutLocation.href);
+ popOut.setAttribute("target", "speakerNotes");
+ popOut.classList.add("fa", "fa-external-link");
+ summary.append(popOut);
+ }
+
+ // Create controls for a speaker note window.
+ function setupSpeakerNotes() {
+ // Show the notes inline again when the window is closed.
+ window.addEventListener("pagehide", (event) => {
+ setState("inline-open");
+ });
+
+ // Hide sidebar and buttons.
+ document.querySelector("html").classList.remove("sidebar-visible");
+ document.querySelector("html").classList.add("sidebar-hidden");
+ document.querySelector(".left-buttons").classList.add("hidden");
+ document.querySelector(".right-buttons").classList.add("hidden");
+
+ // Hide content except for the speaker notes and h1 elements.
+ const main = document.querySelector("main");
+ let children = main.childNodes;
+ let i = 0;
+ while (i < children.length) {
+ const node = children[i];
+ switch (node.tagName) {
+ case "DETAILS":
+ // We found the speaker notes: extract their content.
+ let div = document.createElement("div");
+ div.replaceChildren(...node.childNodes);
+ node.replaceWith(div);
+ i += 1;
+ break;
+ case "H1":
+ // We found a header: turn it into a smaller header for the speaker
+ // note window.
+ let h4 = document.createElement("h4");
+ let pageLocation = new URL(window.location.href);
+ pageLocation.hash = "";
+ let a = document.createElement("a");
+ a.setAttribute("href", pageLocation.href);
+ a.append(node.innerText);
+ h4.append("Speaker Notes for ", a);
+ node.replaceWith(h4);
+ i += 1;
+ break;
+ default:
+ // We found something else: remove it.
+ main.removeChild(node);
+ }
+ }
+
+ // Update prev/next buttons to keep speaker note state.
+ document.querySelectorAll('a[rel="prev"], a[rel="next"]').forEach((elem) => {
+ elem.href += "#speaker-notes-open";
+ });
+ }
+
+ let timeout = null;
+ // This will fire on _other_ open windows when we change window.localStorage.
+ window.addEventListener("storage", (event) => {
+ switch (event.key) {
+ case "currentPage":
+ if (getState() == "popup") {
+ // We link all windows when we are showing speaker notes.
+ window.location.pathname = event.newValue;
+ }
+ break;
+ case "speakerNotes":
+ // When nagigating to another page, we see two state changes in rapid
+ // succession:
+ //
+ // - "popup" -> "inline-open"
+ // - "inline-open" -> "popup"
+ //
+ // When the page is closed, we only see:
+ //
+ // - "popup" -> "inline-open"
+ //
+ // We can use a timeout to detect the difference. The effect is that
+ // showing the speaker notes is delayed by 500 ms when closing the
+ // speaker notes window.
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+ timeout = setTimeout(applyState, 500);
+ break;
+ }
+ });
+ window.localStorage["currentPage"] = window.location.pathname;
+
+ // We encode the kind of page in the location hash:
+ switch (window.location.hash) {
+ case "#speaker-notes-open":
+ // We are on a page in the speaker notes. We need to re-establish the
+ // popup state so that the main window will hide the notes.
+ setState("popup");
+ setupSpeakerNotes();
+ break;
+ case "#speaker-notes-defunct":
+ // We are on a page in a defunct speaker note window. We keep the state
+ // unchanged and mark the window defunct.
+ setupSpeakerNotes();
+ markDefunct();
+ break;
+ default:
+ // We are on a regular page. We force the state to "inline-open" if this
+ // looks like a direct link to the speaker notes.
+ if (window.location.hash == "#speaker-notes") {
+ setState("inline-open");
+ }
+ applyState();
+ setupRegularPage();
+ }
+})();
diff --git a/src/hello-world.md b/src/hello-world.md
index 90988d46..f030d57a 100644
--- a/src/hello-world.md
+++ b/src/hello-world.md
@@ -16,3 +16,21 @@ What you see:
* The `main` function is the entry point of the program.
* Rust has hygienic macros, `println!` is an example of this.
* Rust strings are UTF-8 encoded and can contain any Unicode character.
+
+
+
+This slide tries to make the students comfortable with Rust code. They will see
+a ton of it over the next four days so we start small with something familiar.
+
+Key points:
+
+* Rust is very much like other languages in the C/C++/Java tradition. It is
+ imperative (not functional) and it doesn't try to reinvent things unless
+ absolutely necessary.
+
+* Rust is modern with full support for things like Unicode.
+
+* Rust uses macros for situations where you want to have a variable number of
+ arguments (no function [overloading](basic-syntax/functions-interlude.md)).
+
+
diff --git a/src/hello-world/small-example.md b/src/hello-world/small-example.md
index b1df4b1d..3a0ba8d3 100644
--- a/src/hello-world/small-example.md
+++ b/src/hello-world/small-example.md
@@ -18,3 +18,27 @@ fn main() { // Program entry point
}
```
+
+
+The code implements the Collatz conjecture: it is believed that the loop will
+always end, but this is not yet proved. Edit the code and play with different
+inputs.
+
+Key points:
+
+* Explain that all variables are statically typed. Try removing `i32` to trigger
+ type inference. Try with `i8` instead and trigger a runtime integer overflow.
+
+* Change `let mut x` to `let x`, discuss the compiler error.
+
+* Show how `print!` gives a compilation error if the arguments don't match the
+ format string.
+
+* Show how you need to use `{}` as a placeholder if you want to print an
+ expression which is more complex than just a single variable.
+
+* Show the students the standard library, show them how to search for `std::fmt`
+ which has the rules of the formatting mini-language. It's important that the
+ students become familiar with searching in the standard library.
+
+
diff --git a/src/welcome-day-1.md b/src/welcome-day-1.md
index a6f4fb0c..4817d6d3 100644
--- a/src/welcome-day-1.md
+++ b/src/welcome-day-1.md
@@ -10,3 +10,20 @@ today:
management, and garbage collection.
* Ownership: move semantics, copying and cloning, borrowing, and lifetimes.
+
+
+
+The idea for the first day is to show _just enough_ of Rust to be able to speak
+about the famous borrow checker. The way Rust handles memory is a major feature
+and we should show students this right away.
+
+If you're teaching this in a classroom, this is a good place to go over the
+schedule. We suggest splitting the day into two parts (following the slides):
+
+* Morning: 9:00 to 12:00,
+* Afternoon: 13:00 to 16:00.
+
+You can of course adjust this as necessary. Please make sure to include breaks,
+we recommend a break every hour!
+
+
diff --git a/src/welcome-day-1/what-is-rust.md b/src/welcome-day-1/what-is-rust.md
index e81389a1..ed226ad2 100644
--- a/src/welcome-day-1/what-is-rust.md
+++ b/src/welcome-day-1/what-is-rust.md
@@ -14,3 +14,14 @@ Rust is a new programming language which had its 1.0 release in 2015:
* mobile phones,
* desktops,
* servers.
+
+
+
+
+Rust fits in the same area as C++:
+
+* High flexibility.
+* High level of control.
+* Can be scaled down to very constrained devices like mobile phones.
+
+