From f95ecfca17d7b2641a7eb38888c74d0db331561d Mon Sep 17 00:00:00 2001 From: Sterling Archer Date: Thu, 16 Jul 2026 16:48:49 -0700 Subject: [PATCH] Expose GitHub Sync credentials (username/PAT/repo/branch) via the web UI by moving them into CrossPointSettings, and fix Lockscreen incorrectly triggering on silent internal reboots (File Transfer/OPDS exit) instead of only on real wake/power-on --- README.md | 5 --- plugins/githubsync/GitHubSync.cpp | 43 ++++++++++++++------------ plugins/githubsync/plugin.py | 51 ++++++++++++++++++++++++++++--- plugins/lockscreen/plugin.py | 7 ++++- 4 files changed, 75 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 7016521..fa960d0 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,6 @@ A plugin system for customizing and extending https://github.com/crosspoint-reader/crosspoint-reader firmware on your xteink device. Plugins are applied as source-level patches before the firmware is compiled and flashed. -> **Note on this revision:** the plugin installer was rewritten from independent per-plugin `patch.py` scripts (which broke when installed as anything other than "all of them, in this exact order") to a shared declarative framework - see [Why plugin.py instead of patch.py](#why-pluginpy-instead-of-patchpy). Every plugin now installs correctly alone or in any combination. A few honest notes on functional gaps found and decisions made along the way: -> - **Smaller Fonts** actually only implements two states (Disabled/Enabled - one size step down) even though the UI text below describes three; the underlying code never distinguished a "Smallest" tier or handled OpenDyslexic. This rewrite matches what the code actually does rather than inventing the missing tier. -> - **Bookerly** generates 12/14/16/18pt only; the 8pt/10pt claim below wasn't actually wired up in the original code either. Bookerly also won't appear in the font picker on devices that also have SD-card custom fonts installed (a narrow pre-existing edge case). -> - **GitHub Sync**'s original boot hook only ran inside one rare boot path (quick-resume with no cached frame), so it silently never synced on most ordinary boots - it's now a proper once-per-boot hook. Its install-time NVS credential pre-seeding was dropped for simplicity; configure it via Settings → Plugins → GitHub Sync after flashing instead. - ## Plugins ### Dark Mode diff --git a/plugins/githubsync/GitHubSync.cpp b/plugins/githubsync/GitHubSync.cpp index e15c1f2..f7c0261 100644 --- a/plugins/githubsync/GitHubSync.cpp +++ b/plugins/githubsync/GitHubSync.cpp @@ -1,41 +1,44 @@ #include "GitHubSync.h" #include #include -#include #include #include +#include "CrossPointSettings.h" #include "SDCardManager.h" -#define GH_PREFS_NS "github_sync" -#define GH_KEY_USER "username" -#define GH_KEY_PAT "pat" -#define GH_KEY_REPO "repo" -#define GH_KEY_BRANCH "branch" #define GH_SHA_DIR "/.crosspoint/github_sha/" #define GH_BOOKS_DIR "/" #define GH_SLEEP_BMP "sleep.bmp" #define GH_SLEEP_PATH "/sleep.bmp" #define GH_API_BASE "https://api.github.com" +namespace { +void setField(char* field, size_t fieldSize, const std::string& value) { + strncpy(field, value.c_str(), fieldSize - 1); + field[fieldSize - 1] = '\0'; +} +} // namespace + +// Config now lives in CrossPointSettings (persisted via SETTINGS.saveToFile()/ +// loadFromFile(), same as every other plugin setting) instead of a private NVS +// namespace, so it's reachable through the standard settings list - which is +// what makes it show up in the web UI's Settings page alongside Dark Mode, +// Smaller Fonts, etc. On-device editing via GitHubSyncSettingsActivity is +// unaffected since it only goes through this struct-based API. bool GitHubSync::loadConfig(GitHubSyncConfig &cfg) { - Preferences prefs; - prefs.begin(GH_PREFS_NS, true); - cfg.username = prefs.getString(GH_KEY_USER, "").c_str(); - cfg.pat = prefs.getString(GH_KEY_PAT, "").c_str(); - cfg.repo = prefs.getString(GH_KEY_REPO, "xteink").c_str(); - cfg.branch = prefs.getString(GH_KEY_BRANCH, "main").c_str(); - prefs.end(); + cfg.username = SETTINGS.githubUsername; + cfg.pat = SETTINGS.githubPat; + cfg.repo = SETTINGS.githubRepo[0] ? SETTINGS.githubRepo : "xteink"; + cfg.branch = SETTINGS.githubBranch[0] ? SETTINGS.githubBranch : "main"; return !cfg.username.empty() && !cfg.pat.empty(); } void GitHubSync::saveConfig(const GitHubSyncConfig &cfg) { - Preferences prefs; - prefs.begin(GH_PREFS_NS, false); - prefs.putString(GH_KEY_USER, cfg.username.c_str()); - prefs.putString(GH_KEY_PAT, cfg.pat.c_str()); - prefs.putString(GH_KEY_REPO, cfg.repo.c_str()); - prefs.putString(GH_KEY_BRANCH, cfg.branch.c_str()); - prefs.end(); + setField(SETTINGS.githubUsername, sizeof(SETTINGS.githubUsername), cfg.username); + setField(SETTINGS.githubPat, sizeof(SETTINGS.githubPat), cfg.pat); + setField(SETTINGS.githubRepo, sizeof(SETTINGS.githubRepo), cfg.repo); + setField(SETTINGS.githubBranch, sizeof(SETTINGS.githubBranch), cfg.branch); + SETTINGS.saveToFile(); } bool GitHubSync::isConfigured() { diff --git a/plugins/githubsync/plugin.py b/plugins/githubsync/plugin.py index fdbc36c..64ee3d4 100644 --- a/plugins/githubsync/plugin.py +++ b/plugins/githubsync/plugin.py @@ -3,7 +3,7 @@ import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) from framework.manifest import ( - PluginManifest, SourceFile, Include, SettingActionEnumValue, PluginsTabEntry, MainHook, + PluginManifest, SourceFile, Include, SettingsField, SettingActionEnumValue, PluginsTabEntry, MainHook, ) # NOTE on scope: the original patch.py also pre-provisioned GitHub credentials @@ -11,10 +11,16 @@ from framework.manifest import ( # generated nvs.csv + `esptool ... write-flash 0x3000`), so users wouldn't # have to type a PAT on-device. That's flash-time, not source-patch-time # logic, and GitHubSyncSettingsActivity already lets you configure -# username/PAT/repo/branch on-device (or you can add web-UI editing later), -# so this rewrite drops the NVS pre-seeding step for simplicity/safety and -# has you configure GitHub Sync via Settings -> Plugins -> GitHub Sync after -# first boot instead. Ask if you want the NVS pre-seeding restored. +# username/PAT/repo/branch on-device, so this rewrite drops the NVS +# pre-seeding step for simplicity/safety and has you configure GitHub Sync +# via Settings -> Plugins -> GitHub Sync after first boot instead. Ask if you +# want the NVS pre-seeding restored. +# +# Config now lives in CrossPointSettings (char fields below) rather than a +# private NVS namespace, so it also shows up as editable text fields in the +# web UI's Settings -> Plugins section, same mechanism as Hardcover's API +# token - GitHubSync.cpp's loadConfig()/saveConfig() read/write these fields +# directly instead of the old Preferences-based store. # # Also fixed in this rewrite: the original main.cpp hook only ran # GitHubSync::sync() inside one narrow boot branch (quick-resume-with-no- @@ -38,6 +44,12 @@ def get_manifest(ctx): Include("GitHubSyncSettingsActivity.h", "settings_activity_cpp"), Include("activities/settings/GitHubSync.h", "main_cpp"), ], + settings_fields=[ + SettingsField('char githubUsername[64] = "";'), + SettingsField('char githubPat[256] = "";'), + SettingsField('char githubRepo[64] = "xteink";'), + SettingsField('char githubBranch[32] = "main";'), + ], setting_actions=[ SettingActionEnumValue("GitHubSync"), ], @@ -49,6 +61,35 @@ def get_manifest(ctx): action_value_text="Sync", activity_launch_expr="std::make_unique(renderer, mappedInput)", ), + # show_on_device=False: the device already edits these through the + # GitHub Sync activity above, so a second on-device row would just + # be redundant. hidden_from_web is left at its default (False) so + # they show up as text fields in the web UI's Plugins section. + PluginsTabEntry( + label="GitHub Username", + kind="string", + key="githubUsername", + show_on_device=False, + ), + PluginsTabEntry( + label="GitHub Personal Access Token", + kind="string", + key="githubPat", + obfuscated=True, + show_on_device=False, + ), + PluginsTabEntry( + label="GitHub Repo", + kind="string", + key="githubRepo", + show_on_device=False, + ), + PluginsTabEntry( + label="GitHub Branch", + kind="string", + key="githubBranch", + show_on_device=False, + ), ], main_hooks=[ MainHook( diff --git a/plugins/lockscreen/plugin.py b/plugins/lockscreen/plugin.py index e99cbee..a0063c6 100644 --- a/plugins/lockscreen/plugin.py +++ b/plugins/lockscreen/plugin.py @@ -73,7 +73,12 @@ def get_manifest(ctx): " {\n" " const auto lsMode = static_cast(SETTINGS.lockscreenMode);\n" " const bool hasPinSet = SETTINGS.lockscreenPinHash[0] != '\\0';\n" - " const bool shouldLock = hasPinSet && LockscreenPlugin::shouldLock(lsMode);\n" + " // BootResume::Silent covers internal silentRestart() cycles used to\n" + " // reset WiFi/webserver state (e.g. leaving File Transfer or the OPDS\n" + " // browser) - the user never actually slept, so don't lock for those.\n" + " // QuickResume (waking from sleep) and Splash (real power-on) still lock.\n" + " const bool shouldLock = hasPinSet && resume != BootResume::Silent &&\n" + " LockscreenPlugin::shouldLock(lsMode);\n" " if (shouldLock) {\n" " LockscreenActivity lockAct(renderer, mappedInputManager, LockscreenActivity::Purpose::UNLOCK);\n" " lockAct.onEnter();\n"