diff --git a/server.py b/server.py index f494bce..f82ff75 100644 --- a/server.py +++ b/server.py @@ -1335,8 +1335,159 @@ PLAN_CACHE_DIR = CACHE_DIR / 'plans' ANALYSIS_CACHE_DIR = CACHE_DIR / 'analysis' +@app.get("/run/{run_id}/plan/node/{step_id}", response_class=HTMLResponse) +async def run_plan_node_detail(run_id: str, step_id: str, request: Request): + """HTMX partial: Get node detail HTML fragment.""" + ctx = await get_user_context_from_cookie(request) + if not ctx: + return HTMLResponse('
Login required
', status_code=401) + + run = await asyncio.to_thread(load_run, run_id) + if not run: + return HTMLResponse(f'Run not found
', status_code=404) + + # Load plan data + plan_data = None + PLAN_CACHE_DIR.mkdir(parents=True, exist_ok=True) + for plan_file in PLAN_CACHE_DIR.glob("*.json"): + try: + with open(plan_file) as f: + data = json.load(f) + plan_inputs = data.get("input_hashes", {}) + if set(plan_inputs.values()) == set(run.inputs): + plan_data = data + break + except (json.JSONDecodeError, IOError): + continue + + if not plan_data: + return HTMLResponse('Plan not found
') + + # Find the step + step = None + for s in plan_data.get("steps", []): + if s.get("step_id") == step_id: + step = s + break + + if not step: + return HTMLResponse(f'Step {step_id} not found
') + + # Get step info + step_name = step.get("name", step_id[:20]) + node_type = step.get("node_type", "EFFECT") + cache_id = step.get("cache_id", "") + config = step.get("config", {}) + level = step.get("level", 0) + input_steps = step.get("input_steps", []) + + # Check for IPFS CID + step_cid = None + if run.step_results: + res = run.step_results.get(step_id) + if isinstance(res, dict) and res.get("cid"): + step_cid = res["cid"] + + has_cached = cache_manager.has_content(cache_id) if cache_id else False + color = NODE_COLORS.get(node_type, NODE_COLORS["default"]) + + # Build preview HTML + preview_html = "" + if has_cached and cache_id: + media_type = detect_media_type(get_cache_path(cache_id)) + if media_type == "video": + preview_html = f''' +{config_json}
+