Version 3.10.0 · All versions

All pages

These are the docs as released with Solid Memo 3.10.0. Read the latest docs.

Courses

A course teaches a subject in chapters. Each chapter is a sequence of steps, and each step is a short piece of theory checked by one or more multiple-choice questions. A chapter ends with a final review of all its questions. Every question the learner answers becomes a card in their own deck, scheduled like any other (srs.md). At the end of the course the learner has a deck of every card they studied.

A course is not a new kind of thing in the pod. It is a library release with an outline, and the learner's copy is an ordinary deck. A course published anywhere else, in its creator's pod say, is the same: a learner adds it from a link to its release, and plays it as one from the library, its outline read from that address. The pure parts are in domain/course.ts; the use cases are in useCases.ts.

The model

<> a solid-memo:Deck , dcat:Dataset , schema:Course ;
    solid-memo:studyDirection solid-memo:frontToBack ; … .

<#ch-why-solid> a solid-memo:Chapter , schema:Syllabus ;
    solid-memo:formatVersion 1 ;
    schema:isPartOf <> ;
    schema:position 0 ;
    dcterms:title "Why Solid"@en ;
    dcterms:description "Why Solid exists: the data-silo problem, …"@en ;
    solid-memo:reviewQuestion <#q-why-solid-r01> , … .  # asked only in the final review

<#ch-why-solid-2> a solid-memo:Step , schema:LearningResource ;
    solid-memo:formatVersion 1 ;
    schema:isPartOf <#ch-why-solid> ;
    schema:position 1 ;
    solid-memo:textFormat solid-memo:markdown ;
    solid-memo:theory """## Separation creates choice

When one company holds both your data and the app, …"""@en ;
    solid-memo:checkedBy <#q-why-solid-2a> , <#q-why-solid-2b> .

<#q-why-solid-2a> a solid-memo:Card ;
    solid-memo:formatVersion 5 ;
    solid-memo:front "In the argument for Solid, why would separating data storage from apps create more competition?"@en ;
    solid-memo:back "Storage and apps can each be chosen and replaced separately"@en ;
    solid-memo:backNote "Once data does not belong to the app, users can switch apps …"@en ;
    solid-memo:distractor <#q-why-solid-2a-d1> , <#q-why-solid-2a-d2> , <#q-why-solid-2a-d3> .

<#q-why-solid-2a-d1> a solid-memo:Distractor , schema:Answer ;
    solid-memo:formatVersion 1 ;
    solid-memo:distractorText "Users can install more apps on the same device"@en ;
    solid-memo:distractorNote "How many apps fit on a device has nothing to do with it; …"@en .

decks/solid-fundamentals/v1.ttl, the first course, written in Markdown, is the complete example. The terms are in vocab.md, the shapes in shapes.md, and the rules the shapes cannot state in deck-library.md. Those rules are the domain's (courseRules.ts): npm run library:check runs them, and the Studio can too.

Order

The learner's state

The learner's state is kept in documents the pod already has, with no new type-index entries (data-model.md):

WhereWhat
catalog.ttlThe course's deck: prov:wasDerivedFrom <release>, and one sm:completedChapter <release#ch-…> per chapter completed.
decks/<deckId>.ttlThe cards answered so far, each with its sm:Distractor subjects, under the release's fragment ids; each card names them with schema:suggestedAnswer too.
reviews/<deckId>.ttlAn ordinary front→back review state per card answered, naming its card (sm:reviewOf).
history/<YYYY-MM>.ttlAn sm:Answer per graded answer, with sm:answerMode sm:multipleChoice and, when the answer was wrong, sm:chosenDistractor <…#d1>.

