diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80d9d6d..b4f64c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,7 +104,7 @@ jobs: pnpm check:release-order "$BASE_SHA" - name: Type and content checks - run: pnpm test:spec && pnpm test:home && pnpm test:deploy:dev && pnpm test:release-update && pnpm check:release && pnpm check + run: pnpm test:spec && pnpm test:home && pnpm test:docs && pnpm test:deploy:dev && pnpm test:release-update && pnpm check:release && pnpm check env: GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a5a7525..203084c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -103,7 +103,7 @@ jobs: MDBASE_TS_DIR: .sources/mdbase - name: Check and build website - run: pnpm test:spec && pnpm test:home && pnpm check:release && pnpm check && pnpm build + run: pnpm test:spec && pnpm test:home && pnpm test:docs && pnpm check:release && pnpm check && pnpm build env: GH_TOKEN: ${{ github.token }} diff --git a/docs/app-documentation.md b/docs/app-documentation.md new file mode 100644 index 0000000..97887fb --- /dev/null +++ b/docs/app-documentation.md @@ -0,0 +1,53 @@ +# Reader and Writer documentation + +Reader documentation lives under `src/pages/apps/reader`; Writer documentation lives under +`src/pages/apps/writer`. `src/data/docs-sections.ts` supplies their navigation and the existing +SDK navigation. The Reader extension privacy page remains separate and unchanged. + +The guides describe committed application behaviour. They were checked against Reader commit +`74999d1be78749f7333054007f7b5247b6a79ef9` and Writer commit +`9cb61aa5350259f87680072e93100c5e431f0fd8`. Uncommitted local reliability work is not documented +as shipped behaviour. Keep the guides in step with app changes, particularly recovery, exports, +source capture and extension permissions. + +## Recordings + +The five recordings and posters are served from `public/videos` on the documentation origin. +Do not depend on authenticated GitHub attachments, temporary recording directories or preview +hosting for production playback. MP4s are about 1.5–3.8 MB each. Posters are resized to 960 px. + +The recording agent confirmed that all videos use in-memory sample data, with no real account, +private collection content or personal data. Reader samples use public-domain James, Thoreau +and Emerson excerpts and a fictional article. Writer samples use Darwin-themed manuscripts, +public-domain quotations and fictional authors and reviewers. The live Writer demo may use +different sample content. + +These are silent recordings with captions burned into the picture. `src/data/doc-videos.json` +provides a text equivalent of each sequence; review that text whenever a video is replaced. +`DocVideo.astro` uses native controls, inline playback, no autoplay or loop, and `preload="none"`. +The page loads a poster; the MP4 is requested on playback. Links permit downloading the files. + +| Recording | Placement | SHA-256 of MP4 | +| --- | --- | --- | +| `reader-markdown` | Reader getting started | `facce287ffa892083736bad3c82d83cb41e22ab97914843f6260f7e0ad57f317` | +| `extension-capture` | Reader browser extension | `c57cf032886260fed0370eb206812c52844aad76a07363f31231205c8b287573` | +| `reader-library` | Reader library and views | `81892c8efe4528ee2b6fa93d38eaa2abddde7dc938352ef90fa3b8188a765349` | +| `writer-paper` | Writer getting started | `ce34aa1b8046715dd435ff730568b980116942db10986bb6b3ab06166a62cc14` | +| `writer-book` | Writer chapters and embeds | `a587ba3ec2b23d8c12fdfbea14be2038c1e1276b3bec477728e5c91366b28bd3` | + +## Checks and deployment + +```sh +pnpm test:docs +pnpm check +pnpm build +MDBASE_SPEC_DIR=/absolute/path/to/mdbase-spec pnpm import:spec +pnpm check:links +``` + +`test:docs` checks the catalogue, documentation navigation, recording assets and text descriptions, +and opt-in accessible video controls. Site CI and the production build run it too. + +Production is the repository's GitHub Pages workflow (`.github/workflows/deploy.yml`), triggered +by a merge to `main`. Cloudflare Pages is only the separate development deployment. Verify +`/apps/`, both documentation roots, all five video files and their posters after deployment. diff --git a/package.json b/package.json index fd32c89..3822541 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,9 @@ "test:deploy:dev": "node --test scripts/deploy-pages-dev.test.mjs", "test:release-update": "node --test scripts/connect-release-record.test.mjs", "test:home": "node --test scripts/home-animation.test.mjs", + "test:docs": "node --test scripts/app-docs.test.mjs", "test:spec": "node --test scripts/spec-page.test.mjs", - "test": "pnpm test:spec && pnpm test:home && pnpm test:deploy:dev && pnpm test:release-update && pnpm check:release && pnpm check && pnpm build && pnpm import:spec && pnpm check:links" + "test": "pnpm test:spec && pnpm test:home && pnpm test:docs && pnpm test:deploy:dev && pnpm test:release-update && pnpm check:release && pnpm check && pnpm build && pnpm import:spec && pnpm check:links" }, "dependencies": { "@astrojs/sitemap": "^3.7.3", diff --git a/public/videos/extension-capture.jpg b/public/videos/extension-capture.jpg new file mode 100644 index 0000000..731096e Binary files /dev/null and b/public/videos/extension-capture.jpg differ diff --git a/public/videos/extension-capture.mp4 b/public/videos/extension-capture.mp4 new file mode 100644 index 0000000..2e17c99 Binary files /dev/null and b/public/videos/extension-capture.mp4 differ diff --git a/public/videos/reader-library.jpg b/public/videos/reader-library.jpg new file mode 100644 index 0000000..852e1b4 Binary files /dev/null and b/public/videos/reader-library.jpg differ diff --git a/public/videos/reader-library.mp4 b/public/videos/reader-library.mp4 new file mode 100644 index 0000000..c0e16ca Binary files /dev/null and b/public/videos/reader-library.mp4 differ diff --git a/public/videos/reader-markdown.jpg b/public/videos/reader-markdown.jpg new file mode 100644 index 0000000..0cae6b6 Binary files /dev/null and b/public/videos/reader-markdown.jpg differ diff --git a/public/videos/reader-markdown.mp4 b/public/videos/reader-markdown.mp4 new file mode 100644 index 0000000..a051e63 Binary files /dev/null and b/public/videos/reader-markdown.mp4 differ diff --git a/public/videos/writer-book.jpg b/public/videos/writer-book.jpg new file mode 100644 index 0000000..461082d Binary files /dev/null and b/public/videos/writer-book.jpg differ diff --git a/public/videos/writer-book.mp4 b/public/videos/writer-book.mp4 new file mode 100644 index 0000000..30c5be5 Binary files /dev/null and b/public/videos/writer-book.mp4 differ diff --git a/public/videos/writer-paper.jpg b/public/videos/writer-paper.jpg new file mode 100644 index 0000000..5d6034b Binary files /dev/null and b/public/videos/writer-paper.jpg differ diff --git a/public/videos/writer-paper.mp4 b/public/videos/writer-paper.mp4 new file mode 100644 index 0000000..5cdb95b Binary files /dev/null and b/public/videos/writer-paper.mp4 differ diff --git a/scripts/app-docs.test.mjs b/scripts/app-docs.test.mjs new file mode 100644 index 0000000..920992c --- /dev/null +++ b/scripts/app-docs.test.mjs @@ -0,0 +1,67 @@ +import assert from "node:assert/strict"; +import { readFileSync, existsSync } from "node:fs"; +import { test } from "node:test"; +import { fileURLToPath } from "node:url"; + +const root = new URL("../", import.meta.url); +const read = (path) => readFileSync(new URL(path, root), "utf8"); +const videos = JSON.parse(read("src/data/doc-videos.json")); +const placements = { + "reader-markdown": "src/pages/apps/reader/index.astro", + "extension-capture": "src/pages/apps/reader/extension/index.astro", + "reader-library": "src/pages/apps/reader/library/index.astro", + "writer-paper": "src/pages/apps/writer/index.astro", + "writer-book": "src/pages/apps/writer/chapters/index.astro" +}; + +test("Reader and Writer are listed with live applications and local documentation", () => { + const catalogue = read("src/pages/apps/index.astro"); + for (const app of ["reader", "writer"]) { + assert.ok(catalogue.includes(`https://${app}.mdbase.dev/`)); + assert.ok(catalogue.includes(`/apps/${app}/`)); + } + assert.ok(catalogue.includes("https://reader.mdbase.dev/?preview=1")); + assert.ok(catalogue.includes("https://lab.mdbase-writer.pages.dev/?demo")); +}); + +test("Every documentation navigation entry resolves to a site page", () => { + const sections = read("src/data/docs-sections.ts"); + const links = [...sections.matchAll(/href: "([^"]+)"/g)].map((match) => match[1]); + assert.ok(links.length > 0); + assert.equal(links.filter((href) => href.startsWith("/apps/reader/")).length, 9); + assert.equal(links.filter((href) => href.startsWith("/apps/writer/")).length, 6); + for (const href of links) { + const page = new URL(`src/pages${href}index.astro`, root); + assert.ok(existsSync(page), `Missing navigation target: ${fileURLToPath(page)}`); + } + assert.match(sections, /value: release\.sdkVersion/); +}); + +test("Five sample recordings have local playable files, posters and text equivalents", () => { + assert.deepEqual(Object.keys(videos).sort(), Object.keys(placements).sort()); + for (const [name, metadata] of Object.entries(videos)) { + const mp4 = readFileSync(new URL(`public/videos/${name}.mp4`, root)); + const poster = readFileSync(new URL(`public/videos/${name}.jpg`, root)); + assert.equal(mp4.subarray(4, 8).toString(), "ftyp", `${name}: MP4 container`); + assert.equal(poster.subarray(0, 2).toString("hex"), "ffd8", `${name}: JPEG poster`); + assert.ok(metadata.title && metadata.description); + assert.match(metadata.duration, /^\d+:\d{2}$/); + assert.ok(metadata.width > 0 && metadata.height > 0); + assert.ok(metadata.steps.length >= 4, `${name}: text description of the actions`); + assert.ok(read(placements[name]).includes(``)); + } +}); + +test("Video controls are opt-in and have accessible labels and descriptions", () => { + const component = read("src/components/DocVideo.astro"); + const attributes = component.match(/]+)>/)[1]; + assert.match(attributes, /\bcontrols\b/); + assert.match(attributes, /\bplaysinline\b/); + assert.match(attributes, /preload="none"/); + assert.match(attributes, /poster=/); + assert.match(attributes, /aria-labelledby=/); + assert.match(attributes, /aria-describedby=/); + assert.doesNotMatch(attributes, /\bautoplay\b|\bloop\b/); + assert.match(component, /Text description of the recording/); + assert.match(component, /download/); +}); diff --git a/src/components/DocVideo.astro b/src/components/DocVideo.astro new file mode 100644 index 0000000..c653d51 --- /dev/null +++ b/src/components/DocVideo.astro @@ -0,0 +1,51 @@ +--- +import videos from "../data/doc-videos.json"; + +interface Props { + name: keyof typeof videos; +} + +const { name } = Astro.props; +const video = videos[name]; +const titleId = `video-${name}-title`; +const descriptionId = `video-${name}-description`; +const src = `/videos/${name}.mp4`; +--- + +
+
+ {video.title} + {video.duration} +
+ +

