flippinhutt25 downloadsDelivery-aware academic dashboard and workflow layer over a Markdown course vault. Local-first, no network calls.
A local-first, Markdown-first dashboard and workflow layer for an Obsidian academic vault — for any student, any subject. It reads and writes ordinary Markdown Properties/frontmatter, surfaces what's due and what needs attention across your courses, and helps you create the right kind of note in the right folder. Nothing about it is tied to a particular school, program, or set of courses: you define your own courses and note types in Settings, and it works from there.
It is not a replacement for Obsidian, Tasks, Dataview, Canvas LMS, or a full learning-management system — it's a layer over an existing folder-and-Markdown workflow.
status: frontmatter to complete
right from the view — the item then drops out of every due-date/status
section immediately, no need to open the note or the dashboard file.status: is in-progress, blocked, or
reviewing, regardless of due date (so a project you're chipping away
at stays visible even when its due date is months out). A brand-new
note defaults to not-started and won't appear here until you actually
start it — via the status dropdown described next, or by editing
frontmatter yourself.status: frontmatter directly
and refreshes the dashboard immediately.npm install
npm run build
Copy (or symlink) the plugin folder — manifest.json, main.js,
styles.css — into <vault>/.obsidian/plugins/course-command-center/, then
enable Course Command Center in Obsidian's Community plugins settings.
For active development, npm run dev runs an esbuild watcher that rebuilds
main.js on save.
The plugin ships with no courses configured — it makes no assumptions about your school, program, or folder naming. To set it up:
assignment, lecture, lab-report, poem-analysis — anything you
want) mapped to a vault-relative folder. A datalist offers ~30 built-in
suggestions (assignment, module, discussion, lecture, quiz, SQL lab, ERD,
rubric-tracked project deliverable, etc.) with hand-written fallback
templates, but you can type anything — a type you invent gets a generic
placeholder template instead of a tailored one.The plugin reads these frontmatter fields, all optional, tolerating unknown extra fields:
course: <your course code>
delivery: online | in-person | hybrid
type: <any type name you've configured for that course>
status: not-started | in-progress | blocked | reviewing | submitted | complete
due: YYYY-MM-DD
opens: YYYY-MM-DD
module: string
priority: low | medium | high
created: YYYY-MM-DD
points_possible: number
points_earned: number
grade: string
course values may include extra descriptive text (e.g.
CS 250 — Data Structures) — matching is by course code, not exact string
equality. type is free text: roughly 30 recognized names (see
src/constants.ts's KNOWN_ARTIFACT_TYPES) get a tailored fallback
template and, for some, a dedicated health-check rule; anything else still
works, just with a generic template and no specialized health check.
Frontmatter is only ever written through Obsidian's processFrontMatter
API, and only when you explicitly trigger a create action — never as a side
effect of viewing the dashboard.
Settings → Courses lets you add/remove courses and edit, per course: code, display name, delivery, root folder, meeting days/time/location, Canvas URL, active status, and the artifact-type → folder map described above. Global settings cover the templates folder, daily-note folder, weekly-review folder, and inbox path (all plain text — no folder-numbering convention is assumed).
Templates: when creating a note, the plugin first checks the configured
templates folder for a matching template file for a curated set of common
types (e.g. Assignment Template.md, Online Module Template.md, SQL Lab Template.md — see candidateTemplateFileName in
src/services/template-service.ts for the full mapping). If found, that
file's body is used (frontmatter is always regenerated by the plugin so the
required fields stay consistent); otherwise a built-in fallback template is
used — a tailored one for a recognized type, a generic one-section template
for a custom type. Fallback templates use placeholder section headings
only — no fabricated content, no empty [[ ]] links.
Run course health check (command palette or button in the view) is
read-only — it never edits notes. It reports, per note, one of three
severities (Info / Warning / Action needed) and every result is clickable
to open the note. See the full rule list in the "What it does" section
above; the underlying pure rule functions are in
src/services/health-check-rules.ts. The rules key off a note's type, so
they only apply to notes using one of the recognized type names (assignment,
module, discussion, sql-lab, database-design, requirement,
project-deliverable) — a fully custom type just doesn't get a dedicated
check.
An auto-generated note that gives you "what's due" as a plain file instead of only the live view — set Settings → Dashboard file path (empty by default = disabled) and it starts getting written.
The file has five sections, in this order:
Sections overlap on purpose (Past due is the exception, see above):
something due tomorrow shows up in all three of Deadlines/Upcoming/Do next,
so each section is a complete view at its own zoom level rather than a
disjoint slice. It pulls from every course's due-dated notes, not just
Canvas-synced ones — if you've been setting due: in frontmatter yourself,
those show up here too. The live Course Command Center view shows the
same combined picture in its "Due today", "Do next", "Upcoming deadlines",
and "Past due" sections — the file and the view are two renderings of the
same underlying data, not two separate things to keep in sync with each
other.
Every line renders as a checkbox, and checking either kind removes it.
Check a real note's line and the plugin sets that note's status:
frontmatter to complete. Check a not-yet-created Canvas item's line and
the plugin remembers its Canvas UID as checked-off instead (there's no note
to set status: on) — either way, the dashboard watches for the checked
box, applies the change, and regenerates the file right after, so the item
drops out rather than staying checked-but-present. A checked-off Canvas
item stays hidden across future Refreshes and re-syncs; Settings →
"Checked-off Canvas items" can clear all of them if you check one off by
mistake. Click the Canvas item's link instead of its checkbox if you'd
rather create a real note for it (see below).
It's fully regenerated (not appended to) every time it updates — clicking Refresh in the view, and after a Sync Canvas calendar run, both rewrite it. There's no background/automatic write; a manual edit to the file survives right up until the next explicit refresh or sync, at which point it's replaced. On first write, if a file already exists at the configured path and it doesn't contain the plugin's "Auto-generated by Course Command Center" marker, the plugin refuses to touch it (shown as an error) rather than risk overwriting an unrelated note — pick a different path or move the existing file.
Refresh never re-fetches Canvas, but it doesn't lose Canvas data either. A sync's raw events are saved to the plugin's local settings; every Refresh (and every dashboard-file/view update) re-matches that saved data against your current vault notes rather than needing a fresh network call. So clicking Refresh right after a sync still shows Canvas items — and if you create a note for one in the meantime, the very next Refresh correctly drops it out of "unmatched" without needing to sync again.
An unmatched Canvas event (no vault note yet) still appears in every
relevant section — and its title is a clickable wikilink, not just plain
text. Click it and Obsidian creates a note at exactly where that
assignment's note would live (its course's configured folder); the plugin
then fills in the frontmatter (course, delivery, type: assignment,
due, id) and template content, the same way a quick action would, right
after Obsidian creates the blank file. A secondary (Canvas) link next to
it still opens the assignment on Canvas directly, and the "Create note"
button in the sync review panel still works too — both are just alternate
ways to get the same result as clicking the dashboard-file link.
Automates keeping every date-driven section ("Due today", "Do next", "Upcoming deadlines", "Past due") current by pulling due dates from Canvas, without ever calling the Canvas API or storing a login token.
What happens on sync:
[Course Code] Canvas appends to every event
title.id: frontmatter field stamped by a prior sync, falling back to a
same-course, same-title match) — its due: frontmatter is updated
automatically as part of this one sync action, and id: is backfilled if
the note didn't already have one. Nothing else about the note is touched.This is read-only against Canvas (the ICS feed can't be written to) and is the plugin's one deliberate exception to "no network calls" — it's entirely optional, and everything else keeps working with the field left empty.
Detected by plugin ID only, never required: Tasks, Dataview, Templater, Excalidraw, Linter, QuickAdd, Obsidian Git. Status is shown in Settings.
📅 YYYY-MM-DD) is
also recognized on in-body checklist lines, in addition to frontmatter
due.course, type, status, etc.). Existing lecture/reference
notes that predate the plugin and use a different frontmatter convention
won't appear until they're given matching frontmatter (manually, or by
recreating them through the plugin).status: in-progress and no type: set (or a type: outside
assignment/project-deliverable/module/sql-lab/discussion/
lecture) never appears in "Current work", and — if it also has no
due: — sits in "Do next" indefinitely, since there's no due date to age
it out. This is by design for reference/background notes, but it means a
hub-style note you've marked in-progress for tracking purposes (a
semester project overview, say) can look like it's stuck in the wrong
section if you haven't given it a matching type:.Criterion | Requirement | Evidence in my work | Complete) under a
## Rubric or ## Rubric checklist heading; other table shapes aren't
recognized.overwrite_file_templates) as a best-effort call; it may silently no-op
on Templater versions that don't expose it, leaving template placeholders
in the created note.[Course Code] text Canvas
appends to each calendar event title — if your institution's Canvas
instance formats that differently, or a course's code in Settings doesn't
appear in it, those events won't match and won't be routed anywhere.course: set to (or starting with) the configured course code,
and that "Scan entire vault" is enabled in settings if the note lives
outside the course's configured root folder.No telemetry, no analytics, no AI calls, no login, no Canvas API calls. The only network request the plugin ever makes is fetching your Canvas ICS calendar feed, and only if you've configured a feed URL and explicitly clicked "Sync Canvas calendar" — never automatically or in the background. All data otherwise stays in this vault or in this plugin's local settings.
See CHANGELOG.md.
Issues and pull requests are welcome. Before sending a PR: npm run build
must pass with zero TypeScript errors, and npm test must pass — both are
plain npm scripts, no CI setup required to run them locally. Keep changes
consistent with the design decisions above (local-first, no network calls
beyond the one documented exception, no destructive file operations, no
hard dependency on another plugin).