feat(learn): Markdown files beside the PDFs, read formatted

A studyset's files take Markdown (.md, .markdown) as well as PDFs, in
the same synced rows with a kind (payload schema 3, written only for
Markdown, so older apps skip those rows). Upload a PDF becomes Upload a
file; a Markdown file must be UTF-8 text.

The reader opens one formatted and read-only through the editor's
Markdown reader, keeping fenced code, which that reader drops; the file
itself never changes, and its source text is a tap away, selectable too.
It has neither search, zoom nor screenshots. It reads in 100 parts by
its top-level blocks, so reading progress, deadlines and the cards and
notes tied to a place work as a PDF's pages do, shown as a share; its
outermost headings are its chapters.

Whats-New: Upload Markdown files beside your PDFs: they open formatted, with reading progress, deadlines and flashcards from what you select.
Whats-New-De: Lade Markdown-Dateien neben deine PDFs hoch: Sie öffnen sich formatiert, mit Lesefortschritt, Fristen und Karteikarten aus deiner Auswahl.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-10 21:30:46 +01:00
commit 151f848360
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -330,7 +330,7 @@
"studysetNotesInTopics": "The notes are in its topics",
"studysetNotesInTopicsHint": "Or write one here: \"Add material → Create a note\".",
"studysetFilesInTopics": "The files are in its topics",
"studysetFilesInTopicsHint": "Or add one here: \"Add material → Upload a PDF\".",
"studysetFilesInTopicsHint": "Or add one here: \"Add material → Upload a file\".",
"studysetBrowseAllFlashcards": "{count, plural, =1{Browse it} other{Browse all {count}}}",
"@studysetBrowseAllFlashcards": {"description": "Opens the card browser on every flashcard of a studyset's topics.", "placeholders": {"count": {"type": "int"}}},
"studysetCreateFlashcards": "Create flashcards",
@ -1326,8 +1326,8 @@
"studyButtonGone": "That studyset no longer exists.",
"pdfPageField": "PDF page",
"studysetTabFiles": "Files",
"pdfUploadMenu": "Upload a PDF",
"pdfFileType": "PDFs",
"pdfUploadMenu": "Upload a file",
"pdfFileType": "PDFs and Markdown",
"pdfAdded": "Added \"{title}\".",
"@pdfAdded": {"placeholders": {"title": {"type": "String"}}},
"pdfNotPdf": "This file isn't a PDF.",
@ -1339,7 +1339,7 @@
"pdfDuplicateKeep": "Keep the one there",
"pdfDuplicateAdd": "Add a copy",
"filesEmpty": "No files yet",
"filesEmptyHint": "Use \"Add material → Upload a PDF\" to add one.",
"filesEmptyHint": "Use \"Add material → Upload a file\" to add one.",
"filesSort": "Sort",
"filesSortCustom": "Custom order",
"filesSortName": "Name (A–Z)",
@ -2690,5 +2690,45 @@
"runnerOutline": "Outline",
"@runnerOutline": {"description": "The list of a learning path's steps beside or over its page in the Scroll view."},
"runnerScrollLocked": "Answer the question above to go on.",
"@runnerScrollLocked": {"description": "Below the question a learning path's page stops at, in the Scroll view."}
"@runnerScrollLocked": {"description": "Below the question a learning path's page stops at, in the Scroll view."},
"markdownNotText": "This file isn't Markdown text in UTF-8.",
"@markdownNotText": {"description": "A file named .md or .markdown that couldn't be read as text."},
"markdownTooLarge": "This Markdown file is larger than {megabytes} MB.",
"@markdownTooLarge": {"description": "A Markdown file over the server's limit for files.", "placeholders": {"megabytes": {"type": "int"}}},
"markdownShare": "{percent}%",
"@markdownShare": {"description": "How much of a Markdown file is read, or where in it something is, in percent", "placeholders": {"percent": {"type": "int"}}},
"markdownAt": "At {percent}%",
"@markdownAt": {"description": "Where in a Markdown file the cards or the note below are: a heading in the reader's panels", "placeholders": {"percent": {"type": "int"}}},
"readerByPlace": "Place",
"@readerByPlace": {"description": "Segment in a Markdown file's cards panel: the cards grouped by where in the file they are (as Page does for PDFs)"},
"proposalsFromPart": "From this part",
"@proposalsFromPart": {"description": "Button in a Markdown file's cards panel: an assistant proposes flashcards from the part of the file in view"},
"readerReferencedShare": "Referenced place: {percent}%",
"@readerReferencedShare": {"description": "Below a card being written beside a Markdown file: where in the file it is tied to", "placeholders": {"percent": {"type": "int"}}},
"readerUseCurrentShare": "Use current ({percent}%)",
"@readerUseCurrentShare": {"description": "Button: ties the card to the place in the Markdown file in view", "placeholders": {"percent": {"type": "int"}}},
"progressMarkdownShare": "{percent}% read",
"@progressMarkdownShare": {"description": "A studyset's Progress: how much of a Markdown file is read", "placeholders": {"percent": {"type": "int"}}},
"markdownDeadlineUpcoming": "Starts in {days, plural, =1{1 day} other{{days} days}} — {percent}%/day planned",
"@markdownDeadlineUpcoming": {"description": "A Markdown file's reading deadline not started yet", "placeholders": {"days": {"type": "int"}, "percent": {"type": "int"}}},
"markdownDeadlinePassed": "Deadline passed — {percent}% left",
"@markdownDeadlinePassed": {"description": "A Markdown file's reading deadline that passed", "placeholders": {"percent": {"type": "int"}}},
"markdownDeadlineToday": "Due today — {percent}% left",
"@markdownDeadlineToday": {"description": "A Markdown file's reading deadline due today", "placeholders": {"percent": {"type": "int"}}},
"markdownDeadlinePace": "{percent}%/day to reach {target}% in {days, plural, =1{1 day} other{{days} days}}",
"@markdownDeadlinePace": {"description": "A Markdown file's reading deadline on its way: how much to read a day", "placeholders": {"percent": {"type": "int"}, "target": {"type": "int"}, "days": {"type": "int"}}},
"markdownDeadlineHelp": "Read a share of the file by a day. Progress follows the furthest you scroll as you read.",
"@markdownDeadlineHelp": {"description": "Help in the deadline sheet of a Markdown file"},
"markdownDeadlineReach": "Reach (%)",
"@markdownDeadlineReach": {"description": "Field in the deadline sheet of a Markdown file: the share to reach, in percent"},
"markdownDeadlineChapterEntry": "{title} · to {percent}%",
"@markdownDeadlineChapterEntry": {"description": "A heading of a Markdown file offered as a deadline's target, with the share it ends at", "placeholders": {"title": {"type": "String"}, "percent": {"type": "int"}}},
"markdownToShareToday": "to {percent}% today",
"@markdownToShareToday": {"description": "Chip on a Markdown file's deadline: how far to read today", "placeholders": {"percent": {"type": "int"}}},
"markdownDeadlinesProgress": "{furthest}%/{target}% · {when}",
"@markdownDeadlinesProgress": {"description": "A Markdown file's deadline: how far it is read of the share to reach, and when", "placeholders": {"furthest": {"type": "int"}, "target": {"type": "int"}, "when": {"type": "String"}}},
"readerShowSource": "Show the source text",
"@readerShowSource": {"description": "Tooltip: shows a Markdown file as written, rather than formatted"},
"readerShowFormatted": "Show it formatted",
"@readerShowFormatted": {"description": "Tooltip: shows a Markdown file formatted again, after its source text"}
}