Tangy is building your next step
Shaping the lesson around what you want to make.
Tangy is building your next step
Shaping the lesson around what you want to make.
Computer Science
Build a small browser editor that records incremental changes in a durable journal, periodically saves full checkpoints, and restores unsaved work after a simulated crash. Along the way, you will make the tradeoff between frequent journal writes and larger checkpoint writes visible in a runnable project.
Follow the path module by module. The layout keeps the lesson count, your progress, and the module description in one scan.
Make the Editor Work Before Persistence
3 lessonsCreate the smallest usable editor and establish the document-change model that later lessons will persist. This module stands alone so the learner can run and demonstrate the project immediately.
Add Journaling and Full Checkpoints
3 lessonsIntroduce the two complementary persistence paths: frequent incremental journal entries and less frequent full checkpoints. The learner will make sequence numbers and recovery metadata observable before simulating failure.
Recover After a Simulated Crash
4 lessonsUse the project’s checkpoint and journal data to reconstruct the latest document state after in-memory state disappears. The finished project will provide a repeatable crash-and-recovery demonstration.
Public lesson
Predict
What will happen?
The editor will be more useful if it reports what is happening as you type. Before changing anything, predict what you expect:
If the document starts with some text and you type three characters, should the status panel update only after a refresh, or immediately?
The browser can observe each edit through the input event. The Node.js server’s job is to deliver the page; after the page loads, the browser handles the interaction.
Tasks
Open the existing server entry file and find the HTML that it sends for the main page. Inside its <body>, make the document and status elements explicit:
<main>
<h1>Browser Editor</h1>
<div
id="document"
contenteditable="true"
role="textbox"
aria-label="Document"
>
Start writing here.
</div>
<p id="status" aria-live="polite">TODO: initial status</p>
</main>
<main>
<h1>Browser Editor</h1>
<div
id="document"
contenteditable="true"
role="textbox"
aria-label="Document"
>
Start writing here.
</div>
<p id="status" aria-live="polite">TODO: initial status</p>
</main>
Adapt the surrounding markup rather than replacing unrelated code. The important relationship is:
#document is where the learner edits.#status is where the page reports the current document state.contenteditable="true" makes an ordinary element editable without adding a framework.Run the project using its documented command and open the local address it prints. Click the document area and type. At this point, the text should be editable, but the status will not react yet. That failed check is useful: it shows that making something editable and observing it are separate jobs.
Tasks
Add a script near the end of the page, after the two elements above:
<script>
const documentArea = document.querySelector("#document");
const status = document.querySelector("#status");
function updateStatus() {
// Choose the text property that should represent the visible document.
// Then replace the placeholder status message.
}
// Register the event that fires when the user changes the document.
// Call updateStatus from the event handler.
updateStatus();
</script>
<script>
const documentArea = document.querySelector("#document");
const status = document.querySelector("#status");
function updateStatus() {
// Choose the text property that should represent the visible document.
// Then replace the placeholder status message.
}
// Register the event that fires when the user changes the document.
// Call updateStatus from the event handler.
updateStatus();
</script>
Complete the two comments yourself. Your status should show the number of characters in the document, ignoring leading and trailing whitespace. One expression that helps is:
documentArea.innerText.trim().length
documentArea.innerText.trim().length
For example, the body of updateStatus could be shaped like this, but adapt the wording to your editor:
const characterCount = /* calculate the trimmed character count */;
status.textContent = /* make a readable status message */;
const characterCount = /* calculate the trimmed character count */;
status.textContent = /* make a readable status message */;
The event listener should listen for "input":
documentArea.addEventListener("input", () => {
// update the status here
});
documentArea.addEventListener("input", () => {
// update the status here
});
The separate updateStatus() call matters because the document already contains initial text when the page loads. Without it, the status would remain wrong until the first keystroke.
Tasks
Reload the page, then check these cases:
Find the bug
Something's wrong — can you spot it?
If the text changes but the status does not, inspect the browser’s developer console. The most useful questions are:
querySelector("#document") find the same element whose id you wrote?id="status"?There are two different moments in this app:
Node.js server
└── sends the HTML page
└── browser loads it
├── user edits #document
└── input event updates #status
Node.js server
└── sends the HTML page
└── browser loads it
├── user edits #document
└── input event updates #status
The server does not need to handle every keystroke for this first editor. It only serves the page. The browser owns the immediate interaction, so the feedback feels instant and the server remains simple.
This distinction will matter if the editor later needs saved documents or multiple users: local editing can stay responsive, while a separate server feature can decide when and how changes are stored or shared. For now, the visible check is deliberately modest: edit text in the browser and watch the status panel prove that the page noticed.
3 modules · 10 lessons
Make the Editor Work Before Persistence
Add Journaling and Full Checkpoints
Recover After a Simulated Crash
Learn by building your own version.
Remix this public project to open the workspace, follow the guided build, and let the AI mentor teach you through the work instead of doing it for you.