diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 9bd8ced..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,14 +0,0 @@
-# .NET build artifacts
-bin/
-obj/
-*.dll
-
-# Nix
-.result/
-result
-
-# IDE
-.vs/
-.vscode/
-*.user
-*.suo
diff --git a/deps.json b/deps.json
deleted file mode 100644
index 0243907..0000000
--- a/deps.json
+++ /dev/null
@@ -1,152 +0,0 @@
-[
- {
- "pname": "Diacritics",
- "version": "3.3.29",
- "hash": "sha256-sIbdJ3yMthnmJHly3WheUdYjtwPakcczTJx9ycxtgrY="
- },
- {
- "pname": "ICU4N",
- "version": "60.1.0-alpha.356",
- "hash": "sha256-1QyOgO7pNMeoEgBtl6o8IG4o91wD2hFUgRI0jM0ltxY="
- },
- {
- "pname": "ICU4N.Transliterator",
- "version": "60.1.0-alpha.356",
- "hash": "sha256-RLNwQNVqNz8Omfb/mC/rzQWVq8c7uCnNdG2qi4xJmds="
- },
- {
- "pname": "J2N",
- "version": "2.0.0",
- "hash": "sha256-YvtIWErlm2O49hg3lIRm5Ha8/owkQkfMudzuldC3EhA="
- },
- {
- "pname": "Jellyfin.Common",
- "version": "10.10.7",
- "hash": "sha256-9EIigrDheob4vRP+UBAoIPHH4fyz6Cl27GUpelEGpBg="
- },
- {
- "pname": "Jellyfin.Controller",
- "version": "10.10.7",
- "hash": "sha256-/obWAuxWpSn+NlMES+fjyrf1g+qbxmSYQUBvgXQYltQ="
- },
- {
- "pname": "Jellyfin.Data",
- "version": "10.10.7",
- "hash": "sha256-lRQjg/HkFAtCN0woL1gX6j5dMfVLP/fzQTKjFTcRth4="
- },
- {
- "pname": "Jellyfin.Extensions",
- "version": "10.10.7",
- "hash": "sha256-AOGJ2IoT2v+LnlEqnb2O4FnXapcQiH0V9ny8GUysNHg="
- },
- {
- "pname": "Jellyfin.Model",
- "version": "10.10.7",
- "hash": "sha256-ubsClGTLq/aFnMiBCaHVUX2b88beuL07Yn5VXNbTCjQ="
- },
- {
- "pname": "Jellyfin.Naming",
- "version": "10.10.7",
- "hash": "sha256-/jlIS0X4FuBo6Ac1cjLGzgdJ3vR6VotBspxpNrqfMzo="
- },
- {
- "pname": "Microsoft.Extensions.Caching.Abstractions",
- "version": "2.0.0",
- "hash": "sha256-Eg1MES40kzkGW9tZmjaKtbWI00Kbv7fLJQmjrigjxqk="
- },
- {
- "pname": "Microsoft.Extensions.Caching.Memory",
- "version": "2.0.0",
- "hash": "sha256-1fnNvp62KrviVwYlqVl1CbdaZVpCDah9eCZeNDGDbWM="
- },
- {
- "pname": "Microsoft.Extensions.Configuration.Abstractions",
- "version": "8.0.0",
- "hash": "sha256-4eBpDkf7MJozTZnOwQvwcfgRKQGcNXe0K/kF+h5Rl8o="
- },
- {
- "pname": "Microsoft.Extensions.Configuration.Binder",
- "version": "8.0.2",
- "hash": "sha256-aGB0VuoC34YadAEqrwoaXLc5qla55pswDV2xLSmR7SE="
- },
- {
- "pname": "Microsoft.Extensions.DependencyInjection",
- "version": "8.0.1",
- "hash": "sha256-O9g0jWS+jfGoT3yqKwZYJGL+jGSIeSbwmvomKDC3hTU="
- },
- {
- "pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
- "version": "2.0.0",
- "hash": "sha256-H1rEnq/veRWvmp8qmUsrQkQIcVlKilUNzmmKsxJ0md8="
- },
- {
- "pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
- "version": "8.0.0",
- "hash": "sha256-75KzEGWjbRELczJpCiJub+ltNUMMbz5A/1KQU+5dgP8="
- },
- {
- "pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
- "version": "8.0.2",
- "hash": "sha256-UfLfEQAkXxDaVPC7foE/J3FVEXd31Pu6uQIhTic3JgY="
- },
- {
- "pname": "Microsoft.Extensions.Logging",
- "version": "8.0.1",
- "hash": "sha256-vkfVw4tQEg86Xg18v6QO0Qb4Ysz0Njx57d1XcNuj6IU="
- },
- {
- "pname": "Microsoft.Extensions.Logging.Abstractions",
- "version": "8.0.2",
- "hash": "sha256-cHpe8X2BgYa5DzulZfq24rg8O2K5Lmq2OiLhoyAVgJc="
- },
- {
- "pname": "Microsoft.Extensions.Options",
- "version": "2.0.0",
- "hash": "sha256-EMvaXxGzueI8lT97bYJQr0kAj1IK0pjnAcWN82hTnzw="
- },
- {
- "pname": "Microsoft.Extensions.Options",
- "version": "8.0.2",
- "hash": "sha256-AjcldddddtN/9aH9pg7ClEZycWtFHLi9IPe1GGhNQys="
- },
- {
- "pname": "Microsoft.Extensions.Primitives",
- "version": "2.0.0",
- "hash": "sha256-q44LtMvyNEKSvgERvA+BrasKapP92Sc91QR4u2TJ9/Y="
- },
- {
- "pname": "Microsoft.Extensions.Primitives",
- "version": "8.0.0",
- "hash": "sha256-FU8qj3DR8bDdc1c+WeGZx/PCZeqqndweZM9epcpXjSo="
- },
- {
- "pname": "Microsoft.NETCore.Platforms",
- "version": "1.1.0",
- "hash": "sha256-FeM40ktcObQJk4nMYShB61H/E8B7tIKfl9ObJ0IOcCM="
- },
- {
- "pname": "Microsoft.NETCore.Targets",
- "version": "1.1.0",
- "hash": "sha256-0AqQ2gMS8iNlYkrD+BxtIg7cXMnr9xZHtKAuN4bjfaQ="
- },
- {
- "pname": "System.Globalization",
- "version": "4.3.0",
- "hash": "sha256-caL0pRmFSEsaoeZeWN5BTQtGrAtaQPwFi8YOZPZG5rI="
- },
- {
- "pname": "System.Runtime",
- "version": "4.3.0",
- "hash": "sha256-51813WXpBIsuA6fUtE5XaRQjcWdQ2/lmEokJt97u0Rg="
- },
- {
- "pname": "System.Text.Json",
- "version": "8.0.5",
- "hash": "sha256-yKxo54w5odWT6nPruUVsaX53oPRe+gKzGvLnnxtwP68="
- },
- {
- "pname": "System.Threading.Tasks.Dataflow",
- "version": "8.0.1",
- "hash": "sha256-hgCfF91BDd/eOtLEd5jhjzgJdvwmVv4/b42fXRr3nvo="
- }
-]
diff --git a/flake.lock b/flake.lock
deleted file mode 100644
index 0b0400a..0000000
--- a/flake.lock
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "nodes": {
- "nixpkgs": {
- "locked": {
- "lastModified": 1778443072,
- "narHash": "sha256-zi7/fsqM/kFdNuED//4WOCUtezGtKKqRNORjMvfwjnA=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "da5ad661ba4e5ef59ba743f0d112cbc30e474f32",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "ref": "nixos-unstable",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
- "root": {
- "inputs": {
- "nixpkgs": "nixpkgs"
- }
- }
- },
- "root": "root",
- "version": 7
-}
diff --git a/flake.nix b/flake.nix
deleted file mode 100644
index 2645a68..0000000
--- a/flake.nix
+++ /dev/null
@@ -1,72 +0,0 @@
-{
- description = "Jellyfin Lessons — a Jellyfin plugin";
-
- inputs = {
- nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
- };
-
- outputs =
- { self, nixpkgs }:
- let
- forAllSystems =
- fn: nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed fn;
- in
- {
- devShells = forAllSystems (
- system:
- let
- pkgs = nixpkgs.legacyPackages.${system};
- in
- {
- default = pkgs.mkShell {
- packages = [
- pkgs.dotnetCorePackages.sdk_9_0
- pkgs.typst
- pkgs.nodejs
- ];
-
- shellHook = ''
- echo "jellyfin-lessons dev shell"
- echo " dotnet: $(dotnet --version)"
- echo " typst: $(typst --version)"
- echo " node: $(node --version)"
- '';
- };
- }
- );
-
- packages = forAllSystems (
- system:
- let
- pkgs = nixpkgs.legacyPackages.${system};
- in
- {
- default = pkgs.callPackage (
- {
- buildDotnetModule,
- dotnetCorePackages,
- lib,
- }:
- buildDotnetModule {
- pname = "jellyfin-lessons";
- version = "0.1.0";
-
- src = self;
-
- projectFile = "plugin/Jellyfin.Plugin.Lessons.csproj";
- nugetDeps = ./deps.json;
-
- dotnet-sdk = dotnetCorePackages.sdk_9_0;
- dotnet-runtime = dotnetCorePackages.aspnetcore_9_0;
-
- meta = {
- description = "Jellyfin Lessons plugin";
- license = lib.licenses.mit;
- platforms = lib.platforms.all;
- };
- }
- ) { };
- }
- );
- };
-}
diff --git a/plugin/Jellyfin.Plugin.Lessons.csproj b/plugin/Jellyfin.Plugin.Lessons.csproj
deleted file mode 100644
index 37bd5cc..0000000
--- a/plugin/Jellyfin.Plugin.Lessons.csproj
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
- net9.0
- JellyfinLessons
- jellyfin-lessons
-
-
-
-
- runtime
-
-
- runtime
-
-
-
diff --git a/templates/example-lesson.typ b/templates/example-lesson.typ
new file mode 100644
index 0000000..44122e7
--- /dev/null
+++ b/templates/example-lesson.typ
@@ -0,0 +1,83 @@
+// example-lesson.typ — Complete example demonstrating the lesson template
+// ============================================================
+// Frontmatter:
+// title: God's Creation: The Water Cycle
+// grade: 3
+// subject: Science
+// date: 2026-01-15
+// media: jellyfin://uuid-water-cycle, jellyfin://uuid-cloud-formation
+// description: Learn how God designed water to move through His creation —
+// evaporation, condensation, and precipitation working together to sustain life.
+
+#import "lesson.typ": lesson-template, media-ref
+
+#show: doc => lesson-template(
+ doc,
+ title: "God's Creation: The Water Cycle",
+ grade: "3",
+ subject: "Science",
+ date: "2026-01-15",
+ media: (
+ ("uuid-water-cycle", "The Water Cycle Explained"),
+ ("uuid-cloud-formation", "Cloud Formation and Weather"),
+ ),
+ description: "Learn how God designed water to move through His creation — evaporation, condensation, and precipitation working together to sustain life on Earth.",
+)
+
+= What Is the Water Cycle?
+
+Water is one of God's most amazing creations! The water cycle is how water moves
+all around our planet. It never runs out — it just keeps going around and around.
+There are four main steps: evaporation, condensation, precipitation, and collection.
+
+== Evaporation
+
+When the sun shines on oceans, lakes, and rivers, the water gets warm and turns
+into a gas called *water vapor*. This gas rises up into the sky. You can't see it,
+but it's there! Evaporation happens every day, especially on warm, sunny days.
+
+== Condensation
+
+When the water vapor goes high up where it's cold, it turns back into tiny water
+droplets. These droplets come together to make clouds. Have you ever looked at
+clouds and seen shapes? Those shapes are made of millions of tiny water droplets
+sticking together!
+
+== Precipitation
+
+When the clouds get too heavy with water droplets, the water falls back down to
+Earth. This is called *precipitation*. It can be:
+
+- Rain — liquid water falling from the sky
+- Snow — frozen crystals that fall in cold weather
+- Sleet — tiny ice pellets that bounce when they land
+- Hail — hard balls of ice that form in big storms
+
+God sends precipitation to water the plants and fill the rivers, lakes, and oceans.
+
+== Collection
+
+The water that falls onto the ground collects in rivers, lakes, and oceans. Some
+of it soaks into the ground and becomes *groundwater* — water stored underground
+that plants can reach with their roots. Then the sun warms the water, and the
+whole cycle starts all over again!
+
+= Why Is the Water Cycle Important?
+
+God designed the water cycle to do important work:
+
+- Give plants the water they need to grow
+- Provide fresh water for animals and people to drink
+- Keep our rivers and lakes full of water
+- Help control the temperature of our whole planet
+- Clean the water naturally as it moves through the cycle
+
+*"He causes the vapors to ascend from the ends of the earth; He makes lightning
+for the rain; He brings the wind out of His treasuries."* — Psalm 135:7
+
+= Think About It
+
+1. What happens to water when the sun shines on a lake?
+2. Why do clouds form high up in the sky?
+3. What are the four types of precipitation?
+4. How does the water cycle show God's care for His creation?
diff --git a/templates/lesson.typ b/templates/lesson.typ
new file mode 100644
index 0000000..a85cab3
--- /dev/null
+++ b/templates/lesson.typ
@@ -0,0 +1,90 @@
+// lesson.typ — Typst template for homeschool lessons
+// ============================================================
+// This template produces clean, structured lesson documents.
+// Designed to compile to HTML for rendering inside Jellyfin.
+//
+// Frontmatter convention (documented as comments at top of each lesson):
+// // title: The Lesson Title
+// // grade: 3
+// // subject: Science
+// // date: 2026-01-15
+// // media: jellyfin://uuid-1, jellyfin://uuid-2
+// // description: Short description of the lesson
+//
+// Usage:
+// #import "lesson.typ": lesson-template, media-ref
+// #show: doc => lesson-template(
+// doc,
+// title: "The Water Cycle",
+// grade: "3",
+// subject: "Science",
+// date: "2026-01-15",
+// media: (),
+// description: "Learn about evaporation and condensation.",
+// )
+
+// Render a single Jellyfin media reference.
+// Produces a element in HTML output
+// that the Jellyfin plugin can parse and link to the player.
+#let media-ref(id, title) = {
+ raw(lang: "jellyfin-media", id + "|" + title)
+}
+
+// Render a list of media references as a formatted section.
+#let media-section(media-items) = {
+ if media-items.len() > 0 {
+ heading(level: 2)[Media]
+ for item in media-items {
+ list.item(media-ref(item.at(0), item.at(1)))
+ }
+ }
+}
+
+// The main lesson template.
+// - body: The lesson content (passed via show rule)
+// - title: Lesson title (string)
+// - grade: Grade level (string, e.g. "3")
+// - subject: Subject name (string)
+// - date: Lesson date in YYYY-MM-DD format
+// - media: Array of (id, title) pairs for linked Jellyfin media
+// - description: Short description of the lesson
+#let lesson-template(
+ body,
+ title: "Untitled Lesson",
+ grade: none,
+ subject: none,
+ date: none,
+ media: (),
+ description: none,
+) = {
+ // Document metadata for PDF output
+ set document(title: title)
+
+ // Title — prominent heading
+ heading(level: 1, title)
+
+ // Metadata line: Grade X — Subject — Date
+ let meta-parts = ()
+ if grade != none { meta-parts.push([Grade ] + grade) }
+ if subject != none { meta-parts.push(subject) }
+ if date != none { meta-parts.push(date) }
+ if meta-parts.len() > 0 {
+ let sep = [ — ]
+ let meta-text = meta-parts.first()
+ for i in range(1, meta-parts.len()) {
+ meta-text += sep + meta-parts.at(i)
+ }
+ par(text(style: "italic", size: 0.9em, meta-text))
+ }
+
+ // Description paragraph
+ if description != none {
+ par([*Description:* ] + description)
+ }
+
+ // Lesson body content (renders user-authored sections, paragraphs, lists, etc.)
+ body
+
+ // Media references section — listed with identifiable jellyfin-media codes
+ media-section(media)
+}