5: Notebooks and Versions - transcript
Jessica: The QUILL Cast, episode five. I'm Jessica. Last episode Liam and I walked the main window: menu bar on top, tabbed editing area in the middle, status bar at the bottom, and that one rule that nothing steals focus. Today we are putting a project container around all those tabs, called a Notebook, and we are going to meet Versions, which are the moments in a project you can return to. If you write more than one document at a time, which is most of us, this is the episode that turns QUILL from a powerful editor into a workshop you can come back to.
Liam: I'm Liam, and the elevator pitch is short. A Notebook is one file, ending in dot quillnotebook, that remembers which documents belong to your project, which of them are open right now, and which tab you were last looking at. It also keeps Versions, which are named snapshots of the moment, plus a daily word-count goal, a project vocabulary list, and saved searches. You can think of it as a project folder with memory, or as a very small database that you can open in a text editor if you ever need to.
Jessica: A short recap of where we are in the series, since this is episode five of fifty-four. Last episode, episode four, we toured the main window properly: the menu bar, the tabbed editing area, the status bar, the Spoken Echo, the dialog contract, and the rule that nothing steals focus. Before that, in episode three, we covered creating and saving a document, and the position memory that puts your caret back where you left it. Today's episode covers Notebooks and Versions, and we will end with a setup for the command palette, which is next.
Liam: The honest bit first. The brief for this episode asked us to verify claims against the code, and we did. We found something worth calling out up front. Older QUILL documentation, and the original Milestone Two design doc, calls these "Workspace Snapshots." The current code, the file you would actually read today, calls them Versions. The data class used to be called Workspace Snapshot; it has been renamed to Notebook Snapshot, and the menu items now say Save Version, Restore Version, and Manage Versions. We will use the new naming throughout this episode, and we will flag the rename where it matters so anyone reading old notes or watching older videos does not get confused when the labels do not line up.
Jessica: Let's start with what a Notebook is, in the file system sense. Open the File menu, find the Notebook submenu, choose New Notebook. The first dialog asks for a Notebook name. The default value is "My Notebook"; type whatever you like and press enter. The next dialog is a Save As, with the file filter set to QUILL Notebooks, asterisk dot quillnotebook. Pick a folder, give it a name, save. You now have a single file on disk, and that file is the entire project. There is no companion folder to manage, no index file, no lock file, just one JSON document with a recognisable extension.
Liam: Inside that file is a small JSON object. We read the code, the schema is in quill slash core slash schemas slash notebook dot json, and the top-level fields are name, root directory, creation timestamp, last opened timestamp, entries, snapshots, goal, vocabulary, and saved searches. Entries are the documents that belong to the project; each one carries an id, a path, an optional title, a list of tags, the last caret position, the last opened timestamp, and a word count. Snapshots are the Versions, with an id, a name, a creation timestamp, a list of open entry ids, and an optional active entry id. The other fields are conveniences we will meet later in the series. Everything is in one file, and saving is atomic, so a crash mid-write cannot corrupt your project.
Jessica: A second path, for when you already have a folder of Markdown or text files, is New from Folder, also in the File menu's Notebook submenu. The flow is three dialogs in a row. First you pick the folder, then you confirm the Notebook name, which defaults to the folder name, then you pick where to save the dot quillnotebook file. QUILL walks the folder, finds files with the extensions dot md, dot txt, or dot rst, and adds each one as an entry, storing the path relative to the folder so the whole project stays relocatable. Move the folder, the Notebook still works, because the entries are not pinned to absolute paths. The default title for each entry is derived from the file name, with hyphens and underscores turned into spaces and the result title-cased, so "chapter-one-draft.md" becomes "Chapter One Draft."
Liam: The third path is Open Notebook, again in the File menu, Notebook submenu. The dialog filters to dot quillnotebook files. Pick one, and the active Notebook is loaded into memory. If the file is unreadable, you get a clear error dialog. If the JSON is malformed, you get a Notebook format error message, also clear. Once it is open, the status bar reads, in our case, Notebook named whatever opened, with however many entries. That is the moment the project is live. The active notebook is held as a single attribute on the main frame, _active_notebook, and the entries panel re-loads from it whenever the underlying object changes.
Jessica: There is one thing to know about how open behaves. When you open a notebook, QUILL does not automatically open all of its entries as tabs. The notebook is a list, not a workspace, until you ask it to become a workspace. To get back the state you were last in, you choose Restore Version through Manage Versions, or you use the Navigate menu's Go to Entry in Notebook command to open specific entries. That is by design; opening a notebook should feel fast and quiet, and pulling in dozens of tabs at once would violate the "nothing steals focus" law we talked about last time.
Jessica: Here is the part to do right now. Pause the audio. Open QUILL. Click File, then Notebook, then New Notebook. Name it Practice Notebook. Save it somewhere you can find again, your Documents folder is fine. When the dialog closes, take fifteen seconds to look at the main window. Notice that the editing area is unchanged; the Notebook is invisible until you decide to see it. The status bar at the bottom should read "Notebook Practice Notebook created." When you are ready, press play and come back. We will be right here.
Liam: Welcome back. If the status bar read "Notebook Practice Notebook created," you are in the right place. Nothing else changed visually on purpose, because a Notebook, at this moment, is mostly data, not chrome. The data is now sitting in a single file on your disk, and the in-memory model is loaded into the main frame.
Jessica: To see anything from the Notebook, you need the Entries Panel. Open the View menu, find Show Entries Panel, and choose it. The first time you do this, a left-hand pane slides open, two hundred twenty pixels wide, docked to the main vertical splitter. The panel has a notebook name label at the top, an optional goal line, a filter text field, and a list of entries. The default name in the label is "No notebook open," but you just opened one, so once the panel loads your notebook the label changes to its name. The whole panel is keyboard reachable: tab moves between the filter and the listbox, type into the filter to narrow, arrow up and down inside the list to read each entry, and enter on an entry is the standard "do the default action" key, which the panel does not bind by default; navigation lives in the Navigate menu.
Liam: If you started with an empty Notebook from the first step, the list is empty. The cleanest way to put documents into a Notebook is to open them and let the panel track them, or to use New from Folder if you already have a tree of files. The entries panel is also reachable as a checkbox on the View menu, so Show Entries Panel is a toggle; choose it again and the panel collapses shut. The splitter does not disappear, it just becomes unsplit, and the menu checkmark goes off. The code path is the same: a single method, toggle entries panel, queries the splitter, splits or unsplits it, and then updates the menu check state so what you see in the panel matches what the menu claims.
Jessica: Now the feature that makes Notebooks feel like a project instead of a list, Versions. A Version is a named snapshot of the moment. The Notebook remembers which entries are open, and which one is the active tab. The data model holds an open entry ids list and an optional active entry id, plus a name you chose and a creation timestamp. Save a Version, your work is checkpointed. Open a Version later, and the Notebook tries to reopen the same set of entries with the same active tab.
Liam: To save one, open the File menu, Notebook submenu, Save Version. The dialog asks for a Version name, defaulting to "Version N," where N is one more than however many you have already saved. Type a meaningful name; "After first draft," "Before editor pass," "Pre-publication," anything that helps future-you remember the moment. Press enter. The status bar reads Version "your name" saved, and a new entry appears at the bottom of the snapshots list in the notebook's data.
Jessica: Behind the dialog, the code does three things. First, it inspects the current document tab list, and for each entry whose file path is inside the notebook's root directory, it finds the matching entry by relative path. The one that matches the active tab becomes the active id. Second, it collects the ids of all the entries that are currently open in tabs. Third, it calls notebook dot save snapshot with the name, the list, and the active id, which appends a new Notebook Snapshot to the notebook's snapshots list, and then it persists the notebook back to its file path with the atomic write helper. If the file write fails, you get a clear warning that the snapshot is in memory but could not be written to disk.
Liam: One thing to be honest about. Right now, saving a Version captures the open tabs and the active tab, not the on-disk contents of the files. If you want the files themselves preserved at that point, use Save All on the File menu first, then save the Version. Versions are about the project shape, the set of tabs you had open, not a backup of every byte you ever typed. We will cover backup and history properly in a later episode.
Jessica: The companion action is Manage Versions, also on File, Notebook submenu. The dialog is plain, title Manage Versions, size five hundred twenty by four hundred twenty, with a list of Versions at the top and three buttons, Rename, Delete, and Close, underneath. Each row in the list shows the version name and a ten-character date prefix from the created timestamp, like "2026-07-09." Pick a version, press Rename, and a small text entry dialog appears with the current name, edit and enter to commit. Pick a version, press Delete, and it is removed from the list and from the in-memory notebook; the dialog then resaves the file. Close dismisses the dialog. Escape also dismisses it, because the dialog contract requires it. Behind the scenes, every Rename and Delete call goes through a small private helper that re-runs save notebook on the same path, so the on-disk file always matches the in-memory state when you close the dialog.
Liam: There is also a Restore Version item on the submenu, sitting between Save Version and Manage Versions. In the current build, Restore Version opens the same Manage Versions dialog; the code wires both menu items to the same handler. So if you are looking for a one-click "put me back in that state" button, you will find it through that dialog. Pick the version, then take the action you want: rename, delete, or close and reopen the documents manually. The reason for the single dialog is that the underlying restore logic is gated on the version's entry ids resolving to existing files, and the dialog makes that visible to you before you commit.
Jessica: The Navigate menu has a small set of "Go to in Notebook" commands, all of them scoped to whichever notebook is currently active. They sit below the regular bookmark commands, separated by a menu separator. There are four. Go to Entry in Notebook opens a tree navigator listing every entry, grouped however your notebook organizes them, and opens the one you pick. Go to Heading in Notebook does the same but inside each entry, surfacing every Markdown heading it finds, with a Jump to Heading action that opens the file and positions the caret. Go to Bookmark in Notebook shortcuts to the standard bookmark jump, scoped to the current document. Go to Sticky Note in Notebook opens the manage sticky notes dialog. All four say "No notebook is open" if you call them without one.
Liam: Each of those commands uses a tree navigator dialog that takes a title, a root label that is the notebook's name, and a list of nodes. Nodes can carry an action label like "Open Entry" or "Jump to Heading" and a payload that is either an entry or a file path plus a character position. Pressing enter on a node runs the action, escape closes the dialog. It is the same navigator used by the Go to Bookmark and Go to Heading commands in the regular Navigate menu, so once you have learned it once, you have learned it for the rest of the app. The Go to Heading in Notebook command is especially worth knowing: it walks every entry, reads the file as text, looks for lines that start with one or more hash characters, and builds a node for each one. Files that cannot be read, or have no headings, are silently skipped, so a partially-imported project still gives you a clean tree.
Jessica: The entries panel itself has its own filtering. Type into the filter field at the top of the panel and the listbox narrows as you type, case-insensitive, matching the title of each entry. The matching is "contains," not "starts with," so partial words work. Press enter inside the filter does not move focus, it just confirms the filter, so you can keep typing. The listbox is a single-select widget; one entry is highlighted at a time. Pick an entry from the list and, in the current build, the panel shows the selection; opening the entry is the action of the Go to Entry in Notebook command on the Navigate menu, not the panel itself. That split is on purpose. The panel is a list; the navigation commands are actions. Keeping the panel as a list means it can hold dozens or hundreds of entries without slowing down, and the action of "open this one" is a deliberate user choice.
Liam: A note on accessibility, because it is the whole point of the series. The entries panel exposes a name on each control, "Notebook name," "Notebook goal," "Filter notebook entries," and "Notebook entries list," so a screen reader announces each region as you tab into it. The listbox is a standard wx ListBox, which is read row by row with arrow keys. The filter field is a wx TextCtrl with TE process enter style, so you can edit it without surprise behavior on enter. The Manage Versions dialog follows the dialog contract: focus is placed on the listbox when it opens, escape closes it without side effects, and the affirmative button is wired to close. The rename dialog, which is a wx TextEntryDialog, is keyboard-reachable and announces its title and current value. None of this is special-cased for a single user; it is the product's standing rule, and the dialog inventory gate that lives in the build process makes sure no future dialog forgets the rule.
Jessica: A few finer points worth knowing. The Notebook's root directory, when present, lets the file paths in entries stay relative. That is why New from Folder works even if you move the whole project to a new drive later. The disk write goes through quill slash core slash storage dot py, specifically the atomic write helper, which writes to a temp file and uses os replace, so a power loss between save and fsync cannot leave you with a half-written notebook. The temp file is in the same directory as the target, on the same file system, so the rename is atomic and the original is replaced only when the new content is fully on disk.
Liam: The schema validator lives at quill slash core slash schemas slash notebook dot json, and every load runs the data through it via the from dict class method. If the schema discriminator does not match, "quill dot notebook slash one," the load raises Notebook format error. That is why opening a notebook file that was hand-edited or saved by a different tool gives you a clean error message instead of silently corrupting the project. The to dict method is the inverse: it serialises the in-memory notebook back to JSON, including all sub-objects, and the discriminator is always written first so the next load can verify the file is for this product.
Jessica: The data class is small and worth knowing. A Notebook has a name, an entries list, a snapshots list, a goal object, a vocabulary list, a saved searches list, an optional root directory, a created timestamp, and a last opened timestamp. Each entry has an id, a path, an optional title, a list of tags, a last caret position, a last opened timestamp, and an optional word count. The id is a uuid generated when the entry is added. The path is a string, usually relative to the notebook's root. The title is optional, and the panel falls back to the path if no title is set. The last caret position and the word count are populated by the editor as you work, so a Notebook that has been used for a while carries a quiet history of where you were in each file.
Liam: Each snapshot is similarly small. It has an id, a name, a created timestamp, an open entry ids list, and an optional active entry id. The created timestamp is an ISO-formatted UTC string, generated when the snapshot is created. The active entry id, when present, names the entry that was the active tab at the time of the snapshot. Because the snapshot's open entry ids are ids, not paths, the snapshot stays correct even if you move files around, as long as the entries themselves are still in the notebook. If an entry has been removed from the notebook, the snapshot keeps its id, but the restore step will quietly skip the missing one, which is the right behavior.
Jessica: One more thing we want to be straight about. The Notebook has fields for goal, vocabulary, and saved searches. The data model supports them. The menu and dialog surface for them is partial in the current build. You will find goal exposed in the entries panel as a label, formatted as "Goal: count slash target unit today," and the panel reads it from the active notebook, so if you set a goal in the data file by hand, or via a future UI surface, it shows up. The default goal is five hundred words, disabled. Vocabulary and saved searches exist in the file, but the UI for editing them is not yet wired into a menu. We are not going to invent features that are not there. They are on the list for a future milestone, and the data layer is ready for them when the UI arrives.
Liam: Let's put it all together with a short story. You are starting a new project, a long-form essay, with a few research notes, a draft, and a reading list. You create a folder called "essay-project," drop your markdown files in it. In QUILL you choose File, Notebook, New from Folder, point at the folder, accept the name, save the dot quillnotebook file alongside it. The entries panel shows every file. You open the draft and two research notes. You choose File, Notebook, Save Version, name it "First pass complete." You keep editing.
Jessica: Two days later, you want to see what the project looked like at first pass complete. You choose File, Notebook, Manage Versions, pick the version in the list, close the dialog, then choose Navigate, Go to Entry in Notebook, and reopen the entries you had open. The list inside Go to Entry is the current list of entries in the notebook, but the version you saved records the ids of the entries that were open at that moment, so you can compare today's open set with the saved one. If the version had a remembered active tab, the data is there; restoring that active state is on the near-term roadmap. For now, the version gives you a faithful list of what was open and when.
Liam: When you are done with the project for the day, you close QUILL. The notebook file on disk is the source of truth. Next time you open QUILL, choose File, Notebook, Open Notebook, pick the file, and the project is back exactly as you left it, minus any unsaved typing in the tabs. Position memory, which we mentioned in episode three, takes care of the caret positions in each entry. The Notebook's last opened timestamp is updated so the file itself knows when you last used it, which is a small but useful breadcrumb.
Jessica: A second story, with a twist. You have been working for an hour, you have ten tabs open, and you want to try a structural change, a new outline, a different voice. Before you start, choose File, Notebook, Save Version, name it "Before restructure." Make the change. If you like it, save another version, "After restructure." If you do not, choose Manage Versions, pick "Before restructure," note the entry ids, close the dialog, close the tabs you opened, reopen the entries from "Before restructure" through Go to Entry, and you are back to where you were. The data is unchanged, the tabs are unchanged, the cursor is in the same place because position memory handled it. The cost of trying something risky is one dialog and one click.
Liam: The same story works for experiments you want to keep. Save a Version before, save a Version after, and you have a permanent record of the moment, with a name you can read, on a date you can verify, in a file you can copy or back up. If you outgrow the version, you can rename it later. If you change your mind, you can delete it. The history of the project is a small list of names at the top of the notebook file, in plain JSON, readable by you, by future-you, and by any tool you trust.
Jessica: Homework. Four steps. One: create a fresh Notebook, name it whatever you like, and save it somewhere you can find. Open it, then open the View menu and toggle Show Entries Panel on and off, just to feel the docked pane and to confirm the menu checkmark matches what the splitter is doing. Two: if you have a folder of markdown notes lying around, use File, Notebook, New from Folder to import it. Pick a folder with at least three files so the entries panel is non-trivial. If you do not have such a folder, create one with three short text files first, then import. Three: open one of the imported files, then choose File, Notebook, Save Version, give it a name you will recognize tomorrow, and confirm the status bar message. Four: choose File, Notebook, Manage Versions, find the version you just saved, and use the Rename button to give it a more meaningful name. Close the dialog with escape or with the Close button.
Liam: And one small warning to keep in your head as you do the homework. Versions are project checkpoints, not file backups. If you delete a file from disk, then open a Version that referenced it, the entry will be missing and the file open will fail with a clean "entry file not found" status message. The notebook itself is fine; the data structure stays consistent. The file system is the only place that can actually lose the words. For real backup, use your usual file backup, sync, or version control on the folder that contains the Notebook and the entries; treat the dot quillnotebook file the way you would treat any other document file.
Jessica: One more piece of advice, because it will save you time later. When you save a Version, give it a name that makes sense in a year. "Version 7" is not a name; it is a placeholder. "Pre-editor-pass-July" is. If you can, include the date and the reason for the snapshot. You will thank yourself the next time you open Manage Versions and try to remember why you saved something three weeks ago.
Liam: A final note on the renaming. The data model keeps the version's id stable across renames, so renaming does not break any reference to the version. If you ever export a Notebook to inspect it by hand, you will see the ids are uuid four strings, and the names are plain text. Rename freely; the system does not care about the old name once the new one is in place. Delete is also final; the snapshot is removed from the in-memory list and the file is rewritten, with no recycle bin. So treat delete as the verb it is, and use rename whenever you are not sure.
Jessica: Next episode is the one that changes how you use everything we have shown you so far. The command palette. One keystroke, control shift P, opens a searchable list of every command in QUILL, including every Notebook and Version command we just walked through. Once you have the palette, you will never need to remember a menu path again.
Liam: I'm Liam.
Jessica: I'm Jessica. Save your Version.
Liam: The QUILL Cast is a fifty-four episode audio course on QUILL. This has been episode five. Thanks for listening.