From 4eee1fbe1781eafb3eef434bc3c54e80983893df Mon Sep 17 00:00:00 2001 From: wtclaude Date: Fri, 9 Oct 2026 01:24:27 -0500 Subject: [PATCH] feat(egg): write the recommended PopupNotifications banner (runicnpc 9e, D327, D328) The install writes PopupNotifications.json into oxide/config or carbon/configs as a banner across the top of the screen, by the installer's rule: only where the file is missing or every setting is still the plugin's own default (the Version aside). Anything else is kept whole. The plugin's Version is written or kept, because PopupNotifications resets a config without one. A failed write is said in the console and does not fail the install: it is a look, not the bridge. Tested in ghcr.io/ptero-eggs/installers:debian: missing, the rig's defaults, defaults from a later release (version kept), four kinds of change, not JSON, an existing banner, and an unmakeable directory. egg/build.sh builds. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY --- egg/install.sh | 73 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/egg/install.sh b/egg/install.sh index 036ba9f..aaa25dd 100755 --- a/egg/install.sh +++ b/egg/install.sh @@ -88,6 +88,69 @@ if [ $WORLD_SEED == "0" ]; then fi ## ── The Runic Gateway bridge ───────────────────────────────────────────────── +# PopupNotifications' recommended look (docs/runicnpc/PLAN.md D327): a banner +# across the top of the screen. PopupNotifications is optional and the operator +# installs it (D141), so its config is theirs: the banner goes in only where the +# file is missing or is still exactly the plugin's own defaults, every setting +# equal and the Version aside (D328). Anything else is kept whole. The Version +# is written too: PopupNotifications resets a config without one to defaults. +rg_popup() { # + local file="$1/PopupNotifications.json" + local banner='{ + "Notification duration (in seconds)": 8, + "Maximum notifications shown at any time": 6, + "UI Positioning": { + "Position of the left side of notification (0.0 - 1.0)": 0.15, + "Position of the bottom of noticiation (0.0 - 1.0)": 0.87, + "Width (0.0 - 1.0)": 0.7, + "Height (0.0 - 1.0)": 0.07, + "Space between notification (0.0 - 1.0)": 0.005 + }, + "UI Options": { + "Show close button": false, + "Panel color (hex)": "#2b2b2b", + "Panel transparency (0.0 - 1.0)": 0.8, + "Close button color (hex)": "#d85540", + "Close button transparency (0.0 - 1.0)": 0.5, + "Font": "robotocondensed-bold.ttf", + "Font size": 18 + }, + "Version": { "Major": 0, "Minor": 2, "Patch": 1 } + }' + # PopupNotifications 0.2.1's GetBaseConfig(): a small grey box at the right. + local defaults='{ + "Notification duration (in seconds)": 8, + "Maximum notifications shown at any time": 6, + "UI Positioning": { + "Position of the left side of notification (0.0 - 1.0)": 0.8, + "Position of the bottom of noticiation (0.0 - 1.0)": 0.78, + "Width (0.0 - 1.0)": 0.19, + "Height (0.0 - 1.0)": 0.1, + "Space between notification (0.0 - 1.0)": 0.01 + }, + "UI Options": { + "Show close button": true, + "Panel color (hex)": "#2b2b2b", + "Panel transparency (0.0 - 1.0)": 0.5, + "Close button color (hex)": "#d85540", + "Close button transparency (0.0 - 1.0)": 0.5, + "Font": "droidsansmono.ttf", + "Font size": 12 + } + }' + if [ ! -e "${file}" ]; then + mkdir -p "$1" || return 1 + jq -n "${banner}" > "${file}" || return 1 + echo "Runic Gateway: wrote the recommended PopupNotifications banner to ${file}" + elif jq -e --argjson d "${defaults}" 'type == "object" and del(.Version) == $d' "${file}" >/dev/null 2>&1; then + jq --argjson b "${banner}" '$b + { Version: (.Version // $b.Version) }' "${file}" > "${file}.rg" \ + && mv "${file}.rg" "${file}" || { rm -f "${file}.rg"; return 1; } + echo "Runic Gateway: ${file} was PopupNotifications' defaults; wrote the recommended banner" + else + echo "Runic Gateway: kept ${file} (not PopupNotifications' defaults)" + fi +} + # After the wipe, so REMOVE_FILES can never delete the plugin this just placed. rg_install() { set -euo pipefail @@ -95,10 +158,10 @@ rg_install() { # just the variables an egg declares, and this one is not declared. local api="${RUNICGATEWAY_BUNDLE_API:-https://gitea.whitlocktech.com/api/v1/repos/RunicGateway/installer/contents/v2/rust}" local work=/tmp/runicgateway - local plugins + local plugins config case "${FRAMEWORK:-vanilla}" in - oxide) plugins=/mnt/server/oxide/plugins ;; - carbon) plugins=/mnt/server/carbon/plugins ;; + oxide) plugins=/mnt/server/oxide/plugins; config=/mnt/server/oxide/config ;; + carbon) plugins=/mnt/server/carbon/plugins; config=/mnt/server/carbon/configs ;; *) # Not a failure (§34.4): failing would leave the operator without a game # server over a bridge they may not want yet. The startup skips the @@ -188,6 +251,10 @@ rg_install() { for file in $(jq -r '.files | keys[]' "${manifest}"); do install -m 644 "${work}/runicgateway-rust-plugin/${file}" "${plugins}/${file}" done + # A look, not the bridge: if it cannot be written, say so and finish the install. + if ! rg_popup "${config}"; then + echo "Runic Gateway: could not write the PopupNotifications banner to ${config}; the bridge is installed regardless" + fi # What is installed, readable from the panel's file manager. jq --arg framework "${FRAMEWORK}" --arg installed "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ '{ bundle, protocol, framework: $framework, installed: $installed,