Skip to content

debate/cardmirror

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,016 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CardMirror

CardMirror

CardMirror is a debate text editor focused on high school and college policy debate. It's a standalone replacement for the editor side of Verbatim, the Microsoft Word add-in most US debate teams use: same organizational structure, same F-key shortcuts, same send-to-speech workflow, same Word-compatible .docx files — but with no Word, no macros, and no add-in. That keeps it fast on the multi-megabyte evidence files debaters work in, and enables cross-platform feature parity.

Run it as a desktop app for tournament-day work, or as a web preview in any browser for trying it out or working from a Chromebook or locked-down school machine.

▶ Try the live web preview: https://ant981228.github.io/cardmirror/

🚧 Beta preview. Expect rough edges, missing features, and the occasional bug. Keep a Verbatim copy of anything important — not recommended for tournament-day use yet.

Desktop builds are unsigned, so Windows and macOS warn you the first time you launch. The one-time workaround for each is in the install steps below.

See MANUAL.md for the full user manual; CHANGELOG.md for release notes; ARCHITECTURE.md for the full design; PROJECT.md for project orientation.

Install

Desktop builds live on the Releases page. Pick the file for your operating system, run the installer, and launch CardMirror like any other app.

macOS

  1. Download the .dmg for your Mac:

    • Apple Silicon (M1 / M2 / M3 / M4): CardMirror-x.x.x-arm64.dmg.
    • Intel: CardMirror-x.x.x.dmg (or -x64.dmg).
  2. Open the .dmg, drag CardMirror to your Applications folder.

  3. First launch only. Gatekeeper refuses to open unsigned apps. You'll see one of two messages depending on your macOS version:

    • "can't be opened because Apple cannot check it for malicious software" → Open Finder → Applications, right-click (or Control-click) CardMirrorOpen. Click Open in the confirmation dialog. From then on, normal double-click works.

    • "is damaged and can't be opened" → macOS has hard-quarantined the app and the right-click workaround doesn't suffice. Open a terminal (Spotlight → "Terminal") and run:

      sudo xattr -cr /Applications/CardMirror.app

      You'll be asked for your password. After that, normal double-click works.

Windows

  1. Download CardMirror Setup x.x.x.exe.
  2. Run the installer (Next → Install → Finish).
  3. First launch only. SmartScreen shows "Windows protected your PC." Click More infoRun anyway.

Linux

Two options for installation, depending upon distribution:

  • AppImage (works on every modern distro): download cardmirror-x.x.x.AppImage. In a terminal, in the download folder, run:

    chmod +x cardmirror-x.x.x.AppImage
    ./cardmirror-x.x.x.AppImage

    For most graphical desktop environments with modern file managers, you should be able to run the AppImage by double-clicking after the chmod +x command.

  • Arch and Arch-based distributions — use the AUR:

    yay -S cardmirror-bin
    # or with paru: paru -S cardmirror-bin

    Or grab cardmirror-x.x.x.pacman from the release directly and:

    sudo pacman -U cardmirror-x.x.x.pacman

Web app (Chromebook & browser)

No download needed — open the live web app in a Chromium browser (Chrome, Edge, or ChromeOS) and click the Install button in the address bar to run CardMirror in its own window, offline, updating itself on relaunch. On a Chromebook this is the way to run it — no desktop build required.

Installed, the web app does most of what the desktop editions do: save in place and autosave (it may ask for permission to edit each file), one-keystroke Paste Text, multiple windows (New Document / New Speech Document open their own window) with the dropzone, Quick Cards, and send-to-speech kept in sync across them, the three-pane workspace, and single-file Clean / Convert / Compress tools on the home screen.

Still desktop-only: background file-library search, folder-wide bulk operations, Send to Verbatim Flow, voice control, and card sharing. Firefox and Safari run the editor but lack the File System Access API, so there Save falls back to a download. See Desktop vs. web in the manual for the full breakdown.

Web feels slow on big documents? Check your extensions. Some browser extensions — password managers (1Password, LastPass, Bitwarden) and form fillers especially — rescan the whole page every time you focus a text box, which can freeze the editor for a second or more on a large file. The desktop app never hits this because it loads no extensions. To confirm, open the app in an Incognito window (extensions are off there) — if it's snappy, an extension is the cause. To fix it without turning the extension off everywhere, tell Chrome to run it only on this site by request: click the puzzle-piece (Extensions) icon in the toolbar, click the next to the extension, and set "This can read and change site data" → "When you click the extension."

Updates

Help → Check for Updates… checks manually: it tells you you're current, or offers an available update (downloaded in the background, with a "restart now?" prompt when it's ready).

Auto-check on launch is off by default. Turn it on in Settings → General → "About this install" → Check for updates on launch.

macOS: automatic updates don't work yet. The in-app updater can tell you a new version exists, but it can't install it on Mac. To update, download the latest .dmg from the releases page and reinstall it (drag CardMirror to Applications, replacing the old copy) each time — same steps as the macOS install above. Windows and Linux are unaffected.

