Code Farm: per-plot auto-replant pause and crop-lock check; community sync and node work

Continuous Delivery could never be turned off once a crop was planted. Add a
one-shot per-plot "stop after harvest" flag (POST /game/plot/stop, Devii tool
game_plot_stop, game_plots.stop_after_harvest), cleared by the next plant,
autoreplant claim or Refactor. Autoreplant now also respects crop locks through
the shared _crop_lock predicate. Plan and measurements in happyplanting.md.

Also includes the pending community sync, node network and related work in the
tree; the full suite passed (4999) with all of it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 02:23:43 +02:00
co-authored by Claude Sonnet 5.5
parent ce899fc5e1
commit 02ab747cd0
203 changed files with 11624 additions and 301 deletions
+2 -1
View File
@@ -108,7 +108,8 @@ p {
}
.dot.connecting,
.dot.backoff {
.dot.backoff,
.dot.suspended {
background: var(--warn);
}
+30 -3
View File
@@ -6,6 +6,7 @@ const STATE_LABELS = {
online: "Online",
backoff: "Reconnecting",
halted: "Stopped",
suspended: "Suspended",
};
function formatBytes(value) {
@@ -18,6 +19,30 @@ function formatBytes(value) {
return `${unit === 0 ? size : size.toFixed(1)} ${UNITS[unit]}`;
}
function pad(value) {
return String(value).padStart(2, "0");
}
function formatInstant(iso) {
const moment = new Date(iso);
if (Number.isNaN(moment.getTime())) {
return "";
}
return `${pad(moment.getDate())}/${pad(moment.getMonth() + 1)}/${moment.getFullYear()} ${pad(moment.getHours())}:${pad(moment.getMinutes())}`;
}
export function describeState(state) {
if (state.state === "suspended") {
const ends = formatInstant(state.suspendedUntil);
return ends ? `Suspended until ${ends}, retrying automatically` : "Suspended, retrying automatically";
}
return state.detail || state.lastError || "";
}
export function displayLabel(state, config) {
return state.deviceLabel || config.label;
}
class Popup {
constructor(root) {
this.root = root;
@@ -55,8 +80,8 @@ class Popup {
const { config, state, usage } = view;
this.$("dot").className = `dot ${state.state}`;
this.$("state").textContent = STATE_LABELS[state.state] || state.state;
this.$("detail").textContent = state.detail || state.lastError || "";
this.$("label").textContent = config.label;
this.$("detail").textContent = describeState(state);
this.$("label").textContent = displayLabel(state, config);
this.$("disclosure-card").hidden = config.disclosed;
const toggle = this.$("toggle");
toggle.checked = config.enabled;
@@ -152,4 +177,6 @@ class Popup {
}
}
new Popup(document).start();
if (typeof document !== "undefined") {
new Popup(document).start();
}