New web UI with iPhone web app
Redesigned status, log, tag generator and settings pages: spool rings in filament colour, live job card (progress and time left read from CC2 Control's /api/printer while a job runs), log page with filters, iOS-style settings with a write/dry-run switch. On phones: bottom tab bar, printer switcher, slot details in a bottom sheet. Dark mode follows the system. Web app manifest and home screen icons for "Add to Home Screen". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014GcEk5tkfFSYD6NTF66NkC
This commit is contained in:
1 parent
67c3b54903
commit
ae44441cfb
16 files changed
+982
-285
No files matched your search
@@ -48,6 +48,7 @@ class FakeCC2:
|
||||
self.slots[4]["printer"] = None
|
||||
self.library: list[dict] = []
|
||||
self.job = {"active": False}
|
||||
self.print_info = {"machine": {"progress": 40}, "print": {"current_layer": 12, "total_layers": 30, "remaining": 600}}
|
||||
self.log: list[dict] = [] # newest first, as CC2 Control lists it
|
||||
self.calls: list[tuple] = []
|
||||
self.fail = False
|
||||
@@ -64,6 +65,10 @@ class FakeCC2:
|
||||
self._check()
|
||||
return copy.deepcopy(self.spools_json())
|
||||
|
||||
def printer(self):
|
||||
self._check()
|
||||
return copy.deepcopy(self.print_info)
|
||||
|
||||
def canvas(self):
|
||||
self._check()
|
||||
return {}
|
||||
|
||||
@@ -759,3 +759,17 @@ def test_multicolour_print_books_each_roll(env):
|
||||
cc2.job = {"active": False}
|
||||
poll(env, 10)
|
||||
assert sorted(uses(env)) == [("use", 5, 6.0), ("use", 6, 3.0)]
|
||||
|
||||
|
||||
def test_job_progress_only_read_while_printing(env):
|
||||
b = env["bridge"]
|
||||
env["cc2"].print_info = None
|
||||
poll(env)
|
||||
assert b.snapshot["printers"][0]["job"] is None
|
||||
env["cc2"].print_info = {"machine": {"progress": 40}, "print": {"current_layer": 12, "total_layers": 30, "remaining": 600}}
|
||||
env["cc2"].job = {"active": True, "file": "/local/cube.gcode", "started": 1,
|
||||
"slots": [{"mm": 0, "grams": 0, "spool": ""}, {"mm": 330, "grams": 1.234, "spool": "s0001"}]}
|
||||
poll(env)
|
||||
job = b.snapshot["printers"][0]["job"]
|
||||
assert job["file"] == "cube.gcode" and job["grams"] == 1.23 and job["slots"] == [{"slot": 1, "grams": 1.23}]
|
||||
assert job["progress"] == 40 and job["layer"] == 12 and job["layers"] == 30 and job["remaining"] == 600
|
||||
+10
-1
@@ -28,11 +28,20 @@ def make_client(tmp_path, env=None):
|
||||
|
||||
def test_pages_render(tmp_path):
|
||||
c, _ = make_client(tmp_path)
|
||||
for path in ("/", "/tags", "/settings"):
|
||||
for path in ("/", "/log", "/tags", "/settings"):
|
||||
r = c.get(path)
|
||||
assert r.status_code == 200 and "CC2" in r.text
|
||||
|
||||
|
||||
def test_web_app_files(tmp_path):
|
||||
c, _ = make_client(tmp_path)
|
||||
m = c.get("/manifest.webmanifest")
|
||||
assert m.status_code == 200 and m.json()["display"] == "standalone"
|
||||
for icon in m.json()["icons"]:
|
||||
assert c.get(icon["src"]).status_code == 200
|
||||
assert c.get("/static/icon-180.png").headers["content-type"] == "image/png"
|
||||
|
||||
|
||||
def test_writes_need_marker_header(tmp_path):
|
||||
c, _ = make_client(tmp_path)
|
||||
assert c.post("/api/settings", json={"dry_run": False}).status_code == 403
|
||||
|
||||
Reference in new issue
Block a user