Files on disk
The folder layout the app reads and writes, the two studio.json files, how decks are detected, and what is skipped.
Everything Lecture Studio knows is in the library folder as plain files. Any Marp toolchain, any editor and any sync tool can work on the same folder.
Layout#
Lecture Studio/ the library
studio.json presenter profile and hidden folders
TEACHING_STYLE.md optional, a longer teaching style
sources/ shared sources, searched everywhere
cs101-fall26/ a course
studio.json code, title, term, kind, unit prefix, …
syllabus.md
AGENTS.md course context for the assistant
CLAUDE.md contains "@AGENTS.md"
sources/ course sources
week1/
week1-slides.md the deck
week1-instructor-guide.md optional
assets/ images the deck uses
sources/ unit sources
week2/
…
ai-in-science/ a talk
ai-in-science.md the single deck
assets/
sources/
studio.json kind: talk
The two studio.json files#
The root file is the presenter profile: name, email, affiliation, unit, contact, language, style, unitLabel and exclude.
Each course or talk folder has its own with these keys:
| Key | Meaning |
|---|---|
title, code | Shown on the card and in the deck header. |
term | Free text, usually Fall 2026. |
kind | course or talk. A folder without a file counts as a course. |
unitPrefix | The unit word for new units: week, session, module, lecture, day or part. |
language | The language the assistant writes in for this course. |
archived | true moves the course to the Archived group. |
derivedFrom, startDate | Set by New term from this course…; they link terms and date the units. |
Both files are written pretty-printed with sorted keys, and empty fields are dropped. Editing them by hand is fine; the app picks the change up through the folder watcher.
What counts as what#
- Course: a top-level folder whose
studio.jsonsayscourse, or has nostudio.json. Its units are subfolders whose names are a word followed by a number:week3,session-2,day10. The separator is kept consistent with the existing units. - Talk: a top-level folder whose
studio.jsonsaystalk. One deck, no numbered units. - Deck: any
.mdfile whose front matter containsmarp: true. The deck title is thefooter:value when there is one, otherwise the first#heading. - Document: any other Markdown or text file. Its title is the first
#heading. - Unit word:
unitPrefixfrom the course's file, else the most common prefix among the existing folders, elseweek.
Symlinked courses#
A course can live outside the library. Choose… in the New course or Edit course dialog picks any folder; the library then holds a symbolic link under the course's folder name. The app reads and writes through the link.
What the scan skips#
The file list ignores node_modules, assets, sources, sources-private, site-mirror, input_data, venv, .venv, __pycache__, dist, build, solutions-private, any folder starting with a dot, and any top-level folder named in the profile's exclude list. sources/ folders are counted but their contents are shown in the sources dialog, not in the file picker. *.pyc, package-lock.json and studio.json are never listed. The scan goes four levels deep.
Themes and styles#
The app ships two Marp themes, ytu-lecture and ytu-talk, and uses Marp's default theme for a deck that names none. New decks do not depend on a shipped theme: they carry an inline style: block in the front matter with the sizes the scaffolds use, so they render the same in any Marp tool. See Marp decks.
Where the app keeps its own data#
| Data | Location |
|---|---|
| Library path, panel layout, sort order, last place, project id, subject | The app's preferences |
| Oberik project key | The macOS Keychain (a source build uses a file under Application Support) |
| Chat history | ~/Library/Application Support/LectureStudio/chats/<library>-<hash>/ |
Nothing about a library is stored anywhere else; moving the folder and opening it again is enough, except that the chat history is keyed by the folder's path.