mirror of
https://github.com/usestrix/strix.git
synced 2026-08-25 12:22:37 +02:00
feat(tui): replace Textual with a Go/Bubble Tea interface (#941)
This commit is contained in:
@@ -148,16 +148,16 @@ def _post_json(path: str, payload: dict[str, Any], *, timeout: int) -> tuple[int
|
||||
"""
|
||||
url = f"{_app_url()}{path}"
|
||||
try:
|
||||
response = requests.post(
|
||||
with requests.post(
|
||||
url,
|
||||
json=payload,
|
||||
headers={"Accept": "application/json"},
|
||||
timeout=timeout,
|
||||
)
|
||||
) as response:
|
||||
return response.status_code, _parse_body(response.content)
|
||||
except requests.RequestException as exc:
|
||||
logger.warning("relay request to %s failed: %s", path, exc)
|
||||
raise RelayError("unavailable") from exc
|
||||
return response.status_code, _parse_body(response.content)
|
||||
|
||||
|
||||
def _parse_body(raw: bytes) -> dict[str, Any]:
|
||||
|
||||
+29
-6
@@ -3,16 +3,32 @@
|
||||
import type { ToolRendererProps } from "@/types/events";
|
||||
import { shortPath } from "./utils";
|
||||
|
||||
/** Mirrors the OSS TUI `ViewImageRenderer`: surfaces load errors, otherwise a
|
||||
* compact "view image <path>" line. */
|
||||
const IMAGE_DATA_URI_RE = /data:image\/(png|jpe?g|gif|webp);base64,([A-Za-z0-9+/]+={0,2})/;
|
||||
|
||||
function extractImageDataUri(res: unknown): string | null {
|
||||
let s: string | null = null;
|
||||
if (typeof res === "string") {
|
||||
s = res;
|
||||
} else if (res && typeof res === "object") {
|
||||
const o = res as Record<string, unknown>;
|
||||
if (typeof o.image_url === "string") s = o.image_url;
|
||||
else if (typeof o.url === "string") s = o.url;
|
||||
}
|
||||
if (!s) return null;
|
||||
const m = IMAGE_DATA_URI_RE.exec(s);
|
||||
if (!m || m[2].length < 100 || m[2].length % 4 !== 0) return null;
|
||||
return `data:image/${m[1]};base64,${m[2]}`;
|
||||
}
|
||||
|
||||
/** Renders the view_image result as an inline image, like the strix-app
|
||||
* renderer; falls back to the load-error text when there is no payload. */
|
||||
export default function ViewImageRenderer({ args, result }: ToolRendererProps) {
|
||||
const path = ((args.path as string) ?? "").trim();
|
||||
|
||||
const res = result as Record<string, unknown> | string | null;
|
||||
const imgSrc = extractImageDataUri(result);
|
||||
let error: string | null = null;
|
||||
if (typeof res === "string") {
|
||||
const trimmed = res.trim();
|
||||
// A string result that isn't an image payload or structured data is an error message
|
||||
if (!imgSrc && typeof result === "string") {
|
||||
const trimmed = result.trim();
|
||||
if (trimmed && !trimmed.toLowerCase().startsWith("data:image/") && !trimmed.startsWith("{")) {
|
||||
error = trimmed;
|
||||
}
|
||||
@@ -24,6 +40,13 @@ export default function ViewImageRenderer({ args, result }: ToolRendererProps) {
|
||||
<span className="text-sky-400/80 font-semibold text-sm shrink-0">view image</span>
|
||||
{path && <span className="text-[#888] font-mono text-[13px] break-all">{shortPath(path)}</span>}
|
||||
</div>
|
||||
{imgSrc && (
|
||||
<img
|
||||
src={imgSrc}
|
||||
alt={path || "Tool image output"}
|
||||
className="mt-1.5 max-w-full max-h-96 rounded-lg border border-white/[0.06] object-contain"
|
||||
/>
|
||||
)}
|
||||
{error && <div className="text-red-400/70 text-[13px] mt-1">{error}</div>}
|
||||
</div>
|
||||
);
|
||||
|
||||
File diff suppressed because one or more lines are too long
+6
-6
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6,8 +6,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="color-scheme" content="dark" />
|
||||
<title>Strix Results</title>
|
||||
<script type="module" crossorigin src="./assets/index-CGvQq6oe.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-C3kQ5kk8.css">
|
||||
<script type="module" crossorigin src="./assets/index-DBJ-RJqo.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-DKbLYAbP.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from strix.core.paths import run_record_path
|
||||
from strix.interface.tui.live_view import TuiLiveView
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -41,12 +42,9 @@ def severity_counts(vulns: list[Any]) -> dict[str, int]:
|
||||
def build_run_state(run_dir: Path) -> dict[str, Any]:
|
||||
"""Agent graph + full per-agent event/message stream.
|
||||
|
||||
Reuses the Textual-free ``TuiLiveView`` projection so the viewer and the TUI
|
||||
Reuses the shared ``TuiLiveView`` projection so the viewer and the TUI
|
||||
share one parser for ``agents.json`` + ``agents.db`` and never drift.
|
||||
"""
|
||||
# Imported lazily so importing strix.interface.viewer does not eagerly pull the TUI.
|
||||
from strix.interface.tui.live_view import TuiLiveView
|
||||
|
||||
view = TuiLiveView()
|
||||
view.hydrate_from_run_dir(run_dir)
|
||||
return {"agents": list(view.agents.values()), "events": view.events}
|
||||
|
||||
Reference in New Issue
Block a user