G1 steps 5-6: input prompts, bookmarks, downloads, Saved Pages

Everything touching the network or the card is a job on the Gemini
task (a missing card can block 5 s, past the main loop's watchdog):
about:start is composed from /gemini/bookmarks.gmi and the Saved Pages
(by capsule, newest first); file:// opens a Saved Page; s, S, r, d, b
and downloads report one line to the URL bar, S with progress Toasts.

A Saved Page is the cached body with a first line "> Saved from <url>
on <date>": it shows as a quote and gives relative links their base;
inside one, links to other Saved Pages open the saved copy, others go
online or say "Not saved, and offline". Input prompts (11 masked)
request the same URL with the answer as its query.

Fixed on the way: re-wrapping indented lines rebuilt the text from its
rows, inserting a space where a long word had been cut; refreshing
loaded the whole Saved Page next to a TLS connection (heap down to 436
bytes), now it reads one line and every fetch checks the 55 KB floor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-05 01:24:29 +02:00
co-authored by Claude Opus 5.5
parent 10f3ff7a4c
commit fd306d5013
7 changed files with 619 additions and 90 deletions
+11 -2
View File
@@ -21,7 +21,7 @@ class GeminiApp : public App {
explicit GeminiApp(GeminiService& gemini) : gemini_(gemini) {}
void onEnter() override;
bool onKey(const KeyEvent& e) override;
bool textEntryActive() const override { return addressOpen_; }
bool textEntryActive() const override { return addressOpen_ || inputOpen_; }
void update(uint32_t nowMs) override;
void draw(Canvas& c) override;
@@ -31,7 +31,11 @@ class GeminiApp : public App {
int topRow;
};
void navigate(const std::string& url, bool remember = true);
void navigate(const std::string& url, bool remember = true, bool preferSaved = false);
void follow(const std::string& link);
std::string titleOf() const;
bool isSaved() const { return !page_.savedFrom.empty(); }
std::string savedPathOf() const { return page_.url.substr(7); } // file://…
void show(GeminiPage&& page);
void showMessage(const std::string& url, const std::string& gemtext);
void layout(Canvas& c);
@@ -59,6 +63,11 @@ class GeminiApp : public App {
LineEditor address_{1024};
std::unique_ptr<DialogModel> certDialog_;
GeminiPage pendingCert_;
std::unique_ptr<DialogModel> deleteDialog_;
bool inputOpen_ = false, inputSensitive_ = false;
std::string inputPrompt_, inputUrl_;
LineEditor input_{1024};
std::string reopen_; // after a refresh: the Saved Page to show again
};
} // namespace roro