Removable reference curves, audit fixes across pages, shaded Academy art
Test and deploy / test-and-deploy (push) Successful in 1m8s

The planner's Artisan .alog drawer now shows what's attached with a
"Remove reference curve" control (re-rendered per drawer open), so adding
a reference curve is no longer a one-way door; /api/alog shares the 8 MB
body cap so real-sized logs parse instead of failing with "bad_request".

Deep-evaluation fixes: editing a brew of an archived bean no longer
silently detaches the bean; the roasts pending-review poll no longer
wipes in-progress after-roast edits; roasters gain an Edit (rename/model)
action; the gear page refuses to autosave over a failed load; duplicating
a plan carries its custom name; cupping sessions can attach a plan after
creation (ownership-checked PUT + selector); admin user deletion also
refreshes plans/audit; cupping cup-count subtitle stays live; roasts
error-row colspan corrected. Regression tests cover the new cupping PUT
and the /api/alog body cap.

Academy scenes drop the flat paper-cutout look: shared defs provide
radial-gradient shading on every bean/half-bean/particle, flame gradients
with radiant halos, soft ground shadows, and a warm-lit stage background;
fill-shift animations now ride a partial-opacity tint overlay so shading
survives the color change.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-08-11 16:59:28 -04:00
co-authored by Claude Fable 5
parent 8b80c3fa6f
commit 385474ecef
14 changed files with 368 additions and 34 deletions
+33 -2
View File
@@ -21,6 +21,38 @@ export function initAlogPanel({
resultEl = document.getElementById("alog-result"),
actualInput = document.getElementById("actual-alog-file"),
actualResultEl = document.getElementById("actual-alog-result");
// A plan keeps its attached reference curve until explicitly removed — show what's
// attached (including on page load) with a Remove control, so adding a curve is
// never a one-way door.
function renderReference(summary) {
const ref = state.plan.reference;
if (!ref?.roast) {
resultEl.replaceChildren();
return;
}
const node = document.createElement("p");
node.textContent =
summary ??
`Attached: ${ref.roast.title || "reference curve"}${ref.roast.roastDate || "no date"}.`;
const removeBtn = document.createElement("button");
removeBtn.type = "button";
removeBtn.className = "ghost-btn small";
removeBtn.style.marginLeft = "8px";
removeBtn.textContent = "Remove reference curve";
removeBtn.addEventListener("click", () => {
state.plan.reference = null;
recompute();
setText(resultEl, "Reference curve removed.");
});
node.append(removeBtn);
resultEl.replaceChildren(node);
}
renderReference();
// Re-render on every drawer open: the user may have switched plans since init, and the
// summary must always describe the plan currently loaded.
document
.getElementById("nav-alog")
?.addEventListener("click", () => renderReference());
fileInput.addEventListener("change", async (e) => {
const file = e.target.files?.[0];
if (!file) return;
@@ -135,8 +167,7 @@ export function initAlogPanel({
}
state.plan.reference = body;
recompute();
setText(
resultEl,
renderReference(
`${body.roast.title}${body.roast.roastDate || "no date"}; first crack ${fmt(body.derived?.firstCrackS)}; development ${fmt(body.derived?.developmentS)}; drop ${fmt(body.derived?.dropS)}; DTR ${body.derived?.dtrPct ?? "—"}%.`,
);
}