From a12677c38cbb1374bbf349b124540caa8c1da3f2 Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Sat, 17 Oct 2026 07:39:44 -0700 Subject: [PATCH] docs(ui): prototype branching negative variant history (#193) --- docs/concepts/prompt-variant-timeline.html | 108 +++++++++++++++++++++ tools/preview-prompt-timeline.py | 45 +++++++++ 2 files changed, 153 insertions(+) create mode 100644 docs/concepts/prompt-variant-timeline.html create mode 100644 tools/preview-prompt-timeline.py diff --git a/docs/concepts/prompt-variant-timeline.html b/docs/concepts/prompt-variant-timeline.html new file mode 100644 index 0000000..097c25c --- /dev/null +++ b/docs/concepts/prompt-variant-timeline.html @@ -0,0 +1,108 @@ + + + + + + +Prompt history · ComfyAssets prototype + + + + + +
ComfyAssets/ Prompt Manager / AdminInteractive concept
+
+

Prompt history

One positive prompt. Every negative experiment.

Sample data and illustrative previews.
Everything here stays in this browser tab.

+
+
Positive prompt · unchanged

A remote alpine observatory at blue hour, warm light through glass, dramatic mountain peaks, cinematic composition, atmospheric mist, fine detail

Landscapes
+

Negative variants

Click a red dot to inspect a negative. Small dots are repeat runs. Select a variant for its full run history.
+
+ Positive− Negative variantRunSaved, not run
+ +
+
Select a negative or a run to explore its images.
+
Concept only · No ComfyUI or database connectionTime flows left to right · 3 images per sample run
+
+

Add a negative variant

The positive stays unchanged. A saved variant starts with a hollow dot until its first run.

Leave empty to explicitly use no negative prompt. Existing text is reused.

+

Generated image

+ + + diff --git a/tools/preview-prompt-timeline.py b/tools/preview-prompt-timeline.py new file mode 100644 index 0000000..2510c78 --- /dev/null +++ b/tools/preview-prompt-timeline.py @@ -0,0 +1,45 @@ +"""Serve only the timeline concept and its pinned toolkit assets.""" + +import argparse +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import urlsplit + +ROOT = Path(__file__).resolve().parents[1] +FILES = { + "/": ("docs/concepts/prompt-variant-timeline.html", "text/html; charset=utf-8"), + "/toolkit/core.js": ("web/js/shared/toolkit/core.js", "text/javascript"), + "/toolkit/controls.css": ("web/js/shared/toolkit/controls.css", "text/css"), +} + + +class PreviewHandler(BaseHTTPRequestHandler): + def do_GET(self) -> None: + target = FILES.get(urlsplit(self.path).path) + if target is None: + self.send_error(404) + return + filename, mime = target + body = (ROOT / filename).read_bytes() + self.send_response(200) + self.send_header("Content-Type", mime) + self.send_header("Content-Length", str(len(body))) + self.send_header("Cache-Control", "no-store") + self.send_header("X-Content-Type-Options", "nosniff") + self.end_headers() + self.wfile.write(body) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--bind", default="127.0.0.1") + parser.add_argument("--port", type=int, default=8766) + args = parser.parse_args() + server = ThreadingHTTPServer((args.bind, args.port), PreviewHandler) + print(f"Timeline prototype: http://{args.bind}:{args.port}/", flush=True) + try: + server.serve_forever() + except KeyboardInterrupt: + pass + finally: + server.server_close()