diff --git a/blog/.env.example b/blog/.env.example index 5b806721b..030d5872b 100644 --- a/blog/.env.example +++ b/blog/.env.example @@ -5,5 +5,11 @@ # Main Doenet application URL # Local development: http://localhost:3000 -# Production: https://doenet.org -PUBLIC_DOENET_MAIN_URL=https://doenet.org +# Production: https://beta.doenet.org +PUBLIC_DOENET_MAIN_URL=https://beta.doenet.org + +# Newsletter generator configuration (server/build only) +NEWSLETTER_ISSUE_DATE=2026-02-18 +NEWSLETTER_EXCERPT_MARKER=more +NEWSLETTER_MANUAL_ORDER=["simplify-creating-interactive-activities","learn-doenet-saint-louis-university","community-challenge-fractions"] +NEWSLETTER_EVENTS_URL=https://beta.doenet.org/events diff --git a/blog/.gitignore b/blog/.gitignore index 5975753da..8f475d9c2 100644 --- a/blog/.gitignore +++ b/blog/.gitignore @@ -18,8 +18,12 @@ pnpm-debug.log* .env.local .env.production +# newsletter content (untracked for easy editing) +newsletter-events.md + # macOS-specific files .DS_Store # jetbrains setting folder .idea/ + diff --git a/blog/package.json b/blog/package.json index 9375ece5e..89c9ba1d8 100644 --- a/blog/package.json +++ b/blog/package.json @@ -19,10 +19,11 @@ "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "astro": "^5.17.1", - "rehype-katex": "^7.0.1", - "remark-math": "^6.0.0", + "marked": "^17.0.3", "react": "^19.2.3", "react-dom": "^19.2.3", + "rehype-katex": "^7.0.1", + "remark-math": "^6.0.0", "sharp": "^0.34.3" } } diff --git a/blog/src/content/community-challenge-fractions.md b/blog/src/content/community-challenge-fractions.mdx similarity index 68% rename from blog/src/content/community-challenge-fractions.md rename to blog/src/content/community-challenge-fractions.mdx index 54728503e..bf979e120 100644 --- a/blog/src/content/community-challenge-fractions.md +++ b/blog/src/content/community-challenge-fractions.mdx @@ -9,4 +9,8 @@ heroImage: "/blog/images/community-challenge-fractions/fractions-snapshot.png" Challenger: Anurag Katyal is an avid Doenet user, who enjoys creating problems for other people. -Understanding fractions is a common issue in building number sense for students. What does a fraction mean? How do we add fractions, and why does it work? How do we compare fractions? We’d love to have more activities to help our students build conceptual understanding of fractions! To participate in this challenge, create a public activity at beta.doenet.org and submit a link to your activity to communications@doenet.org. The winner of this challenge will have their activity featured in the next Doenet newsletter! +Understanding fractions is a common issue in building number sense for students. What does a fraction mean? How do we add fractions, and why does it work? How do we compare fractions? We’d love to have more activities to help our students build conceptual understanding of fractions! + +
+ +To participate in this challenge, create a public activity at beta.doenet.org and submit a link to your activity to communications@doenet.org. The winner of this challenge will have their activity featured in the next Doenet newsletter! diff --git a/blog/src/content/learn-doenet-saint-louis-university.mdx b/blog/src/content/learn-doenet-saint-louis-university.mdx index aeb39462e..9602d540d 100644 --- a/blog/src/content/learn-doenet-saint-louis-university.mdx +++ b/blog/src/content/learn-doenet-saint-louis-university.mdx @@ -13,6 +13,8 @@ During our November 7-8 Doenet workshop, participants built classroom-ready acti The hands-on format allowed participants to focus deeply on their specific course needs, with individualized support from facilitators throughout the development process. Each participant completed a polished activity ready for immediate classroom use, with several planning to deploy their work as early as the following semester. + + The final show-and-tell session showcased what participants were able to accomplish in just two days. One of the examples worth mentioning is the interactive Mean Value Theorem themed activity featuring dynamic graphs that respond to student input, built-in answer checking, and a carefully designed set of problems, designed by Daniel Kang. Students working through this activity can explore the theorem visually while receiving immediate feedback on their responses, transforming what's traditionally a challenging calculus concept into an engaging, interactive experience.  diff --git a/blog/src/content/simplify-creating-interactive-activities.mdx b/blog/src/content/simplify-creating-interactive-activities.mdx index e802d43d6..6e22db817 100644 --- a/blog/src/content/simplify-creating-interactive-activities.mdx +++ b/blog/src/content/simplify-creating-interactive-activities.mdx @@ -24,6 +24,8 @@ Here's a snippet from one of these activities where students discover how to ske Despite how well my students respond to interactive exploratory activities, one thing I have discovered after writing hundreds is that **making them is a huge pain!** For the past couple decades, I've been working on ways to ease that pain. + + When I started to experiment in 2005 with creating my own interactives, I was teaching multivariable calc, so my efforts mostly took the form of 3D applets. [Here’s an example](https://mathinsight.org/directional_derivative_gradient_introduction) from 2011 about directional derivatives and gradients.  diff --git a/blog/src/pages/generate-newsletter.astro b/blog/src/pages/generate-newsletter.astro new file mode 100644 index 000000000..c727881da --- /dev/null +++ b/blog/src/pages/generate-newsletter.astro @@ -0,0 +1,757 @@ +--- +import { getCollection, render } from "astro:content"; +import { newsletterStyles } from "../utils/newsletterStyles"; +import { newsletterConfig } from "../utils/newsletter-config"; +import fs from "node:fs"; +import path from "node:path"; +import { Marked } from "marked"; + +// Note: Helper functions (escapeRegExp, extractUntilMarker, cleanForEmail, applyEmailStyles) +// are inlined directly in the client-side script to avoid eval and CSP issues. + +// ============================================================================ +// Newsletter configuration is sourced from environment variables. +// See blog/.env.example for the available keys. +// ============================================================================ + +// ============================================================================ +// DATA FETCHING AND PROCESSING +// ============================================================================ + +// Fetch all blog posts +const allPosts = await getCollection("blog"); + +// Filter and sort posts by ordered slug list +const manualOrder = newsletterConfig.manualOrder; +const manualIndex = new Map+ This page generates an email-friendly HTML newsletter from the posts + listed in the configuration. +
+ + + +