{video.description} Silent recording with on-screen captions and sample data.

+
+ Text description of the recording +
    {video.steps.map((step) =>
  1. {step}
  2. )}
+
+

Download MP4

+
+ + diff --git a/src/components/DocsLayout.astro b/src/components/DocsLayout.astro index 1838964..b68af51 100644 --- a/src/components/DocsLayout.astro +++ b/src/components/DocsLayout.astro @@ -1,54 +1,25 @@ --- import BaseLayout from "../layouts/BaseLayout.astro"; -import release from "../data/connect-release.json"; +import { docsSections, type DocsSectionId } from "../data/docs-sections"; interface Props { title: string; description: string; active: string; + section?: DocsSectionId; headings?: Array<{ href: string; label: string }>; } -const { title, description, active, headings = [] } = Astro.props; -const docGroups = [ - { - label: "Start", - docs: [ - { href: "/sdk/quickstart/", label: "Run the source-built sandbox", key: "quickstart" }, - { href: "/sdk/connect-quickstart/", label: "Connect a browser app", key: "connect-quickstart" }, - { href: "/sdk/", label: "SDK overview", key: "overview" } - ] - }, - { - label: "Build", - docs: [ - { href: "/sdk/portable-apps/", label: "Portable HTML apps", key: "portable-apps" }, - { href: "/sdk/manifest/", label: "Application manifest", key: "manifest" }, - { href: "/sdk/contracts/", label: "Contracts and adapters", key: "contracts" }, - { href: "/sdk/authorization/", label: "Authorization", key: "authorization" }, - { href: "/sdk/operations/", label: "Records and operations", key: "operations" }, - { href: "/sdk/routing/", label: "Authority routes", key: "routing" }, - { href: "/sdk/testing/", label: "Testing", key: "testing" }, - { href: "/sdk/notifications/", label: "Notifications and timers", key: "notifications" }, - { href: "/sdk/offline-sync/", label: "Offline sync", key: "offline-sync" } - ] - }, - { - label: "Reference", - docs: [ - { href: "/sdk/security/", label: "Security model", key: "security" }, - { href: "/sdk/api/", label: "API reference", key: "api" } - ] - } -]; +const { title, description, active, section = "sdk", headings = [] } = Astro.props; +const { label, navLabel, headerCurrent, sourcePath, meta, groups: docGroups } = docsSections[section]; --- - +
-

Connect SDK

+

{label}

{title}

