From ec190375fa3e0f9153cd96ec05c26ce9729d9abe Mon Sep 17 00:00:00 2001 From: KHA Entertaiment Date: Sun, 22 Mar 2026 12:34:25 -0700 Subject: [PATCH 1/2] feat: adaptive restore UX and emoji fixes --- src/ocbs/serve.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ocbs/serve.py b/src/ocbs/serve.py index 7fec8fe..017a468 100644 --- a/src/ocbs/serve.py +++ b/src/ocbs/serve.py @@ -362,6 +362,14 @@ def _get_html_page(self, token: str, checkpoint_info: dict, expires_at: datetime step1_button_text = "Step 1: I received this - start changes" step1_button_disabled = "" work_underway_display = "none" + + restore_btn_text = "Restore Backup & Restart" + if escaped_scope == "config": + restore_btn_text = "Restore Configuration & Restart" + elif "workspace" in escaped_scope: + restore_btn_text = "Full Restore & Restart" + elif "session" in escaped_scope: + restore_btn_text = "Restore Config + Sessions & Restart" if is_restored: status_message = """ @@ -615,7 +623,7 @@ def _get_html_page(self, token: str, checkpoint_info: dict, expires_at: datetime @@ -624,7 +632,7 @@ def _get_html_page(self, token: str, checkpoint_info: dict, expires_at: datetime From 651396370b16b426ed19ee91ff088db5328d714d Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 23:59:34 +0000 Subject: [PATCH 2/2] fix: apply CodeRabbit auto-fixes Fixed 2 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit --- pyproject.toml | 4 +-- src/ocbs/serve.py | 84 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 62 insertions(+), 26 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c64cdb8..d7aa674 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ dependencies = [ ] [project.optional-dependencies] -clawhub = "clawhub>=0.1.0" +clawhub = ["clawhub>=0.1.0"] [project.scripts] ocbs = "ocbs.cli:main" @@ -32,4 +32,4 @@ dev = [ [tool.pytest.ini_options] testpaths = ["tests"] -python_files = ["test_*.py"] +python_files = ["test_*.py"] \ No newline at end of file diff --git a/src/ocbs/serve.py b/src/ocbs/serve.py index 017a468..57a8483 100644 --- a/src/ocbs/serve.py +++ b/src/ocbs/serve.py @@ -363,13 +363,52 @@ def _get_html_page(self, token: str, checkpoint_info: dict, expires_at: datetime step1_button_disabled = "" work_underway_display = "none" - restore_btn_text = "Restore Backup & Restart" - if escaped_scope == "config": - restore_btn_text = "Restore Configuration & Restart" - elif "workspace" in escaped_scope: - restore_btn_text = "Full Restore & Restart" - elif "session" in escaped_scope: - restore_btn_text = "Restore Config + Sessions & Restart" + # Get raw scope (before HTML escaping) to determine what data exists + raw_scope = checkpoint_info.get('scope', '') + + # Stage 2: Partial restore (config, or config+session if workspace exists) + if raw_scope == "config": + stage2_label = "Restore Configuration & Restart" + elif raw_scope == "config+session": + stage2_label = "Restore Config + Sessions & Restart" + elif raw_scope == "config+session+workspace": + stage2_label = "Restore Config + Sessions & Restart" + else: + stage2_label = "Restore Backup & Restart" + + # Stage 3: Full restore (only for backups with workspace/session data) + has_extended_data = "session" in raw_scope or "workspace" in raw_scope + stage3_label = "Full Restore & Restart" + + # Build stage2 button HTML + stage2_html = f""" +
+ + + +
+ """ + + # Build stage3 button HTML (only if session/workspace data exists) + stage3_html = "" + if has_extended_data: + stage3_html = f""" +
+ + + +
+ """ if is_restored: status_message = """ @@ -626,16 +665,10 @@ def _get_html_page(self, token: str, checkpoint_info: dict, expires_at: datetime ↻ Restart Gateway Only - -
- - -
+ + {stage2_html} + + {stage3_html}

Link expires in {hours}h {minutes}m

@@ -701,13 +734,16 @@ def _get_html_page(self, token: str, checkpoint_info: dict, expires_at: datetime }}); }}); - // Handle restore form - document.querySelector('form[action="/restore"]').addEventListener('submit', function(e) {{ - if (!confirm('Are you sure? This will restore from the checkpoint.')) {{ - e.preventDefault(); - }} else {{ - document.getElementById('restore-spinner').style.display = 'inline-block'; - }} + // Handle restore forms (both partial and full) + document.querySelectorAll('form[action="/restore"]').forEach(function(form) {{ + form.addEventListener('submit', function(e) {{ + const scope = this.querySelector('input[name="restore_scope"]').value; + const spinnerId = (scope === 'full') ? 'full-restore-spinner' : 'restore-spinner'; + const spinner = document.getElementById(spinnerId); + if (spinner) {{ + spinner.style.display = 'inline-block'; + }} + }}); }});