Linux users who installed via the AUR can update with yay -Syu instead.

(Optional) Set up AI features

A few features call out to Anthropic's Claude API:

  • AI-formatted citations from a pasted URL or freeform quote.
  • AI repair of OCR / PDF extraction errors in a selection (Mod-Shift-R).
  • AI image alt-text and table-from-image (right-click an image).
  • AI commenting / explain features in the comments column.

To enable them:

  1. Get an API key from console.anthropic.com (you'll need to top up a small amount of credit — Anthropic doesn't have a free tier for the API).
  2. In CardMirror, click the ⚙ gear icon in the ribbon.
  3. Toggle AI features on and paste your API key into the Anthropic API key field.

The key is stored locally on your machine and is sent directly to Anthropic when you trigger an AI feature. It doesn't travel through a third-party server.

Translation (Mod-Shift-T on a selection → copied to the clipboard) also has an Anthropic backend, but it works without any AI setup: the default MyMemory backend needs no key. You can also plug in a Google Cloud Translation key. Configure it under Settings → Editing → Translation.

Run from source

You only need this if you want to build CardMirror yourself (contribute, run a development branch, or use the editor on a platform we don't publish binaries for). For day-to-day use, download a release above.

1. Install Node.js

CardMirror is built with JavaScript / TypeScript and needs Node.js to run. Node is a regular desktop installer.

  • macOS — open nodejs.org in your browser and click the blue "LTS" download button. Open the .pkg file from Downloads and click through the installer.
  • Windows — open nodejs.org and click the blue "LTS" download button. Open the .msi file from Downloads and click through the installer.
  • Linux — the easiest path is the official installer at nodejs.org/en/download — pick your distro and follow the few commands it shows.

You don't need to verify the install — if the next step works, Node is installed.

2. Download the source

  1. Open the CardMirror page on GitHub in your browser.
  2. Click the green <> Code button near the top of the file list.
  3. Click "Download ZIP" at the bottom of the dropdown.
  4. Unzip the download. You'll get a folder called cardmirror-main. Move it somewhere you can find later — your Desktop or Documents is fine.
  5. Open the cardmirror-main folder and look inside. Some unzippers double-wrap. You want the folder that directly contains package.json, README.md, index.html, and src/ — if you only see another cardmirror-main folder, that's the wrapper; open it.

3. Open a terminal inside that folder

A "terminal" is a window where you type commands. You're going to open one already pointing at the CardMirror folder.

  • macOS — enable Finder → right-click → Services → New Terminal at Folder once via System Settings → Keyboard → Keyboard Shortcuts → Services → Files and Folders, then right-click the folder.
  • Windows — open File Explorer in the folder, click the address bar, type cmd, press Enter.
  • Linux — right-click inside the folder and pick Open Terminal Here (Nautilus / Dolphin / Thunar all offer it), or open in terminal and cd to the filepath.

To make sure you're in the right directory, type ls (macOS / Linux) or dir (Windows) and press Enter. You should see package.json, README.md, src, apps. If you don't, your terminal is likely one folder too high up — verify your current folder with the pwd command (macOS / Linux / Windows PowerShell) or by typing echo %cd% (Windows, non-PowerShell).

4. Install dependencies

npm install

Downloads everything CardMirror needs. Takes 30 seconds to a couple of minutes. Deprecation warnings are normal; only red error lines indicate trouble.

5. Run the web edition

npm run dev

After a few seconds, open http://localhost:5173/ in your browser.

Run the desktop edition (from source)

npm run desktop:dev

This builds the Electron main process, starts the Vite dev server, and launches the desktop window. Same code, same renderer — but in a native window with file-system access.

Coming back later

Open a terminal in the same folder; run npm run dev (or npm run desktop:dev) again. To pick up newer code, download a fresh ZIP and rerun npm install in it.

Other commands

These all run inside the terminal pointed at the CardMirror folder (same setup as the install steps above):

npm test            # run all tests
npm run test:bench  # performance benchmarks
npm run typecheck   # strict TypeScript check

Testing round-trip against your own .docx files

The round-trip test suite and the round-trip benchmark both walk a folder of .docx fixtures and run universal preservation checks on each one (text length, heading IDs, mark counts, indent / spacing multisets, etc.). Point them at any folder by setting CARDMIRROR_DOCS_DIR:

CARDMIRROR_DOCS_DIR="/path/to/your/docx/files" npm test
CARDMIRROR_DOCS_DIR="/path/to/your/docx/files" npm run test:bench

When the variable isn't set, the suite looks under reference-docs/example docs/ (the project owner's local corpus). When that folder doesn't exist either — the default state on a fresh clone — the file-dependent tests skip cleanly and the rest of the suite still runs.

Round-trip a docx

The CLI imports a Verbatim/Advanced-Verbatim docx, normalizes it through our schema, and re-exports a fresh docx:

