A multi-platform application that generates allergen-aware, printable lunch calendars for any school in the LINQ Connect system. It is available on Windows (WPF/.NET), macOS (SwiftUI), and iPhone/iPad (SwiftUI). It fetches menu data from the LINQ Connect API, analyzes each entree for allergen safety, and produces a color-coded HTML calendar designed for easy scanning and printing. Defaults are pre-configured for Lakeville Area Schools.
- Live API and offline HAR file support -- fetch menus directly from the LINQ Connect API, or load a previously captured HAR file for offline use.
- 17 allergen filters -- select any combination of allergens; entrees containing them are flagged with strikethrough text.
- Braces-friendly filter -- optional
π¦·marker next to hard, crunchy, chewy, or sticky items (popcorn, hard pretzels, raw apples, sticky candy, beef jerky, etc.) so families with kids in orthodontia can scan the calendar at a glance. Items aren't removed β they're flagged so the family can still decide. - ADHD-friendly color-coded calendar -- per-plan color-coded badges, "From Home" badges, gray no-school days, favorite highlighting. High-contrast colors chosen for quick visual scanning.
- 21 visual themes -- seasonal, fun, and basic themes with auto-suggestion by month. Themes can be hidden via settings.
- Three calendar layout modes -- List (classic stacked badges), Icons Left, or Icons Right. Grid modes show per-plan icon buttons alongside food items in a CSS Grid layout with tinted row backgrounds. Per-plan emoji icons, short labels (click-to-edit), and display order are user-customizable. Common plan names auto-collapse to cleaner labels and icons such as
Breakfast,Lunch, andBig Cat. - Keyboard-friendly Windows workflow -- labeled controls, access keys, quick Breakfast/Lunch session chips, compact month/year navigation, and shortcuts like
Alt+B(browser),Alt+P(preview),Alt+Left, andAlt+Right. - Customizable holiday icons -- configure emoji and optional messages for no-school days by keyword. Defaults pre-populated from common US holidays.
- Cross out past days -- optional faded overlay with a subtle X on past days, like crossing off a wall calendar. Days are marked past only after 3 PM (end of school day).
- Rotating day labels -- configurable corner triangles for alternating schedules (Red/White Day, Day A/B). Fetch from CMS now pulls both the currently selected month and the following month so cross-month rotations are visible without a re-fetch. Fetched assignments preserve exact dates and treat gaps inside the fetched window as intentional blanks.
- Share link on calendar -- optional footer with two QR codes: one linking to the school's LINQ Connect menu page and one to the project GitHub page, both embedded as self-contained base64 PNGs.
- Source link -- "View Source" button in the status bar opens the LINQ Connect public menu page for your school in the default browser.
- Preview zoom -- adjustable +/- zoom in the status bar (5% increments, default 100%). Zoom applies only to the in-app preview; the saved HTML file and browser view are unaffected.
- Preview toggle -- hide the preview pane entirely when you want a narrow, sidebar-only window. Open in browser stays available without re-expanding the preview.
- Printable landscape HTML -- self-contained HTML with
@page landscape,print-color-adjust: exact, and compact print-only spacing so common month layouts stay on a single page. - Forced home days -- per-session configurable weekdays that always show a "From Home" badge, regardless of safe options.
- Persisted settings -- selected allergens, forced home days, not-preferred/favorite foods, theme, layout mode, plan icons/order, and holiday overrides are saved to a JSON file and restored on next launch. Repeated entries are normalized away on load/save, and not-preferred takes precedence over favorite if stale settings contain both.
- Settings drift sync -- when you load menu data, saved favorites and not-preferred names are reconciled against the live recipe catalog using a fuzzy match (character bigram Dice β₯ 0.7). Renames between deploys (
PB&J UncrustableβPBJ Uncrustables) migrate automatically; unmatched names are preserved in case they appear in a future month. The status bar reports how many items were synced. - Dynamic school support -- enter any LINQ Connect identifier code to look up a district and its buildings. All menu plans are discovered dynamically.
- About screen -- Help > About (Windows), App > About (macOS), or the in-app About screen (iPhone/iPad) shows version, git commit/branch, build date, .NET / OS info, and a one-click "Copy Diagnostics" button that puts a paste-ready bug report on the clipboard.
Windows:
- .NET 10 SDK (LTS through November 2028; 10.0.7 is the latest patch as of April 2026)
- Windows 10 or later
- If you're working from WSL, use
dotnet.exe/powershell.exefor the WPF app and tests.
Apple platforms:
- macOS 13 (Ventura) or later
- Xcode 15+ with Swift 5.9+
- iOS/iPadOS 17+ for device testing
# Build
dotnet build school-lunch-menu.Windows/SchoolLunchMenu.csproj
# Run
dotnet run --project school-lunch-menu.Windows/SchoolLunchMenu.csproj
# Publish self-contained executable
dotnet publish school-lunch-menu.Windows/SchoolLunchMenu.csproj --configuration Release --self-contained -r win-x64 -p:PublishSingleFile=trueFrom WSL, prefer:
dotnet.exe build school-lunch-menu.Windows/SchoolLunchMenu.csproj
dotnet.exe test tests/SchoolLunchMenu.Tests/SchoolLunchMenu.Tests.csproj
dotnet.exe test tests/SchoolLunchMenu.UiTests/SchoolLunchMenu.UiTests.csproj# Build the macOS app
cd school-lunch-menu.Mac
xcodebuild -project SchoolLunchMenu.xcodeproj -scheme SchoolLunchMenu -configuration Release build
# Build the iPhone/iPad app for Simulator
xcodebuild -project SchoolLunchMenu.xcodeproj -scheme SchoolLunchMenuiOS -configuration Debug -destination 'generic/platform=iOS Simulator' build
# Or open in Xcode
open school-lunch-menu.Mac/SchoolLunchMenu.xcodeprojIn Xcode, use the SchoolLunchMenu scheme for macOS and SchoolLunchMenuiOS for iPhone/iPad.
For device installs, archives, or any signed build, fill in school-lunch-menu.Mac/Configs/LocalSigning.xcconfig from school-lunch-menu.Mac/Configs/LocalSigning.xcconfig.example, then open Signing & Capabilities if you want to confirm the resolved team and bundle IDs in Xcode. See docs/APPLE-SIGNING.md for the exact workflow and non-App-Store distribution options.
# macOS archive
./school-lunch-menu.Mac/scripts/archive-macos.sh
# macOS Developer ID export (after archiving)
./school-lunch-menu.Mac/scripts/export-macos-developer-id.sh
# macOS notarization + stapling (required before broad outside-the-store sharing)
NOTARY_PROFILE=school-lunch-menu-notary ./school-lunch-menu.Mac/scripts/notarize-macos.sh
# iPhone/iPad archive
./school-lunch-menu.Mac/scripts/archive-ios.sh
# iPhone/iPad Ad Hoc export (after archiving)
./school-lunch-menu.Mac/scripts/export-ios-adhoc.shThese scripts use the local signing overrides in school-lunch-menu.Mac/Configs/LocalSigning.xcconfig if present.
The macOS Developer ID export still needs notarization before Gatekeeper will accept it on other Macs.
- Enter an identifier code and click Look Up to find your school district.
- Select a building from the dropdown.
- Pick a month and year. On Windows you can use the compact month navigator or
Alt+Left/Alt+Right. - Pick a serving session. On Windows, the quick session chips make Breakfast/Lunch switching faster than reopening a dropdown.
- Click Fetch from API to download menu data from LINQ Connect, or click Load from HAR to load a previously saved
.harfile. - Check the allergens you want to filter (Milk is selected by default).
- Optionally mark foods as not preferred or favorites.
- Configure forced home days (default: Thursday) per serving session.
- Optionally change Day Layout to "Icons Left" or "Icons Right" to see per-line icon buttons. Less-used plan icon/label/reorder controls live under the Plan Buttons & Icons expander.
- Optionally customize holiday icons in the Holiday Icons section.
- Optionally check Cross out past days to fade and X-out past dates.
- Optionally check Share link on calendar to add a QR code footer linking to the project page.
- Optionally expand Day Labels to set up rotating labels (e.g., Red Day / White Day). Add labels with colors, choose a corner position, and optionally set a start date.
- Click Generate Calendar to produce the HTML calendar. It's displayed in the preview pane (use +/- to zoom) and saved to a temporary directory.
- Click Open in Browser to launch in your default browser, or View Source in the status bar to see the LINQ Connect public menu page.
- Print the page in landscape orientation from the browser.
Settings are persisted to a JSON file and restored on next launch:
- Windows:
settings.jsonnext to the executable - macOS:
~/Library/Application Support/SchoolLunchMenu/settings.json - iPhone/iPad: app sandbox
Application Support/SchoolLunchMenu/settings.json
{
"SelectedAllergenIds": ["4dce70b9-238e-ea11-bd68-f554d510c22b"],
"ForcedHomeDaysBySession": {
"Lunch": ["Thursday"]
},
"NotPreferredBySession": {
"Lunch": ["PBJ Uncrustable"]
},
"FavoritesBySession": {
"Lunch": ["Hamburger on Bun"]
},
"Identifier": "YVAM38",
"BuildingId": "c895c20a-5e8e-ea11-bd68-f554d510c22b",
"SelectedSessionName": "Lunch",
"SelectedThemeName": "Default",
"LayoutMode": "IconsLeft",
"PlanLabelOverrides": {
"Lunch - Middle School 25-26": "Lunch"
},
"PlanIconOverrides": {
"Lunch - Middle School 25-26": "π½οΈ"
},
"CrossOutPastDays": true,
"BracesFriendlyFilter": false,
"ShowShareFooter": false,
"DayLabelsEnabled": true,
"DayLabelCycle": [
{ "Label": "Red", "Color": "#dc3545" },
{ "Label": "White", "Color": "#adb5bd" }
],
"DayLabelAssignments": [
{ "Date": "2026-03-19", "Label": "Red", "Color": "#dc3545" }
],
"DayLabelCoverageStartDate": "2026-03-01",
"DayLabelCoverageEndDate": "2026-04-04",
"DayLabelCorner": "TopLeft"
}school-lunch-menu/
βββ .github/
β βββ workflows/
β βββ dotnet.yml # Windows CI/CD pipeline
β βββ swift.yml # Apple CI/CD pipeline
βββ school-lunch-menu.Windows/ # Windows WPF application (.NET 10)
β βββ App.xaml.cs # DI container setup, Serilog configuration
β βββ MainWindow.xaml # WPF layout with guided sidebar flow and WebView2 preview
β βββ ViewModels/ # MainViewModel (MVVM with CommunityToolkit)
β βββ Models/ # ProcessedDay, ProcessedMonth, RecipeItem, AppSettings
β βββ Models/Api/ # LINQ Connect API response DTOs
β βββ Services/ # API client, HAR parser, menu analyzer, HTML generator
β βββ Converters/ # WPF value converters
βββ school-lunch-menu.Mac/ # Apple SwiftUI application (macOS + iPhone/iPad)
β βββ SchoolLunchMenu.xcodeproj
β βββ Configs/ # Shared signing defaults + local override example
β βββ scripts/ # Apple archive/export helper scripts
β βββ SchoolLunchMenu/
β βββ App/ # SwiftUI app entry point
β βββ Views/ # Shared SwiftUI views (MainView, SidebarView, PreviewView)
β βββ ViewModels/ # MainViewModel shared across Apple platforms
β βββ Models/ # Swift model types matching Windows DTOs
β βββ Models/Api/ # LINQ Connect API response Codable types
β βββ Services/ # API client, HAR parser, menu analyzer, HTML generator
β βββ Resources/ # Asset catalogs
βββ docs/ # Project documentation
βββ scripts/ # Utility scripts (for example screenshot capture)
βββ SchoolLunchMenu.slnx # Visual Studio solution file
βββ README.md
Both platforms follow the MVVM (Model-View-ViewModel) pattern:
- Models define data structures for API responses, processed menu data, and application settings
- ViewModels contain business logic, state management, and commands
- Views handle UI rendering and user interaction
- Services encapsulate external concerns (API calls, file I/O, HTML generation)
The Windows version uses:
- WPF with XAML for UI
- CommunityToolkit.Mvvm for MVVM infrastructure
- Microsoft.Extensions.DependencyInjection for DI
- Serilog for logging
The Apple versions use:
- SwiftUI with the Observation framework
- Native Swift Codable for JSON serialization
- URLSession for networking
- WebKit for HTML preview
- Shared Swift services and view models across macOS and iPhone/iPad
The Windows side includes both unit tests and FlaUI-based UI smoke tests:
dotnet test tests/SchoolLunchMenu.Tests/SchoolLunchMenu.Tests.csproj
dotnet test tests/SchoolLunchMenu.UiTests/SchoolLunchMenu.UiTests.csprojTests cover:
- MenuAnalyzer -- allergen safety, not-preferred, favorites, no-school days, multi-plan analysis, braces-friendly flagging on/off
- CalendarHtmlGenerator -- HTML output, themes, grid mode, past days, day labels, share footer
- SettingsService -- default settings, round-trip persistence
- DayLabelFetchService -- CMS HTML parsing regex patterns,
cal_datecross-month URL construction, full HTTP capture round-trip - BracesFoodAdvisor -- positive matches (popcorn, jerky, raw apples), negative matches (soft pretzel, popcorn chicken, mac & cheese), case-insensitive, empty-string handling
- PreferenceReconciler -- exact match, fuzzy rename via Dice coefficient (β₯ 0.7), unmatched-name preservation, multi-session, threshold rejection of unrelated names
- AboutDiagnostics -- diagnostic block contains expected sections and reports the live .NET runtime
- FlaUI smoke tests -- app launch, core controls, session selector, preview toggle, braces filter checkbox discovery, and end-to-end calendar generation
- ProcessedDay -- computed properties (IsNoSchool, AnyLineSafe, HasMenu)
- CalendarThemes -- 21 themes, categories, auto-suggestion coverage
Total: 99 unit tests (62 β 99, +37 new) plus the 3-test FlaUI smoke suite.
The Apple targets are currently verified by GitHub Actions builds for macOS and iOS Simulator. A dedicated Swift test target does not exist yet.
- Dashboards -- the README badges at the top link to the active GitHub Actions dashboards for
.NET Build,Apple Build, andCodeQL. - Dependency updates --
.github/dependabot.ymlenables weekly GitHub Actions and NuGet update scans. - Agent guidance --
CLAUDE.mdandCODEX.mddescribe repo-specific tooling expectations for Windows-from-WSL and Mac verification. - Resume docs --
docs/AI-HANDOFF.mdcaptures the current branch state, validated work, remaining Mac-side checks, and the fastest commands to continue. - Mac continuation prompt --
docs/mac-verify-prompt.mdis intended to be pasted directly into another Claude/Codex instance running on a Mac. - Plugins / skills -- there are no repo-local plugin manifests or Codex skill bundles checked into this repository. The supported automation surface is the normal CLI stack:
gh,dotnet.exe,powershell.exe,xcodebuild, and Xcode.
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
