App icon for workflows with an App set up; smaller icons in folder lists
This commit is contained in:
@@ -21,7 +21,7 @@ from aiohttp import web, WSMsgType
|
||||
from .chatlog import ChatLog
|
||||
from .graphdoc import apply_patch, remap_conflicts
|
||||
from .stats import collect_stats
|
||||
from .workspace import Workspace
|
||||
from .workspace import Workspace, has_app
|
||||
|
||||
log = logging.getLogger("ComfyUI-RigShare")
|
||||
|
||||
@@ -285,7 +285,7 @@ class Hub:
|
||||
"updated": int(room.updated), "members": members,
|
||||
"nodes": len((room.doc or {}).get("nodes") or []),
|
||||
"restricted": self.folder_restricted(self.folder_of(room)),
|
||||
"app": room.key.endswith(".app.json")})
|
||||
"app": room.key.endswith(".app.json") or has_app(room.doc)})
|
||||
out.sort(key=lambda r: (-len(r["members"]), -r["updated"]))
|
||||
return out
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ Paths handled here are relative to the *userdata* root, e.g.
|
||||
every path is fully URL-decoded and normalised before it is checked.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import posixpath
|
||||
import re
|
||||
@@ -40,6 +41,13 @@ def norm(path):
|
||||
return posixpath.normpath("/" + path).lstrip("/") if path else ""
|
||||
|
||||
|
||||
def has_app(doc):
|
||||
"""A workflow with an App set up (ComfyUI App mode: chosen inputs or outputs)."""
|
||||
data = (doc or {}).get("extra") if isinstance(doc, dict) else None
|
||||
linear = (data or {}).get("linearData") if isinstance(data, dict) else None
|
||||
return isinstance(linear, dict) and bool(linear.get("inputs") or linear.get("outputs"))
|
||||
|
||||
|
||||
def min_role(a, b):
|
||||
return a if ROLE_RANK[a] <= ROLE_RANK[b] else b
|
||||
|
||||
@@ -51,6 +59,7 @@ class Workspace:
|
||||
self.folders = store._read("folders.json", {}) # "shared/<name>" -> {owner, acl, created}
|
||||
os.makedirs(os.path.join(root, "users"), exist_ok=True)
|
||||
os.makedirs(os.path.join(root, "shared"), exist_ok=True)
|
||||
self._app_cache = {} # absolute path -> (mtime, size, has an App)
|
||||
|
||||
def save(self):
|
||||
self.store._write("folders.json", self.folders)
|
||||
@@ -191,6 +200,24 @@ class Workspace:
|
||||
def _abs(self, rel):
|
||||
return os.path.join(self.root, *[p for p in rel.split("/") if p])
|
||||
|
||||
def _file_has_app(self, full):
|
||||
"""Read a workflow file only when it changed since the last check."""
|
||||
try:
|
||||
st = os.stat(full)
|
||||
except OSError:
|
||||
return False
|
||||
cached = self._app_cache.get(full)
|
||||
if cached and cached[0] == st.st_mtime and cached[1] == st.st_size:
|
||||
return cached[2]
|
||||
try:
|
||||
with open(full, "r", encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
found = '"linearData"' in text and has_app(json.loads(text))
|
||||
except (OSError, ValueError):
|
||||
found = False
|
||||
self._app_cache[full] = (st.st_mtime, st.st_size, found)
|
||||
return found
|
||||
|
||||
def _entry(self, rel, name, is_dir, kind, key, perms):
|
||||
full = self._abs(rel)
|
||||
is_app = not is_dir and name.endswith(".app.json") # saved as an App (ComfyUI App mode)
|
||||
@@ -203,7 +230,7 @@ class Workspace:
|
||||
meta = self.folders.get(folder) or {}
|
||||
entry.update(owner=meta.get("owner"), restricted=bool(meta.get("acl")),
|
||||
manage=self.can_manage(folder, kind, key, perms))
|
||||
if is_app:
|
||||
if is_app or (not is_dir and self._file_has_app(full)):
|
||||
entry["app"] = True
|
||||
entry["deletable"] = self.can_delete(rel, kind, key, perms)
|
||||
return entry
|
||||
|
||||
@@ -390,3 +390,31 @@ def test_listing_inside_a_shared_folder(run):
|
||||
assert (await here(bob, "shared/FaceSwap"))["manage"] is False
|
||||
assert await here(alice, "users/alice") is None
|
||||
run(scenario())
|
||||
|
||||
|
||||
def test_workflows_with_an_app_set_up(run):
|
||||
async def scenario():
|
||||
async with rig_server() as rig:
|
||||
await rig.setup_admin()
|
||||
alice = await rig.add_user("alice", edit=True)
|
||||
h = rig.http
|
||||
with_app = json.dumps({"nodes": [], "extra": {"linearData": {"inputs": [[3, "seed"]], "outputs": [9]}}}).encode()
|
||||
empty_app = json.dumps({"nodes": [], "extra": {"linearData": {"inputs": [], "outputs": []}}}).encode()
|
||||
for name, body in (("faceswap.json", with_app), ("empty.json", empty_app), ("plain.json", b"{}")):
|
||||
await h.post(rig.url(f"/api/userdata/{enc('workflows/users/alice/' + name)}"), data=body, headers=alice)
|
||||
|
||||
async def apps():
|
||||
listing = await (await h.get(rig.url("/rigshare/api/workspace/list?path=users/alice"), headers=alice)).json()
|
||||
return {e["name"] for e in listing["entries"] if e.get("app")}
|
||||
assert await apps() == {"faceswap"}, "only a workflow whose App has inputs or outputs"
|
||||
# Edited on disk: the cached answer follows the file.
|
||||
await h.post(rig.url(f"/api/userdata/{enc('workflows/users/alice/faceswap.json')}"), data=b'{"nodes": []}', headers=alice)
|
||||
assert await apps() == set()
|
||||
|
||||
sock = await rig.ws("alice")
|
||||
await sock.send({"type": "join", "room": "file:workflows/shared/f.json", "name": "f", "doc": json.loads(with_app)})
|
||||
await sock.until("room")
|
||||
presence = [m for m in await sock.drain() if m["type"] == "presence"][-1]
|
||||
assert presence["rooms"][0]["app"] is True
|
||||
await sock.close()
|
||||
run(scenario())
|
||||
|
||||
@@ -204,7 +204,7 @@ body.rs-no-manager [aria-label="ComfyUI Manager"] { display: none !important; }
|
||||
.rs-fb-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; padding: 7px 8px; border: 0; background: none;
|
||||
color: inherit; font: inherit; text-align: left; cursor: pointer; }
|
||||
.rs-fb-main:disabled { cursor: default; }
|
||||
.rs-fb-icon { font-size: 16px; color: var(--rs-muted); width: 18px; text-align: center; flex: none; }
|
||||
.rs-fb-icon { font-size: 13px; color: var(--rs-muted); width: 15px; text-align: center; flex: none; }
|
||||
.rs-fb-icon.folder { color: #f5b942; }
|
||||
.rs-fb-tools-row { display: none; gap: 3px; padding-right: 6px; }
|
||||
.rs-fb-row:hover .rs-fb-tools-row, .rs-fb-row:focus-within .rs-fb-tools-row { display: flex; }
|
||||
|
||||
Reference in New Issue
Block a user