The learner's flow

  1. Start. A library deck whose release is a course is marked as one (LibraryDeck.isCourse, from schema:Course in the index). Starting it (startCourse) copies the release's metadata into an empty deck (importDeck with no cards). An instance that already has a copy of any release of the course gets that copy back: a course is started once. A course added from a link is started the same way; its own release says it is a course, so the deck list offers to continue it as it does a library course (useCopies). Until every chapter is completed, the course is offered above the deck list, with its chapters done and a button to go on where the learner left off; a course deck's menu leads to its page too.
  2. A step. A step comes in two phases. First the learner reads the theory, with no question shown. Theory in chunks is read one chunk at a time, starting at the first: "Part 2 of 3" says where the learner is, Continue goes to the next chunk and Back, from the second on, to the one before. Below the last chunk (all the theory, when it is one), a button goes on to the step's question(s); it is described by a hint that the theory is not shown while answering. The step's questions are then asked one at a time, with the theory not rendered at all: the answer checks what was understood, not what can be read off the screen. After each answer the app says whether it was right. A wrong choice shows its distractor's note. The right answer is shown as the card's back, with its sm:backNote. The first answer to a question adds the card to the deck ("Added to your deck"). After the step's last question, Next goes to the next step's theory, or, after the chapter's last step, to the final review. There is no way back to the theory while answering; every step starts on its theory, at its first chunk, the one the chapter resumes at included, so leaving and coming back shows it again. Moving to another chunk puts the focus on "Part 2 of 3", so a screen reader reads the chunk from its start. When the reader's language is switched to one with fewer chunks (which a release checked by the library never has), a chunk past the last shows the last.
  3. The final review. Once every step is done, the chapter's final review comes next, whether the learner gets there with Next after the last step or from the course's page. It opens on a word before it: that every step of the chapter is done, and what the review is. A button starts it, and a link goes back to the course's chapters; the heading takes the focus. The review asks all the chapter's questions, shuffled and again without the theory, each taking the focus as it comes up. A question answered wrongly comes back until it is answered right. Then the chapter is completed (completeChapter), which opens the next; while that is saved, and when it fails, with a way to try again, the review says so. A final review reached some other way (a bookmark, a typed link, Back) for a chapter with steps not done goes to the chapter instead, at the first of them, so the word before the review is never wrong.
  4. Back to the chapters. Once the chapter is completed, and the course read afresh, the learner is taken back to the course's page, to go on or stop there. A learner who has left the review while it was saved stays where they went, and nothing is cheered. The page cheers the chapter just completed: a word that it is done, which takes the focus, so a screen reader reads it and Tab goes on to Continue, now to the next chapter. The chapter's Done badge pops with a burst of sparkles. When that was the course's last chapter left, the word says the course is complete and three confetti shooters along the bottom of the page pop one after the other, their confetti shooting up and fluttering slowly back down, all over in under five seconds. The cheer is kept in memory, not in the URL, and only until the learner goes elsewhere: a reload or a later visit does not show it again. The motion is decoration only, hidden from screen readers, and none of it plays when the reader asks for reduced motion.
  5. Afterward. A finished course says so on its page, with a link back to the decks. The cards are studied like any deck's, by flip-and-grade (srs.md). A chapter can be retaken at any time, as practice: a chapter whose steps are all done opens at its first step, and each step shows its theory again. No single earlier step can be picked to reread its theory: the step a chapter opens at is derived from the progress, not chosen.

How each answer is graded, and when it is graded at all, is in srs.md. The screens and their routes are in routing.md. The chapter player is ChapterPlayerScreen and ChapterPlayerContainer, and the review between a chapter's theory and its questions is ChapterReviewScreen and ChapterReviewContainer, all in packages/ui/src/ui/.

The course for newcomers

The library's index may name one course as the one to offer someone who has no decks yet: sm:newcomerCourse on its catalog, pointing at the course's series (deck-library.md). The app marks that deck LibraryDeck.forNewcomers, but only when its series is in the index and its current release is a course; anything else offers nothing.

Use cases and ports