npm run round-trip path/to/input.docx [path/to/output.docx]

The output is fully native to Verbatim — same canonical style ids, same direct-formatting conventions. Stylepox and other non-Verbatim cruft is dropped on import (per ARCHITECTURE.md §3).

Public API

import {
  schema,        // the ProseMirror schema
  fromDocx,      // .docx bytes → ProseMirror doc
  toDocx,        // ProseMirror doc → .docx bytes
  exportDoc,     // schema doc → { documentXml, relsXml }
  importDoc,     // document.xml → schema doc
  newHeadingId,  // generate a fresh stable heading UUID
} from 'cardmirror';

Example: read a docx, modify, write it back

import { fromDocx, toDocx } from 'cardmirror';
import { readFile, writeFile } from 'node:fs/promises';

const buf = await readFile('input.docx');
const doc = await fromDocx(buf);

// `doc` is a ProseMirror Node — walk it, transform it, edit it...
console.log(`${doc.nodeSize} chars in tree`);

const out = await toDocx(doc);
await writeFile('output.docx', out);

Schema highlights

doc:        sequence of block-level kinds
pocket:     Heading 1 paragraph (with stable id)
hat:        Heading 2 paragraph (with stable id)
block:      Heading 3 paragraph (with stable id)
card:       structured: tag (card_body | undertag | cite_paragraph | analytic | table)*
tag:        Heading 4 (only inside card)
cite_paragraph, card_body: body paragraphs inside cards
analytic:   outline-4 paragraph (Analytic style; can be standalone or in-card)
undertag:   Undertag-styled paragraph
paragraph:  unstyled body text (first-class — can sit between any nodes)
table:      table_row+ (at doc level OR inside a card / analytic_unit)
table_row:  (table_cell | table_header)+
table_cell: paragraph+
image:      inline atom (base64 bytes + EMU dimensions + alt; round-trips through .docx)

Every paragraph-like textblock carries round-trip-only attrs indent (left indent in OOXML dxa) and spacing (verbatim <w:spacing> map). Tables carry rawTblPr (table-level borders / style / shading captured opaquely); cells carry rawTcPr (per-cell borders, shading, vAlign).

Marks: cite_mark, underline_mark, underline_direct, emphasis_mark, undertag_mark, analytic_mark, plus direct formatting bold, italic, strikethrough, superscript, subscript, link, highlight, font_color, font_size, shading, pilcrow_marker, font_family, comment_range (anchors a thread to a range of text).

See src/schema/ for full specs and ARCHITECTURE.md §4 for design rationale.

Acknowledgements

CardMirror is built on ProseMirror, the modular rich-text editor framework created and maintained by Marijn Haverbeke. Nearly every editor primitive CardMirror leans on — the schema-validated transactions, the typed-tree document model, NodeViews, plugin state, the keymap and history modules — is ProseMirror's.

The idea to use ProseMirror, as well as credit for dozens of course-corrections along the way, belongs to text editor wizard Slim Lim.

If you're curious how ProseMirror works under the hood, Marijn's launch post is the best high-level introduction, and the ProseMirror docs cover the APIs in depth.

Thank you to Marijn and the ProseMirror community for the years of careful library design that made this project tractable. If ProseMirror has been useful to you too, Marijn's work is supported directly at https://marijnhaverbeke.nl/fund/.

CardMirror's voice control was shaped by ideas pioneered in the hands-free-editing community — Talon, Cursorless, and Pokey Rule.

The card sharing feature was conceived and prototyped by Q Cooper of Missouri State University; his proof-of-concept demonstrated the cross-machine send-and-receive flow the built-in version is based on. Thanks also to Shreeram Modi, whose debate-scripts for Verbatim inspired a few features here — notably the customizable acronym marking.

The Word-style image resize handles were contributed by Neo Cai. The multi-doc nav rail's per-document outline close and vertical resize were contributed by cora. The find bar's viewport clamping on narrow windows was contributed by Shreeram Modi.

The app's interface icons are from the Untitled UI free icons, used under their free license. See THIRD-PARTY-NOTICES.md for the full third-party attributions and license terms.

This project was funded by the subscribers at Debate Decoded — subscribing is the best way to support my work.

License

CardMirror is licensed under the PolyForm Noncommercial License 1.0.0. You can read, fork, modify, and share the source for any noncommercial purpose (personal use, hobby projects, debate-team and academic use, research, government use, charitable / public-interest organizations); commercial use requires a separate license. See LICENSE for the full terms.

Underlying dependencies (ProseMirror and friends) ship under their own permissive licenses, preserved in node_modules/.

About

ProseMirror-based editor for competitive policy/LD/PF debate. Lossless .docx round-trip against Advanced Verbatim.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 90.4%
  • CSS 7.1%
  • JavaScript 1.4%
  • Python 0.5%
  • HTML 0.4%
  • PowerShell 0.1%
  • Other 0.1%