{description}
diff --git a/src/data/doc-videos.json b/src/data/doc-videos.json new file mode 100644 index 0000000..283d075 --- /dev/null +++ b/src/data/doc-videos.json @@ -0,0 +1,69 @@ +{ + "reader-markdown": { + "title": "Highlight a passage, get a Markdown file", + "duration": "0:43", + "width": 1600, + "height": 900, + "description": "Create a highlight with commentary, inspect its Markdown record, and embed it in a literature note.", + "steps": [ + "The sample library opens The Principles of Psychology beside a view of its Markdown source record.", + "A passage in the section on voluntary attention is selected. The comment field opens and a note is added before Save highlight is chosen.", + "The passage becomes a yellow highlight. A new annotation file appears with its source link, document revision, quoted text, target and commentary.", + "The annotation panel lists the passage and comment. Add to literature note inserts an ordinary annotation embed into the source note." + ] + }, + "extension-capture": { + "title": "Save from the web with the extension", + "duration": "1:02", + "width": 1440, + "height": 900, + "description": "Save a sample article and several highlighted passages from Chrome's side panel, then open the saved copy in Reader.", + "steps": [ + "A fictional article, The marginalia problem, is open in Chrome. Reader's side panel identifies the page and the destination sample collection.", + "Text is selected on the page. A comment is typed in the panel and a colour is chosen to save the page and highlight.", + "The saved passage is highlighted on the live page. Further selections are saved in other colours and appear in Saved highlights.", + "Selecting a saved highlight finds it on the page. Open in Reader opens the source's readable saved copy.", + "Reader shows the same highlights and comments on the saved copy, with a Markdown file for each annotation." + ] + }, + "reader-library": { + "title": "Views, annotations and panes", + "duration": "0:46", + "width": 1440, + "height": 900, + "description": "Browse table and shelf views, search annotations across sources, and open two reading panes.", + "steps": [ + "The sample library switches between All sources, a Bookshelf view and a Currently reading view.", + "Annotations replaces the source list with a table of passages. A search for attention narrows the table to a matching highlight.", + "The command palette opens The Principles of Psychology. Another source, Walden, is opened beside it.", + "The two documents remain visible together. Tabs can be docked and split; the arrangement is saved per collection." + ] + }, + "writer-paper": { + "title": "Write, cite and typeset a paper", + "duration": "0:55", + "width": 1600, + "height": 900, + "description": "Complete a citation, correct an unknown citekey and insert a Reader quotation while the paper preview updates.", + "steps": [ + "A sample paper about Darwin is open with Outline, its Markdown editor and the typeset article preview side by side.", + "A citation is typed in the introduction. Completion finds a source in the Reader library and inserts its citekey with a page locator.", + "A deliberately misspelled citekey is marked on its Markdown line. The suggested matching source corrects it, and the preview updates.", + "Sources is searched for vegetation. A highlighted quotation is inserted from the matching Reader source, including its citation and page.", + "The inserted text appears in the editor and the typeset paper. The manuscript remains an ordinary collection record." + ] + }, + "writer-book": { + "title": "Build a book from chapter records", + "duration": "0:49", + "width": 1600, + "height": 900, + "description": "Open and edit a chapter, reorder chapters and add a new chapter in a thesis-layout manuscript.", + "steps": [ + "The sample book Patient Observation embeds Coral reefs and Earthworms as separate chapter records. The thesis preview shows a title page and chapters.", + "Coral reefs is opened from its chapter card. Text is added to that record, and the whole-book preview updates.", + "The chapters are reordered in Outline; their order and chapter numbering change in the preview.", + "Add chapter creates Afterlives and embeds it after the existing chapters. Writing in the new record produces another typeset chapter." + ] + } +} diff --git a/src/data/docs-sections.ts b/src/data/docs-sections.ts new file mode 100644 index 0000000..432a38c --- /dev/null +++ b/src/data/docs-sections.ts @@ -0,0 +1,105 @@ +import release from "./connect-release.json"; + +export type DocsSectionId = "sdk" | "reader" | "writer"; + +type DocLink = { href: string; label: string; key: string }; +type DocsSection = { + label: string; + navLabel: string; + headerCurrent: "sdk" | "apps"; + sourcePath: string; + meta: { label: string; value: string }; + groups: Array<{ label: string; docs: DocLink[] }>; +}; + +export const docsSections: Record = { + sdk: { + label: "Connect SDK", + navLabel: "SDK documentation", + headerCurrent: "sdk", + sourcePath: "src/pages/sdk", + meta: { label: "@mdbase-dev/connect", value: release.sdkVersion }, + groups: [ + { + label: "Start", + docs: [ + { href: "/sdk/quickstart/", label: "Run the source-built sandbox", key: "quickstart" }, + { href: "/sdk/connect-quickstart/", label: "Connect a browser app", key: "connect-quickstart" }, + { href: "/sdk/", label: "SDK overview", key: "overview" } + ] + }, + { + label: "Build", + docs: [ + { href: "/sdk/portable-apps/", label: "Portable HTML apps", key: "portable-apps" }, + { href: "/sdk/manifest/", label: "Application manifest", key: "manifest" }, + { href: "/sdk/contracts/", label: "Contracts and adapters", key: "contracts" }, + { href: "/sdk/authorization/", label: "Authorization", key: "authorization" }, + { href: "/sdk/operations/", label: "Records and operations", key: "operations" }, + { href: "/sdk/routing/", label: "Authority routes", key: "routing" }, + { href: "/sdk/testing/", label: "Testing", key: "testing" }, + { href: "/sdk/notifications/", label: "Notifications and timers", key: "notifications" }, + { href: "/sdk/offline-sync/", label: "Offline sync", key: "offline-sync" } + ] + }, + { + label: "Reference", + docs: [ + { href: "/sdk/security/", label: "Security model", key: "security" }, + { href: "/sdk/api/", label: "API reference", key: "api" } + ] + } + ] + }, + reader: { + label: "mdbase Reader", + navLabel: "mdbase Reader documentation", + headerCurrent: "apps", + sourcePath: "src/pages/apps/reader", + meta: { label: "Status", value: "Prerelease" }, + groups: [ + { label: "Start", docs: [{ href: "/apps/reader/", label: "Get started", key: "overview" }] }, + { + label: "Use", + docs: [ + { href: "/apps/reader/sources/", label: "Adding sources", key: "sources" }, + { href: "/apps/reader/annotating/", label: "Reading and annotating", key: "annotating" }, + { href: "/apps/reader/notes/", label: "Notes and citations", key: "notes" }, + { href: "/apps/reader/library/", label: "Library and views", key: "library" }, + { href: "/apps/reader/import/", label: "Importing a library", key: "import" }, + { href: "/apps/reader/extension/", label: "Browser extension", key: "extension" } + ] + }, + { + label: "Reference", + docs: [ + { href: "/apps/reader/data/", label: "How Reader stores data", key: "data" }, + { href: "/apps/reader/shortcuts/", label: "Shortcuts and display", key: "shortcuts" } + ] + } + ] + }, + writer: { + label: "mdbase writer", + navLabel: "mdbase writer documentation", + headerCurrent: "apps", + sourcePath: "src/pages/apps/writer", + meta: { label: "Status", value: "Prerelease" }, + groups: [ + { label: "Start", docs: [{ href: "/apps/writer/", label: "Get started", key: "overview" }] }, + { + label: "Use", + docs: [ + { href: "/apps/writer/writing/", label: "Writing and reviewing", key: "writing" }, + { href: "/apps/writer/citations/", label: "Sources and citations", key: "citations" }, + { href: "/apps/writer/chapters/", label: "Chapters and embeds", key: "chapters" }, + { href: "/apps/writer/export/", label: "Layouts and export", key: "export" } + ] + }, + { + label: "Reference", + docs: [{ href: "/apps/writer/data/", label: "Saving and collection data", key: "data" }] + } + ] + } +}; diff --git a/src/pages/apps/index.astro b/src/pages/apps/index.astro index 4b33e25..44df789 100644 --- a/src/pages/apps/index.astro +++ b/src/pages/apps/index.astro @@ -35,6 +35,31 @@ const applications: CatalogueEntry[] = [ { label: "Source", href: "https://github.com/mdbase-dev/mdbase-connect/tree/main/apps/editor" } ] }, + { + name: "mdbase Reader", + description: "Read, highlight and organise PDFs, EPUBs and saved web pages, with Markdown notes and citation metadata.", + status: "Development build", + runsOn: "Web; optional Chrome extension", + collections: "Hosted by Connect or local through Connector", + links: [ + { label: "Open Reader", href: "https://reader.mdbase.dev/" }, + { label: "Documentation", href: "/apps/reader/" }, + { label: "Try the preview", href: "https://reader.mdbase.dev/?preview=1" }, + { label: "Chrome extension", href: "https://chromewebstore.google.com/detail/mdbase-reader/kimdfjefhbfgfecconmaiaaindjccidp" } + ] + }, + { + name: "mdbase writer", + description: "Write Markdown papers, theses and books with a typeset preview, citations from Reader, and PDF or Word export.", + status: "Development build", + runsOn: "Web", + collections: "Hosted by Connect or local through Connector", + links: [ + { label: "Open writer", href: "https://writer.mdbase.dev/" }, + { label: "Documentation", href: "/apps/writer/" }, + { label: "Try the demo", href: "https://lab.mdbase-writer.pages.dev/?demo" } + ] + }, { name: "mdbase for Obsidian", description: "Obsidian plugin for collection setup, type editing, migration, validation, and hosted collection mirrors.", diff --git a/src/pages/apps/reader/annotating/index.astro b/src/pages/apps/reader/annotating/index.astro new file mode 100644 index 0000000..9aaa2ca --- /dev/null +++ b/src/pages/apps/reader/annotating/index.astro @@ -0,0 +1,135 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +--- + + +

Reading

+

+ Reader opens PDFs, EPUBs and saved web pages. Open a document's contents + from the document toolbar. Enter reading mode{" "} + (Ctrl .) hides everything except the document. + Press it again or Esc to leave. +

+

+ Text size, typeface and line length can be changed from{" "} + Text and display in the header. These apply to EPUBs and + saved web pages. PDFs keep their layout. When you come back to a source, + Reader restores its saved reading position when it matches the open document. +

+ +

Highlight a passage

+

+ Select text in the document. A toolbar appears next to the selection: +

+
    +
  • Highlight (H) saves the passage as a highlight straight away.
  • +
  • Comment (C) opens a comment field. Choose Save highlight to save the highlight with your comment.
  • +
  • Copy text copies the passage, and Copy with citation copies it as a quote citing the source.
  • +
+

+ Esc dismisses the toolbar. By default, selecting text does not + create an annotation until you choose an action. Use the colour controls + to choose a highlight colour; highlights are also identified by their text + and annotation details. +

+

+ If you highlight a lot, set Selecting text to{" "} + Highlight at once in Text and display. + Every selection is then saved as a highlight as soon as you make it. +

+ +

Capture a PDF area

+

+ For a figure, table or equation in a PDF, choose Select area{" "} + in the document toolbar and drag across the part of the page you want. + The captured image is saved as a PNG file in the collection's{" "} + files folder, and the annotation embeds it. You can add a + comment before saving. A new crop is held in memory until it is saved; + reloading can lose the unsaved image and comment. +

+ +

Comment on the source

+

+ New comment in the annotation panel records a thought about + the source as a whole, with no passage attached. +

+ +

Bookmarks

+

+ Bookmark this position in the document toolbar, or the + command of the same name, saves your current place as an annotation. The + document must be open. +

+ +

The annotation list

+

+ Open a source's Annotations view, or the side panel while + reading. You can search annotations, filter them to highlights, area + captures, comments on the source, bookmarks or those with commentary, and + order them by position in the document or newest first. +

+

+ Show in document jumps to an annotation's passage.{" "} + Add to literature note embeds it in the source's note. See{" "} + Notes and citations. +

+ +

Edit and delete

+

+ Comments save automatically about a second after you stop typing. If a save + fails, your text stays in the editor with Retry save. + Unsaved comments are kept in memory only, so a forced reload or a crash can + lose text that was not saved yet. Reader warns you before you close a tab + with unsaved changes. +

+

+ If the annotation was changed elsewhere, for example in another app, Reader + keeps your version and lets you compare it with the collection version + before choosing. An annotation can be edited in one pane at a time. +

+

+ Deleting an annotation asks for confirmation. If literature notes embed it, + Reader lists them first, because those embeds will break. +

+ +

When a file changes

+

+ Each annotation records the exact version of the file it was made on. + Reader will not silently move an annotation to a passage it cannot confirm. +

+
    +
  • PDF highlights and bookmarks stay attached when a PDF is re-saved without its pages changing, for example after a metadata edit.
  • +
  • Web page highlights are found again by their quoted text.
  • +
  • EPUB positions belong to one version of the book. After the file changes, Reader checks the quoted text before it follows an old position.
  • +
+ +

Connection and unsaved work

+

+ Reader needs Connect access to open collection files and save changes. + A document already loaded may remain readable during a connection failure, + but this is not an offline-library mode. The former + Available offline feature has been removed. +

+

+ Wait for annotation saves to finish before closing or reloading. Pending + annotation text and new PDF crops are held in memory. Literature notes + have a separate local-draft recovery path; see + Saving and conflicts. +

+
diff --git a/src/pages/apps/reader/data/index.astro b/src/pages/apps/reader/data/index.astro new file mode 100644 index 0000000..3a4605d --- /dev/null +++ b/src/pages/apps/reader/data/index.astro @@ -0,0 +1,173 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +import CodeBlock from "../../../../components/CodeBlock.astro"; + +const layout = `sources/ + src_7d7c62fd-….md one record per source +annotations/ + ann_fa4cdc4f-….md one record per annotation +files/ + reader/src_7d7c62fd-…/ + distant-reading.pdf the source's original file + annotation-9b21….png an area capture +views/ + reading-this-month.md a saved library view +_types/ + reader-source.md + reader-annotation.md`; + +const source = `--- +type: reader-source +id: src_7d7c62fd-… +title: Distant Reading +kind: document +authors: [Franco Moretti] +saved_at: 2026-08-10T06:17:37.487Z +reading: + status: reading +documents: + - file: '[[files/reader/src_7d7c62fd-…/distant-reading.pdf]]' + file_id: 019fea51-… + role: primary + format: pdf + media_type: application/pdf + revision: sha256:c8b2e7… +csl: + id: morettiDistant13 + type: book + title: Distant Reading +--- +Argues that close reading scales poorly to large corpora. [@morettiDistant13] + +![[annotations/ann_fa4cdc4f-…]]`; + +const annotation = `--- +type: reader-annotation +id: ann_fa4cdc4f-… +source: '[[sources/src_7d7c62fd-…|Distant Reading]]' +annotation_type: highlight +motivation: commenting +color: yellow +document: + file: '[[files/reader/src_7d7c62fd-…/distant-reading.pdf]]' + file_id: 019fea51-… + revision: sha256:c8b2e7… +target: + quote: + exact: the trouble with close reading + prefix: 'And ' + pdf: { … } +created_at: 2026-08-11T09:02:14.120Z +created_by: dev.mdbase.reader +tags: [] +--- +> the trouble with close reading + +This is the claim to test against the sampling chapter.`; +--- + + +

+ Everything Reader saves is an ordinary Markdown record or file in your + collection. There is no separate Reader database. You can read, edit, back + up or move the records with any tool. +

+ +

Collection layout

+ +

+ This is an illustrative layout for the starter types; names and type folders + follow the collection's configuration. Imported files go under{" "} + files/reader/imports. Record links point to source paths, so tools + such as Obsidian can resolve them. +

+ +

Source records

+

+ A source record describes one work. Its body is your literature note. +

+ + + + + + + + + + + + + +
FieldHolds
id, title, kind, saved_atRequired. The source's stable identity, title, kind and when it was added.
authors, published, url, description, language, tagsOptional details shown in the library.
documentsThe source's files. Each entry links to a file and records its format and the SHA-256 revision that was saved.
readingReading status, progress from 0 to 1, and the saved position associated with a document file.
relationsLinks to related sources.
cslOne CSL-JSON item. Its id is the citation key.
+

+ Unknown fields are kept, so you can add your own. +

+ +

Annotation records

+

+ Each highlight, comment, bookmark or area capture is its own record. Its + body holds the quoted passage, your comment, or both. +

+ + + + + + + + + + + + + + +
FieldHolds
sourceA link to the source record.
annotation_typehighlight, note (a comment on the source), bookmark or area.
documentThe file and the exact revision the annotation was made on.
targetWhere the annotation points: the quoted text with its surrounding context, plus a PDF position, EPUB CFI or web page selector.
locatorA readable position label, such as a page.
motivation, color, tagsWhy it was made, its colour and its tags.
created_at, modified_at, created_byWhen and by which app it was made.
+ +

Types and contracts

+

+ Reader reads and writes through two application contracts,{" "} + dev.mdbase.reader.source and{" "} + dev.mdbase.reader.annotation. The starter types{" "} + reader-source and reader-annotation implement + them with the field names shown above. A collection can use its own types + and field names instead, as long as they implement the contracts. See{" "} + Contracts and adapters. +

+

+ The starter types assign records by folder and fields: records in{" "} + sources with id, title and{" "} + kind are sources, and records in annotations with{" "} + id, source and annotation_type are + annotations. +

+ +

What stays on the device

+
    +
  • Pane arrangement and display preferences.
  • +
  • Literature note drafts that have not reached the collection yet.
  • +
  • Connect access grants used by this browser, subject to your collection approval.
  • +
+

+ Pending annotation comments and new PDF crops are held in memory and can be + lost on reload. Local source-note drafts are best-effort browser recovery, + not backups. Reader no longer provides the former offline-document copy feature. +

+

+ Open mdbase writer with the same collection to + reuse citation metadata and Reader quotations in manuscripts. +

+
diff --git a/src/pages/apps/reader/extension/index.astro b/src/pages/apps/reader/extension/index.astro new file mode 100644 index 0000000..93c3147 --- /dev/null +++ b/src/pages/apps/reader/extension/index.astro @@ -0,0 +1,134 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +import DocVideo from "../../../../components/DocVideo.astro"; +--- + + +

Capture and highlight

+ + +

Install

+

+ Install mdbase Reader from the Chrome Web Store + in Chrome 123 or newer. Store-installed copies receive Chrome's automatic + updates. Pin Reader from Chrome's extensions menu so its button is easy to reach. +

+

+ If you used an unpacked development copy, disable it to avoid duplicate + capture actions. The store copy has its own identity and needs a fresh + Connect approval; settings and grants do not migrate automatically. +

+

+ If you have an unpacked development build, open chrome://extensions, + enable Developer mode and choose Load unpacked. + Select the folder containing manifest.json. Unpacked copies + need manual updates; use the store version for ordinary reading. +

+

+ The extension saves the page you are viewing in your browser. Because it + reads the page you already have open, it works for pages that Reader's own + capture service cannot fetch, such as articles behind a sign-in. +

+ +

Connect a collection

+

+ Open the extension and choose Connect. It opens an mdbase + Connect approval for a collection, like any other app. Choose the + destination collection. The extension remembers it for next time. +

+ +

Save a page

+

+ Click the toolbar button or press Alt Shift S. + Reader opens in Chrome's side panel for that tab. Check the title, add tags + and a note if you like, then choose Save page or + Save PDF. Opening the panel or changing collections does + not save a page. Once saved, Open in Reader takes you to the source. +

+
    +
  • + For an article, Reader extracts the main text and saves a readable copy, + plus an archive of the page without any form values you typed. +
  • +
  • + For a PDF open in Chrome's viewer, it saves the PDF. The file is + downloaded from the page itself, so your own access applies. +
  • +
+

+ If the page is already in the collection, the panel finds the existing + source instead of creating a duplicate. Addresses are compared without + tracking parameters, www., AMP variants or trailing slashes. +

+ +

Highlight as you read

+

+ The panel stays open while you read. Each new text selection appears in it, + so you can highlight several passages in a row. Add a comment or tags if + you like, then choose a colour to save the passage. Ctrl + Enter (⌘ Enter on a Mac) also saves the + selected passage, or the unsaved page if no passage is selected. You can also + press Alt Shift H, or right-click and + choose Save highlight to mdbase Reader or{" "} + Add a note in mdbase Reader. +

+

+ Highlights are anchored to the saved copy of the page. When a passage + appears more than once, Reader uses the occurrence whose surrounding text + matches best, and only if one clearly does. Otherwise the highlight is not + saved, and your note stays in the panel so you can try again. +

+

+ Unsaved capture drafts are kept per tab and page for the browser session, + so closing the side panel does not discard them. They are not collection + backups. For PDF highlighting, save the file and choose + Open in Reader to highlight. +

+ +

Citation details

+

+ When a page carries a DOI or scholarly metadata, the panel shows the + citation it will store. With a DOI, the citation comes from the DOI + registry, and only the DOI is sent. Otherwise it comes from the page's own + metadata tags. +

+ +

Mark pages you have saved

+

+ Settings → Mark pages I've saved is off by default. When + you turn it on, the extension asks for permission to read the pages you + visit. It then checks each HTTPS page against your collection, shows a + badge with a highlight count or a saved-page check mark, and displays saved + highlights. Opening the side panel can also show highlights for that page. + Turning saved-page marking off removes its optional HTTPS permission. +

+ +

Permissions

+
    +
  • + Without Mark pages I've saved, the extension can only + read a page when you open the panel on it. +
  • +
  • + Its only permanent site access is the mdbase Connect API, which it uses + to reach your collection. That access cannot read the pages you browse. +
  • +
  • Its access to your collection comes from the Connect approval, which you can revoke in Connect.
  • +
  • See the extension privacy policy for capture data, permissions and third-party lookups.
  • +
+
diff --git a/src/pages/apps/reader/import/index.astro b/src/pages/apps/reader/import/index.astro new file mode 100644 index 0000000..836ee93 --- /dev/null +++ b/src/pages/apps/reader/import/index.astro @@ -0,0 +1,99 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +--- + + +
+ Importing is experimental. +

+ Try it on a new or test collection first. An import only adds records and + files. It does not change your Zotero or Readwise library. +

+
+ +

Start an import

+

+ Run Import a library… from the command palette, or open + Reader's import page. + Choose Zotero or Readwise Reader. Keep the import tab open while it runs. +

+ +

From Zotero

+

+ Zotero imports bring references, original files, notes, collections and + Zotero's own annotations. They use a small Zotero plugin that writes an + export folder for Reader. +

+
    +
  1. Download the Zotero exporter (.xpi) from the import page. It supports Zotero 10.0.x.
  2. +
  3. In Zotero, choose Tools → Plugins → Install Plugin From File… and select the file.
  4. +
  5. Choose Tools → Export for mdbase Reader… and save the export folder.
  6. +
  7. In Reader, select the whole exported folder. Do not select Zotero's data directory.
  8. +
+

+ The experimental exporter includes all of My Library, + not a selected collection or group library. Finish syncing and avoid editing + Zotero while exporting. Only a completed export can be imported. +

+

+ Reader checks every payload file against its checksum before collection + writes. Selecting a folder needs a supporting browser such as Chrome. + Export bundles contain private notes and attachment content; keep them private. +

+ +

From Readwise Reader

+

+ Readwise imports bring Reader documents and files, plus highlights from + classic Readwise sources such as Kindle, Apple Books and Instapaper. + Classic sources import as metadata and highlights, without the book text. +

+
    +
  1. Get your Readwise access token and paste it into Reader.
  2. +
  3. Choose whether to include unsaved Feed items. Archived library items are always included.
  4. +
  5. Choose Scan Readwise library.
  6. +
+

+ The token stays in memory in that browser tab and is sent only to Readwise. + The importer only reads from Readwise. Scanning reads metadata, highlights + and saved article pages. Original PDFs and EPUBs are downloaded only after + you confirm. A large library can take several minutes to scan. +

+ +

Review and confirm

+

+ Reader shows what it found: the number of sources, annotations and notes, + and the content files and their size. Then choose a destination collection. + If the collection does not have Reader's setup yet, you review and apply it + first. Nothing is written until you confirm. +

+

+ Imported files are stored under files/reader/imports. When the + import finishes, Open imported collection takes you to it. +

+ +

Stopping and resuming

+

+ Choose Stop safely to pause an active import. Everything + already written is kept, and Resume import continues the + confirmed job. The job stays bound to its destination; switching collections + cannot redirect it. +

+

+ After reloading, reselect the same export folder or reconnect Readwise, + choose the same collection and scan again. Check the destination before + resuming. Records already imported are kept; review the completion warnings + for missing or unavailable content. +

+
diff --git a/src/pages/apps/reader/index.astro b/src/pages/apps/reader/index.astro new file mode 100644 index 0000000..3d19a88 --- /dev/null +++ b/src/pages/apps/reader/index.astro @@ -0,0 +1,128 @@ +--- +import DocsLayout from "../../../components/DocsLayout.astro"; +import DocVideo from "../../../components/DocVideo.astro"; +--- + + +
+ Reader is prerelease software. +

+ Keep backups of important collections and check the save status before + closing or reloading. Sources, notes and annotations remain ordinary + mdbase records. +

+
+ +

What it is

+

+ Reader is for reading, annotating, organising and citing source material. + Each source, such as a paper, a book or an article, is a Markdown record in + your collection. Its file sits beside it, and every highlight or comment is + a separate Markdown record. Other mdbase tools, a text editor or Obsidian + can read the collection records and files. See How Reader stores data. + To write a manuscript using your library's citations and highlights, open + mdbase writer with the same collection. +

+

+ Reader works with collections hosted by mdbase Connect and with collections + that stay on your computer and are served by the{" "} + Connector. For a computer-backed collection, + keep its desktop app running and the computer available. +

+ +

Highlight to Markdown

+ +

+ See also the recordings for views and panes + and web capture. +

+ +

Open a collection

+
    +
  1. Open reader.mdbase.dev and connect a collection.
  2. +
  3. + Sign in to mdbase Connect. If you do not have an account,{" "} + sign up first. +
  4. +
  5. Choose the collection you want to use as your library.
  6. +
  7. Review and approve Reader's access to that collection.
  8. +
+

+ You can use an existing collection or a new, empty one. Reader keeps its + records in their own folders, so it can share a collection with notes from + other apps. +

+ +

Reader setup

+

+ The first time you open a collection, Reader may show{" "} + Set up this reading collection. It lists the exact + changes it needs: the source and annotation contracts and two starter + types, reader-source and reader-annotation. Nothing + else in the collection changes. Choose Apply reviewed setup{" "} + to add them. +

+

+ You can adapt the starter types later, for example in the{" "} + mdbase Editor. Reader reads your records + through its contracts, so your types can use their own field names. +

+ +

The workspace

+ + + + + + + + + + +
AreaWhat it holds
Sources sidebarFind a source by title, author or tag, and see open and recent sources.
LibraryEvery source as a table or cards, with filters and saved views.
DocumentsEach open source in a tab, showing its document, literature note, annotations or citation.
Side panelAnnotations and details for the source you are reading.
+

+ Tabs and panels can be moved and split. Drag a tab onto another tab strip + to move it, or onto the edge of a pane to split. A tab's context menu and + the pane's ⋯ menu offer the same actions without + dragging. Your arrangement is saved per collection, and{" "} + Reset pane arrangement in the command palette restores the + default without closing tabs. On a phone, one pane is shown at a time. +

+

+ Press Ctrl K (⌘ K on a Mac) + to search sources and run any command. +

+ +

Try the preview

+

+ Open the interface preview + to explore sample sources. It runs in memory and creates no collection + records or files. Changes to the sample library are not kept. +

+ +

Switch collections

+

+ Open the collection-name menu in the header to choose another connected + collection or connect a new one. Review any pending saves before switching. + If a collection is unavailable, follow Reader's reconnect or access-review prompt. +

+

+ See Support to report bugs or request help. + Remove private notes, collection paths and credentials from reports. +

+
diff --git a/src/pages/apps/reader/library/index.astro b/src/pages/apps/reader/library/index.astro new file mode 100644 index 0000000..416d8d7 --- /dev/null +++ b/src/pages/apps/reader/library/index.astro @@ -0,0 +1,101 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +import DocVideo from "../../../../components/DocVideo.astro"; +--- + + +

Views and panes

+ + +

Open the library

+

+ Run Open library from the command palette. The library opens + as a tab, so it can sit beside a document. Show sources as a table or as + cards, and choose which columns the table shows. Any frontmatter field can + be a column. +

+

+ For a quick lookup without opening the library, use the Sources sidebar. + Press Ctrl Shift F, or / when + you are not typing, and search by title, author or tag. +

+ +

Lenses

+

Lenses are ready-made starting points:

+
    +
  • All sources, Recently opened, Currently reading and Unread inbox
  • +
  • Has annotations and Missing citation data
  • +
  • PDF, EPUB and Saved web pages
  • +
+ +

Filter and sort

+

+ Filter by reading status, format, tag, or conditions on any frontmatter + field. Every condition must match. Sort by when a source was saved or last + opened, title, creator, publication date, status, or any field. +

+ + +

The search box has three scopes:

+ + + + + + + + + +
ScopeSearches
LibrarySource details such as title, authors and tags.
Notes & annotationsLiterature notes and annotation text. It does not search inside PDFs, EPUBs or saved pages.
Open documentsText from loaded, supported document renderers. Unopened and suspended documents are not included, even if a suspended source still has a tab.
+ +

Reading status

+

+ Each source can have a reading status: inbox, queued, reading, finished, + archived or abandoned. It is stored in the source record, so other apps can + see it too. +

+ +

Edit several sources

+

+ Select sources in the table to set their reading status together, or to + set any field to a value. Leaving the value empty removes that field from + every selected source. You can also export a bibliography of just the + selected sources. Esc clears the selection. +

+ +

Saved views

+

+ When a filtered, sorted arrangement is worth keeping, save it as a new view. + Views are ordinary collection records implementing mdbase.view, + normally in the views folder. The filter, sort and presentation + can be reopened on another device or used by other mdbase tools. Choose + Save view to update an editable Reader-owned view; save a new + view to keep a separate arrangement. +

+ +

All annotations

+

+ Choose Annotations beside Sources in a + library view to browse annotations across sources. Search by + passage, note, source or tag, filter by type or tag, and sort by source or + creation date. This arrangement can be saved as an annotation view too. + The annotation text loads separately from the source list; wait for loading + to finish before treating a search result or count as complete. +

+
diff --git a/src/pages/apps/reader/notes/index.astro b/src/pages/apps/reader/notes/index.astro new file mode 100644 index 0000000..7f579b9 --- /dev/null +++ b/src/pages/apps/reader/notes/index.astro @@ -0,0 +1,113 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +import CodeBlock from "../../../../components/CodeBlock.astro"; + +const note = `Argues that close reading scales poorly to large corpora. [@morettiDistant13] + +![[annotations/ann_6f1c…]] + +My disagreement: the sampling problem cuts both ways.`; +--- + + +

The literature note

+

+ Every source has a literature note: the Markdown body of its source record. + Open it with Open literature note, or switch the source's + tab to its note view. Reader never rewrites the note from your annotations. + It contains only what you write or insert. +

+ +

Insert annotations and citations

+

+ Use the insert menu in the note editor, or Add to literature note{" "} + on an annotation: +

+
    +
  • + Annotations are inserted as embeds, so the note shows the + current text of the highlight or comment. If you edit the annotation, the + note shows the change. +
  • +
  • + Citation inserts a Pandoc-style citation such as{" "} + [@morettiDistant13] at the cursor. It needs the source to + have citation data with a citation key. +
  • +
+ + +

Saving and conflicts

+

+ The note saves automatically about a second after you stop typing. The same + note can be open in several panes. They share its text but keep their own + cursor, scroll position and undo history. +

+

+ If a save fails, keep the note open and follow the retry prompt. Reader keeps + recoverable source-note drafts in this browser, scoped to the collection + and source. On reopening, review any recovered draft before saving it. + Browser storage is best-effort and can be cleared; it is not a collection backup. +

+

+ If the note changed in the collection while you were editing, Reader shows + This note changed in the collection and lets you compare + your draft with the collection version. Check the save status before closing. + Annotation comments use a different, memory-only buffer. +

+ +

Citation data

+

+ Citation data is optional. When you add it, it is stored as one CSL-JSON + item in the source record's csl field, the format used by + Zotero, Pandoc and most citation tools. Its id is the citation + key, which must be unique in the collection. +

+

+ Open Open citation data to edit it as a form or as raw + CSL-JSON. Every official CSL field is kept exactly as entered, and less + common fields can be added from the form. Problems are marked before you + save. The Missing citation data lens in the library lists + sources without it. The same source metadata and citekey are available in + mdbase writer when you open the same collection. +

+ +

Find citation details

+

+ Find citation details searches by DOI, ISBN, URL or title. + Reader shows the details it found for you to review before saving. DOIs are + looked up in the DOI registry. Everything else goes to Wikimedia's Citoid + service. Only the identifier, address or title you searched for is sent; + the result is not saved until you approve it. +

+ +

Export

+ + + + + + + + + + + + + + +
CommandProduces
Export bibliographyreferences.json, a CSL-JSON file of every source with citation data. You can also export only the sources selected in the library.
Export current sourceA zip file with the source and annotation records as they are stored, a single Markdown file combining the note and its annotations, the source's references, and the original files. An EXPORT-REPORT.md lists anything that could not be included.
+
diff --git a/src/pages/apps/reader/shortcuts/index.astro b/src/pages/apps/reader/shortcuts/index.astro new file mode 100644 index 0000000..664e12f --- /dev/null +++ b/src/pages/apps/reader/shortcuts/index.astro @@ -0,0 +1,92 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; + +const shortcuts = [ + ["Ctrl K", "Search sources and run commands"], + ["Ctrl Shift F", "Filter sources in the sidebar"], + ["/", "Search the library view, or the sidebar elsewhere"], + ["H", "Highlight the selected text"], + ["C", "Comment on the selected text"], + ["Esc", "Dismiss the selection toolbar, or leave reading mode"], + ["Ctrl .", "Enter or leave reading mode"], + ["Ctrl \\", "Show or hide the left sidebar"], + ["Ctrl Shift \\", "Show or hide the right sidebar"], + ["Ctrl Tab / Ctrl Shift Tab", "Next or previous tab"], + ["Ctrl Shift T", "Reopen the last closed tab"], + ["Alt ← / Alt →", "Back or forward"], + ["F6", "Move focus to the next pane"], + ["Ctrl Enter", "Open the selected sidebar source beside the current one"] +]; + +const display = [ + ["Text size", "Larger or smaller text for EPUBs and saved web pages."], + ["Typeface", "Serif or sans serif."], + ["Line length", "Narrow or standard."], + ["Density", "Comfortable or compact lists and tables."], + ["Sidebar while reading", "Keep the sidebar open or hide it while a document is focused."], + ["Selecting text", "Show actions, or highlight at once."] +]; +--- + + +

Keyboard shortcuts

+

+ On a Mac, use ⌘ in place of Ctrl, except for{" "} + Ctrl Tab. Single-key shortcuts such as{" "} + H and / do nothing while you are typing in a field. +

+ + + + + + {shortcuts.map(([keys, action]) => ( + + ))} + +
KeysAction
{keys}{action}
+

+ For the browser extension's shortcuts, see{" "} + Browser extension. + Chrome can change extension shortcuts at chrome://extensions/shortcuts. +

+ +

Command palette

+

+ Ctrl K opens the command palette. Type part of a + source title or author to open it, or a command name to run it. Commands + include opening the library, adding or importing sources, switching between + open tabs, opening a source's note, annotations or citation, arranging and + resetting panes, exporting, bookmarking and changing the theme. The + Reset pane arrangement command keeps your tabs open; + Import a library… opens the import flow in a new tab. +

+ +

Text and display

+

+ Open Text and display in the header. These settings are + stored in this browser. Light, dark and system themes are also available + from the command palette. Reading preferences change reflowable documents; + PDFs retain their page layout. +

+ + + + + + {display.map(([name, effect]) => ( + + ))} + +
SettingEffect
{name}{effect}
+
diff --git a/src/pages/apps/reader/sources/index.astro b/src/pages/apps/reader/sources/index.astro new file mode 100644 index 0000000..f27613b --- /dev/null +++ b/src/pages/apps/reader/sources/index.astro @@ -0,0 +1,86 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +--- + + +

+ Choose Add source in the library, or run{" "} + Add source… from the command palette. You can add a file + or paste a link or identifier. +

+ +

Add a file

+

+ Choose a PDF, an EPUB, or a web page you saved earlier. You can also drop a + file onto Reader. The original file is stored unchanged in your collection, + and Reader creates a source record that points to it. +

+ + +

+ Paste a web address, DOI, arXiv ID, ISBN or PubMed ID into{" "} + Link or identifier and choose Add. + Reader shows each step as it works: fetching the page, looking up citation + details, or looking for an open-access PDF. +

+ +

Web pages

+

+ For a web address, Reader fetches the page and saves a readable copy. If + the address points to a PDF, it saves the PDF instead. When the page cannot + be fetched, Reader may find citation details and offer to save a citation-only + source. Review that offer before accepting it. +

+

+ Pages behind a sign-in or paywall cannot be fetched this way. Use the{" "} + browser extension, which saves the page + you are already viewing, or save the page yourself and add the file. +

+ +

Identifiers

+

+ For a DOI, arXiv ID, ISBN or PubMed ID, Reader first looks up the citation + details. For a DOI or arXiv ID, it then looks for a freely available PDF. + It tries direct PDF links first, then open-access article pages. Paywalled + and blocked sites are skipped. +

+

+ If no PDF is found, you can keep a source with citation details and attach + a file later. Review any follow-up message: Open source + opens a source already added, while a citation-only offer needs your approval. + If citation data could not be stored, Reader says so; use the source's + citation tab to review or add it. +

+ +

What is sent where

+ + + + + + + + + + + +
StepServiceWhat is sent
Fetching a web page or PDFReader's capture serviceThe address to fetch. Private and local network addresses are refused.
DOI and arXiv citationsThe DOI registry (doi.org)The DOI only.
Other citations: ISBN, PubMed, URL or titleWikimedia CitoidThe identifier, address or title only.
Open-access search for a DOIOpenAlex, and Unpaywall where enabledThe DOI only. PDFs it finds are then fetched by Reader's capture service.
Open-access search for an arXiv IDarXiv, through Reader's capture serviceThe arXiv ID only.
+

+ Citation lookups send the identifier, address or search text needed for that + lookup, not your notes or annotations. Fetching a page sends its address; + check that it does not contain a private access token. See the + privacy policy for data handling. +

+
diff --git a/src/pages/apps/writer/chapters/index.astro b/src/pages/apps/writer/chapters/index.astro new file mode 100644 index 0000000..6462822 --- /dev/null +++ b/src/pages/apps/writer/chapters/index.astro @@ -0,0 +1,85 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +import CodeBlock from "../../../../components/CodeBlock.astro"; +import DocVideo from "../../../../components/DocVideo.astro"; + +const chapters = `![[chapters/introduction]] + +![[chapters/results]] + +![[chapters/discussion]]`; +--- + + +

Building a book

+ + +

Include a chapter

+

+ Put an embed on a line of its own in the main manuscript. Writer includes + the referenced record at that point in the document. The chapter is an + ordinary Markdown record; it needs no special manuscript type. +

+ +

+ Use paths belonging to records in your collection. Writer resolves record + links in the collection; an unresolved link appears in Problems. A main + manuscript can also contain its own prose around the chapter embeds. +

+ + +

+ Outline lists the included chapters in document order. Open one there or + from its card in the editor. Cards show the chapter title, word count and + problems. Each chapter has its own editing session and autosave status. +

+

+ Drag a chapter in Outline, or use Alt ↑ and + Alt ↓, to reorder its embed in the main manuscript. + Outline can also add a new chapter at the end. Review the preview after + changing the order. +

+ +

References across chapters

+

+ Writer assembles the complete manuscript before formatting citations and + references. Footnotes, citations and labels can work across records. + Choose unique figure, section and equation labels throughout the manuscript + so a reference has one intended target. +

+

+ Images belong to the collection, and relative image paths are resolved from + the chapter containing them. A chapter's file links should still work when + it is included from another record. +

+ +

Embed a Reader annotation

+

+ An embed referring to a Reader annotation uses its quotation and available + source citation. For example, ![[annotations/your-highlight]] + {" "}can include a saved passage. Keep the source's CSL metadata and citekey + available in the same collection, and verify the quoted text and locator. +

+ +

Missing and recursive embeds

+

+ Missing records and cyclic includes appear in Problems. A cycle happens + when a manuscript eventually embeds itself, directly or through another + record. Remove the recursive embed or move shared prose into a separate + record. Check all included chapters and images before + exporting. +

+
diff --git a/src/pages/apps/writer/citations/index.astro b/src/pages/apps/writer/citations/index.astro new file mode 100644 index 0000000..858a3f5 --- /dev/null +++ b/src/pages/apps/writer/citations/index.astro @@ -0,0 +1,88 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +import CodeBlock from "../../../../components/CodeBlock.astro"; + +const citations = `A parenthetical citation [@smith2024]. + +A specific passage [@smith2024, p. 12]. + +Several sources [@smith2024; @jones2023].`; +--- + + +

Use your Reader library

+

+ Connect Writer to the collection containing your + Reader library. Each citable source has CSL-JSON + citation metadata with a citekey in csl.id. The manuscript uses + that citekey; the source record and its metadata remain in the collection. +

+

+ Add sources and review their citation data in + Reader's citation editor. + Citekeys must be unique. If a source has no citation data, add it before + trying to cite the source in Writer. +

+ +

Insert a citation

+
    +
  1. Open Sources, or press Ctrl Shift E (⌘ on a Mac).
  2. +
  3. Search by author, title, year or citekey.
  4. +
  5. Select a source, add a page number if needed, and insert its citation.
  6. +
+

+ Sources already cited in the manuscript appear before the rest of the + library. The panel marks the source under your cursor and can step through + its citations. You can also type @ or [@ in the + editor to open source completion suggestions. +

+ +

Replace the example citekeys with your own. Hover a citation to inspect its bibliography entry.

+ +

Reader quotations

+

+ Sources can show quotations from Reader annotations. Insert one to bring + the highlighted text into the manuscript with its citation and available + page information. Check the quotation and page against the original source + before using it in submitted work. +

+

+ Embedding an annotation record + is another way to use a quotation. Quoted text and commentary stay separate; + commentary is not part of the source's words. +

+ +

Style and language

+

+ Choose Citation style and Language in + Manuscript settings. Six citation styles and eight locales are bundled. + A .csl file in the collection can provide another citation style. + The selected style controls author/date or note-style formatting, bibliography + entries and repeated citations across all included chapters. +

+

+ Preview and Word export use the same citation metadata and style, with + separate rendering engines. Review both the citation text and bibliography + in the final export. +

+ +

Missing or unknown sources

+

+ An unknown citekey appears in Problems and may offer a similar known key. + Check its spelling, the selected collection and the source's citation data. + Editing a source citekey can leave older manuscript citations unresolved; + update those references as well. +

+
diff --git a/src/pages/apps/writer/data/index.astro b/src/pages/apps/writer/data/index.astro new file mode 100644 index 0000000..ee38a18 --- /dev/null +++ b/src/pages/apps/writer/data/index.astro @@ -0,0 +1,94 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +import CodeBlock from "../../../../components/CodeBlock.astro"; + +const manuscript = `--- +type: writer-manuscript +title: A research paper +authors: + - name: Alex Researcher + affiliation: Example University +template: article +csl: chicago-notes-bibliography +lang: en-GB +--- +# Introduction + +A claim supported by a source [@smith2024, p. 12].`; +--- + + +

Manuscript records

+

+ A manuscript is a Markdown record implementing{" "} + dev.mdbase.writer.manuscript. Its body is your writing; + frontmatter holds its title, authors, abstract, layout, citation style and + language. The following example uses the starter type's field names: +

+ +

+ Chapters remain separate Markdown records. Reader sources hold their own + citation data and annotations, and comments use collection records too. + Other mdbase tools can read these records. Writer does not delete or rename them. +

+ +

Autosave

+

+ Manuscript text, chapter text and settings save through Connect record + sessions. Each included record has its own session. Watch the save status + and wait for changes to be saved before closing, reloading or changing + collections. +

+

+ A failed save displays Not saved. The editor keeps the + current text, and saving retries when you type. Keep the tab open while + restoring access. If saving still fails, copy your writing somewhere safe + before reloading; do not assume an unsent edit is in the collection. +

+ +

Conflicts

+

+ If a record changes elsewhere while you are editing it, Writer reports the + conflict. Keep mine retains your version; + Use theirs adopts the collection version. Check both + versions and copy any text you need before choosing. A conflict in one + chapter belongs to that chapter's record. +

+ +

Browser state and demo

+

+ Layout, zoom, theme and the last-used export format are remembered in the + browser. These preferences are separate from the manuscript settings stored + in the collection. Browser state is not a backup of your writing. +

+

+ The demo collection + runs in memory. It is separate from the collections you authorise through + Connect. Do not use it for work you need to keep. +

+ +

Access and availability

+

+ You approve Writer's collection access through Connect. A computer-backed + collection needs its Connector and computer available. If a collection + cannot be opened, follow the reconnect prompt, check its availability or + choose another collection. +

+

+ When reporting a problem, include the browser, steps to reproduce and the + displayed error. Remove private manuscript text, paths and credentials. + Keep your collection backups independent of the browser. +

+
diff --git a/src/pages/apps/writer/export/index.astro b/src/pages/apps/writer/export/index.astro new file mode 100644 index 0000000..a256d03 --- /dev/null +++ b/src/pages/apps/writer/export/index.astro @@ -0,0 +1,86 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +--- + + +

Manuscript settings

+

+ Open Settings to edit title, subtitle, authors, abstract, + date, citation style, layout and language. Enter authors one per line; + Name; Affiliation adds an affiliation. Settings update the + manuscript's frontmatter and preview as you edit. +

+

+ A problem with a setting appears beside that field. Check the save status + after changing settings, just as you do after writing body text. +

+ +

Layouts

+

+ The bundled Article and Thesis layouts + typeset the manuscript with appropriate title and document structure. + You can also choose a Typst template stored as a .typ file in + the collection. Custom layouts require Typst knowledge and a template + function accepting title, subtitle, authors, abstract, date and body. +

+

+ A collection template is one file: files it imports are not loaded. + Its layout affects the PDF. A manuscript's citation style can be a bundled + style or a .csl file in the collection. +

+ +

Export formats

+

+ Open the menu beside Export to choose a format. The main + button repeats the format you used last in this browser. You can also open + the export menu with Ctrl Shift S + (⌘ on macOS). +

+ + + + + + + +
FormatContents
PDFThe typeset manuscript using the selected layout. It is available once the manuscript typesets.
Word (DOCX)An editable document with formatted citations, resolved cross-references and Word document styles.
Pandoc bundle (ZIP)Materialised Markdown, references, citation style and supporting files for building with Pandoc or Quarto. Follow the bundle's README.
+

+ Exports are generated in the browser. The first Word export downloads about + 16 MB of additional software. Keep the tab open while it prepares the file. + A slow first export does not mean the manuscript needs to be reopened. +

+ +

PDF and Word differences

+

+ PDF uses the Typst layout; Word uses its own styles and Pandoc's document + structure. Word is not a page-for-page copy of the preview. A custom Typst + layout uses article styles for Word output. The bundled article and thesis + layouts have their corresponding Word reference styles. +

+

+ The Word export resolves cross-reference numbers before conversion. A + Pandoc/Quarto bundle leaves cross-references for the external build tools; + follow its instructions for Quarto or an appropriate Pandoc filter. +

+ +

Check before sharing

+

+ Review Problems and export warnings, then inspect the downloaded file. + Missing records or images can leave output incomplete. Check page breaks, + figures, cross-references, citations and bibliography before submitting or + sharing. The bundle includes manuscript text and supporting content; + treat it as private research material. +

+
diff --git a/src/pages/apps/writer/index.astro b/src/pages/apps/writer/index.astro new file mode 100644 index 0000000..a4ec368 --- /dev/null +++ b/src/pages/apps/writer/index.astro @@ -0,0 +1,97 @@ +--- +import DocsLayout from "../../../components/DocsLayout.astro"; +import DocVideo from "../../../components/DocVideo.astro"; +--- + + +
+ Writer is prerelease software. +

+ Keep backups of important work and check the save status before closing or + reloading. The demo + uses an in-memory sample collection. Demo changes are not kept and do not + change your own collection. +

+
+

+ mdbase writer is for papers, theses and books. Your manuscript stays a + Markdown record in your collection. Writer typesets it as you write and + uses your Reader library for citations and quotations. + The bundled layouts need no Typst or LaTeX knowledge. +

+ +

Writing a paper

+ +

+ The recordings use a separate Darwin-themed sample library. The live demo + may contain different sample manuscripts. See also + building a book from chapters. +

+ +

Connect a collection

+
    +
  1. Open writer.mdbase.dev and choose Connect a collection.
  2. +
  3. Sign in through mdbase Connect, choose a collection and review Writer's requested access.
  4. +
  5. Choose the same collection as your Reader library to cite its sources.
  6. +
  7. If prompted, review the proposed manuscript and source types, then choose Set up collection.
  8. +
+

+ Setup adds the contracts and types Writer needs after your approval. It does + not rewrite existing manuscript text. Collections can be hosted by Connect + or served from your computer by the Connector. For a computer-backed + collection, keep the desktop app running and the computer available. +

+ +

Create a manuscript

+
    +
  1. Choose New manuscript.
  2. +
  3. Enter a title and choose an article or thesis layout and a citation style.
  4. +
  5. Choose Create manuscript and begin writing.
  6. +
+

+ To use an existing note, choose Use a note you already have… + in the same dialog. Select the note and choose Use as manuscript. + This adds the manuscript type while retaining its other types, text and location. +

+ +

The workspace

+ + + + + + + + + + + +
AreaWhat it does
EditorWrite Markdown. Edits autosave through Connect.
PreviewSee the typeset pages. Click a block to return to its text.
OutlineNavigate headings, open chapters and reorder chapter embeds.
SourcesFind library sources, insert citations and use Reader quotations.
CommentsReview comments, replies and suggested edits.
SettingsEdit the title, authors, abstract, date, language, citation style and layout.
ProblemsFind citation, cross-reference, image, embed and typesetting problems.
+

+ Settings changes update the manuscript's frontmatter and preview. Your + sidebar, editor/preview arrangement and zoom are remembered in this browser. + On a phone, switch between writing, preview and outline views. +

+ +

Next steps

+ +
diff --git a/src/pages/apps/writer/writing/index.astro b/src/pages/apps/writer/writing/index.astro new file mode 100644 index 0000000..ad4d028 --- /dev/null +++ b/src/pages/apps/writer/writing/index.astro @@ -0,0 +1,108 @@ +--- +import DocsLayout from "../../../../components/DocsLayout.astro"; +import CodeBlock from "../../../../components/CodeBlock.astro"; + +const example = `# Introduction {#sec-introduction} + +A claim supported by a source [@smith2024, p. 12]. + +See @sec-introduction and @fig-results. + +![Results](figures/results.png){#fig-results} + +A sentence with a footnote.[^note] + +[^note]: A longer explanation.`; +const shortcuts = [ + ["Ctrl K", "Open the command palette"], + ["Ctrl Shift F", "Search across the manuscript's records"], + ["Ctrl Shift E", "Find a source"], + ["Ctrl ,", "Open manuscript settings"], + ["Ctrl \\", "Show or hide the sidebar"], + ["Ctrl Shift \\", "Cycle the editor/preview arrangement"], + ["Ctrl Alt M", "Comment on a selection"], + ["Ctrl Alt S", "Suggest an edit to a selection"], + ["Ctrl Shift S", "Choose an export format"], + ["F8 / Shift F8", "Next or previous problem"] +]; +--- + + +

Markdown and labels

+

+ Use Markdown headings, emphasis, lists, tables, links and footnotes. Writer + also understands Pandoc citations and Quarto-style labels and references. + This example uses a library citekey and an image stored in the collection: +

+ +

+ Replace smith2024 with one of your source citekeys and the image + path with your own file. Labels such as {"{#sec-introduction}"} + {" "}identify headings; @sec-introduction refers to them. Figures + use a fig- label. Keep labels unique across the whole manuscript. +

+

+ Image paths are resolved relative to the record containing them. Store the + image in the collection where Writer can read it. Missing files appear in + Problems and can affect the downloaded document. +

+ +

Preview and navigation

+

+ The preview typesets your manuscript as you type and follows the cursor's + block. Click a preview block to jump to its Markdown. Hover over a citation + to see its bibliography entry, or a cross-reference to see its target. + Ctrl-click (⌘-click on a Mac) follows a reference. +

+

+ Outline lists your sections and chapters with word counts. Manuscript + search can find text across the included records and open the result in its + editor. The command palette is another way to find sources or run commands. +

+ +

Problems

+

+ Writer marks unknown citekeys, missing labels, unavailable records and + images, footnote problems, malformed LaTeX and typesetting errors at the + affected Markdown line. Open Problems to jump to one. F8 moves + to the next problem; Shift F8 moves back. +

+

+ Unknown citekeys and labels can offer close matches. Problems with a + manuscript setting take you to that field in Settings. Check Problems + before exporting and review the downloaded document. +

+ +

Comments and suggestions

+

+ Select text and choose a comment or suggested edit from the selection + toolbar or command palette. Open Comments to read threads and reply. + A suggested edit includes replacement text; accepting it changes the + record through its editing session. Review the replacement before accepting. +

+

+ Comments are collection records anchored to quoted text. Writer looks for + the quote again after edits and reports a detached comment when it cannot + find a reliable target. A comment can be signed with your collection person + record when identity access is available; otherwise it is unsigned. +

+ +

Shortcuts

+

On macOS, use ⌘ in place of Ctrl. Writer's keyboard-shortcuts dialog lists the available commands.

+ + + {shortcuts.map(([keys, action]) => )} +
KeysAction
{keys}{action}
+
diff --git a/src/styles/global.css b/src/styles/global.css index 934e1b1..88be608 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -284,6 +284,7 @@ main[tabindex="-1"]:focus { outline: none; } .docs-article ul, .docs-article ol { padding-left: 1.2rem; } .docs-article li + li { margin-top: 0.3rem; } .docs-article :not(pre) > code { padding: 0; color: var(--ink); background: transparent; border: 0; } +.docs-article kbd { padding: 0.05rem 0.3rem; color: var(--ink); font: 400 0.78em/1.4 var(--mono); border: 1px solid var(--line); border-radius: 2px; white-space: nowrap; } .docs-toc { padding-left: 1rem; border-left: 1px solid var(--ink); } .docs-toc > strong { color: var(--ink); } .docs-toc nav { margin-top: 0.5rem; }