Use caseDoes
startCourse(instanceUrl, course)The instance's deck of the course: the copy it has (isCopyOf), else a new, empty copy of the current release.
importReleaseFromUrl(instanceUrl, release)A course added from a link is started so too (readReleaseFromLink reads and checks it first, deck-library.md).
getCourse(deck)The course as the learner has it: the deck as its entry is now, the release, its outline, its cards by id, the cards answered and the progress. Writes nothing. A deck that is gone throws deckGone.
answerCourseQuestion(instanceUrl, deck, card, choice, now, times?)Grades one answer as courseAnswerEffect says, logging how long choosing took (times.recallMillis). A card introduced is written first (applyCardChanges), then graded, so a review state never exists without its card. Returns the effect and the card's state.
completeChapter(deck, chapterUrl)Adds sm:completedChapter to the deck's entry.
setCompletedChapters(deck, edit)The Studio's change of the chapters completed: one marked not done (in any release, by its fragment id), or the course restarted, none completed. Answers and review states stay.

Two ports gained methods (ports.ts):

The deck's distractors are written and removed with its cards (withDistractors and distractorsOf in deckMapper.ts). An edit that states no distractors, as the card editor's does while its wrong options are left as they were, keeps the card's. Removing a card removes the distractors it names. The card editor and the Studio edit them (studio.md).

The tests that hold this against a real server are in courses.integration.test.ts.

Writing a course

A course is written as any library deck is (deck-library.md), with these additions. The Studio writes a course in a draft in the pod the same way, and suggests its ids so (studio.md).

  1. Type the release schema:Course too, and set sm:studyDirection sm:frontToBack.
  2. Add the chapters and steps, each with sm:formatVersion 1, schema:isPartOf and a schema:position of its own among its siblings. A chapter's title and a step's theory are language-tagged text, one per language, one of them English.
  3. Write every question as a card (format 5, or 6 for one with a front label) with text on its back. Ask it from one place only: one step's sm:checkedBy, or one chapter's sm:reviewQuestion.
  4. Give each card at least two distractors (the first course gives three), each with sm:formatVersion 1, text in every language its back has and a note on why it is wrong. A back in no language (code, a number) is untagged or zxx, and so are its distractors' texts. Write wrong options that are plausible and of the same form as the right one, never "all of the above".
  5. Name the subjects so their order reads from the ids: chapters ch-<topic>, steps ch-<topic>-<n>, a step's questions q-<topic>-<n><letter> (q-why-solid-2a, q-why-solid-2b), review questions q-<topic>-r<nn> (q-why-solid-r01) and distractors <question>-d<n> (q-why-solid-2a-d1).
  6. To write theory, a chapter's description or a question in Markdown (code, tables, lists), state sm:textFormat sm:markdown on that step, chapter or card (deck-library.md). A question's options are then one paragraph each, its back included, and hold no links.
  7. To have the learner read a step's theory in chunks, write it in Markdown and put a thematic break, --- with a blank line before and after it, between each two chunks (without the blank line before it, --- underlines the line above as a heading, which the check refuses). Every language of the theory has as many chunks, none of them empty: no break first, last, or right after another (deck-library.md). Break at a change of topic, where a learner could pause, and keep a chunk to about a screenful; a short theory needs no breaks.
  8. Keep the attribution and the review rounds as every authored deck does (deck-library.md).
  9. npm run format:turtle, then npm run library, which runs the course checks (deck-library.md) and the Markdown checks (deck-library.md).

A new version follows the library's rule. Nothing published is removed: a chapter, step, card or distractor that should go is retired (owl:deprecated true). Ids are never reused, since learners' decks keep them:

Each learner's deck follows the release it was copied or last upgraded to, and its outline with it. An upgrade is offered on the course's page, as on the deck's, when the newer release changes something the deck holds or describes, the outline (a chapter, a step or its theory, their order, or which questions they ask: sameOutline), or a question the learner has not reached yet. An upgrade of a course's deck adds no cards: the learner reaches new questions through the course. It still changes, retires and restores the cards the deck holds, their distractors included (migrations.md). A release that changes only the outline, or questions not reached yet, writes nothing to the deck but its release: the learner then reads the newer outline and meets the newer questions, with the chapters completed and the questions answered kept.

Why schema.org