From 348a4555cacd6d62a8cb92871d55f3169e946c98 Mon Sep 17 00:00:00 2001 From: Aaron Axvig Date: Mon, 3 Aug 2026 21:38:34 -0500 Subject: [PATCH] Add dynamic sizing of recipe --- MMM-NextcloudCookbook.css | 18 +++++++++------- MMM-NextcloudCookbook.js | 40 +++++++++++++++++++++++++++++++++++ README.md | 44 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/MMM-NextcloudCookbook.css b/MMM-NextcloudCookbook.css index 9e92a15..f3a1ff0 100644 --- a/MMM-NextcloudCookbook.css +++ b/MMM-NextcloudCookbook.css @@ -11,6 +11,8 @@ } .MMM-NextcloudCookbook .ncc-active { + --ncc-type-scale: 1; + position: fixed; inset: 0; z-index: 20; @@ -49,9 +51,9 @@ } .MMM-NextcloudCookbook .ncc-recipe h1 { - margin: 0 220px 26px 0; + margin: 0 220px calc(26px * var(--ncc-type-scale)) 0; color: #fff6d8; - font-size: 82px; + font-size: calc(82px * var(--ncc-type-scale)); line-height: 1.08; } @@ -59,8 +61,8 @@ display: flex; gap: 10px 22px; align-items: baseline; - margin: 0 0 36px; - font-size: 36px; + margin: 0 0 calc(36px * var(--ncc-type-scale)); + font-size: calc(36px * var(--ncc-type-scale)); } .MMM-NextcloudCookbook .ncc-meta dt { @@ -79,21 +81,21 @@ } .MMM-NextcloudCookbook .ncc-columns h2 { - margin: 0 0 18px; + margin: 0 0 calc(18px * var(--ncc-type-scale)); color: #d6b96b; - font-size: 56px; + font-size: calc(56px * var(--ncc-type-scale)); } .MMM-NextcloudCookbook .ncc-columns ul, .MMM-NextcloudCookbook .ncc-columns ol { margin: 0; padding-left: 1.25em; - font-size: 50px; + font-size: calc(50px * var(--ncc-type-scale)); line-height: 1.36; } .MMM-NextcloudCookbook .ncc-columns li { - margin-bottom: 18px; + margin-bottom: calc(18px * var(--ncc-type-scale)); } .MMM-NextcloudCookbook .ncc-error { diff --git a/MMM-NextcloudCookbook.js b/MMM-NextcloudCookbook.js index 1a11c53..55610eb 100644 --- a/MMM-NextcloudCookbook.js +++ b/MMM-NextcloudCookbook.js @@ -12,6 +12,7 @@ Module.register("MMM-NextcloudCookbook", { start() { this.state = { active: false, recipe: null, error: null }; this.hiddenModules = false; + window.addEventListener("resize", () => this.scheduleRecipeFit()); this.sendSocketNotification("CONFIG", this.config); }, @@ -30,6 +31,45 @@ Module.register("MMM-NextcloudCookbook", { this.updateDom(this.config.animationSpeed); }, + notificationReceived(notification) { + if (notification === "MODULE_DOM_UPDATED") this.scheduleRecipeFit(); + }, + + scheduleRecipeFit() { + window.requestAnimationFrame(() => this.fitRecipe()); + }, + + fitRecipe() { + const root = document.getElementById(this.identifier)?.querySelector(".ncc-active"); + const recipe = root?.querySelector(".ncc-recipe"); + if (!root || !recipe) return; + + const style = window.getComputedStyle(root); + const availableHeight = root.clientHeight + - Number.parseFloat(style.paddingTop) + - Number.parseFloat(style.paddingBottom); + const availableWidth = root.clientWidth + - Number.parseFloat(style.paddingLeft) + - Number.parseFloat(style.paddingRight); + let minimum = 0.45; + let maximum = 2; + let best = minimum; + + for (let attempt = 0; attempt < 12; attempt++) { + const scale = (minimum + maximum) / 2; + root.style.setProperty("--ncc-type-scale", scale); + const fits = recipe.scrollHeight <= availableHeight + && recipe.scrollWidth <= availableWidth; + if (fits) { + best = scale; + minimum = scale; + } else { + maximum = scale; + } + } + root.style.setProperty("--ncc-type-scale", best.toFixed(3)); + }, + setRecipeMode(active) { if (active && !this.hiddenModules) { MM.getModules().exceptModule(this).enumerate((module) => { diff --git a/README.md b/README.md index 6464ded..c128113 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,50 @@ References: - [Cookbook API documentation](https://nextcloud.github.io/cookbook/dev/api/0.1.0/index.html) - [Cookbook user documentation](https://nextcloud.github.io/cookbook/user/) +## Future architecture: recipe sources and consumers + +The current module both retrieves recipes and presents the full-screen recipe +mode. A future version should separate those responsibilities using +MagicMirror's notification system as a shared recipe data stream: + +```text +MMM-NextcloudCookbook ─┐ +MMM-Paprika ───────────┼── recipe data notifications ──> MMM-RecipeMode +MMM-Mealime ───────────┘ +``` + +In that model: + +- `MMM-NextcloudCookbook` is a source adapter. It reads recipes from Nextcloud + Cookbook and publishes normalized recipe records. +- `MMM-RecipeMode` is a consumer. It provides recipe selection, the phone + control page, portion scaling, QR code, and full-screen cooking display. +- Other source modules can publish recipes from Paprika, Mealime, local files, + or other services without duplicating the presentation code. +- Additional consumers can use the same recipe data for meal planning, search, + shopping lists, dashboards, or other displays. + +The shared notification contract should eventually define source-qualified +stable recipe IDs, basic list metadata, complete recipe records, refresh and +error states, and optional source capabilities. The normalized record should +retain schema.org Recipe fields where practical. Exact notification names and +payload schemas should be designed when a second source or consumer is +implemented, rather than treating the current module's private HTTP API as the +cross-module contract. + +This separation is a future feature. The current combined behavior should +remain functional until `MMM-RecipeMode` can replace its presentation and +control responsibilities. + +### Future presentation option: preserve selected modules + +Recipe mode currently hides every other MagicMirror module. A future +`MMM-RecipeMode` configuration should accept an allowlist of modules to leave +visible while cooking. For example, a user could preserve a calendar at the top +of the display while the recipe occupies the remaining area. The option should +support module names and, where multiple instances exist, specific instance +identifiers. Hiding all other modules should remain the default. + ## Development ```bash