studio: seed 25-tab fashion workspace idempotently (guard v3)
Move the workspace-tab seeder out of a hand-edited index.html and into a dedicated branding patcher wired into apply-branding.sh, so it survives a frontend-package reinstall and re-applies on every branding pass. Root cause the seeder addresses: the frontend restores open tabs from localStorage via getStorageValue/setStorageValue, whose clientId-suffixed sessionStorage layer is read BEFORE plain localStorage. A previously-saved 1-tab shadow (Comfy.OpenWorkflowsPaths:<clientId>) masked the seed, and the old guard was burned at v2 so existing profiles never re-seeded. Fix: bump guard v2->v3 (existing profiles re-seed once) and purge the stale sessionStorage shadows on re-seed so the fresh localStorage seed wins even on a same-tab reload. Clean-profile boot now opens all 25 fashion workflows (alongside ComfyUI's own default tab).
This commit is contained in:
@@ -71,7 +71,14 @@ python3 "$BRANDING_DIR/patch_sidebar_logo.py" "$STATIC_DIR"
|
||||
# --- 7. Apply the black + Hanzo-purple theme ---
|
||||
# Installs hanzo-theme.css, injects it as the last stylesheet in index.html, and
|
||||
# rewrites the "dark" palette's canvas clear color to pure black.
|
||||
echo "[7/7] Applying black + purple theme..."
|
||||
echo "[7/8] Applying black + purple theme..."
|
||||
python3 "$BRANDING_DIR/patch_theme.py" "$STATIC_DIR" "$BRANDING_DIR"
|
||||
|
||||
# --- 8. Seed the default 25-tab fashion workspace ---
|
||||
# Injects a <head> bootstrap that pre-populates the localStorage keys the
|
||||
# frontend reads to restore open workflow tabs. Idempotent; bump SEED_VERSION
|
||||
# in patch_workspace_seed.py to force existing profiles to re-seed once.
|
||||
echo "[8/8] Seeding default workspace tabs..."
|
||||
python3 "$BRANDING_DIR/patch_workspace_seed.py" "$STATIC_DIR"
|
||||
|
||||
echo "=== Hanzo Studio branding complete ==="
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Seed the default multi-tab fashion workspace for Hanzo Studio.
|
||||
|
||||
Injects a tiny <head> bootstrap script into index.html that pre-populates the
|
||||
localStorage keys the frontend reads on boot to restore open workflow tabs:
|
||||
|
||||
Comfy.OpenWorkflowsPaths JSON array of workflow paths (one tab each)
|
||||
Comfy.ActiveWorkflowIndex restore split index (0 = open all as trailing tabs)
|
||||
|
||||
Why localStorage: the frontend's restore path (restoreWorkflowTabsState in
|
||||
GraphView useWorkflowPersistence) reads these through getStorageValue()/
|
||||
setStorageValue(), which are localStorage helpers with a clientId-suffixed
|
||||
sessionStorage layer read FIRST. On re-seed we therefore also purge any stale
|
||||
`<key>:<clientId>` sessionStorage shadows so the fresh localStorage seed is
|
||||
authoritative even on a same-tab reload (otherwise a previously-saved 1-tab
|
||||
shadow would mask the seed).
|
||||
|
||||
ComfyUI always opens its own blank "Unsaved Workflow" as the active tab on boot
|
||||
(initializeWorkflow -> loadDefaultWorkflow); the 25 seeded fashion workflows are
|
||||
restored alongside it in the background, so the workspace opens with 26 tabs.
|
||||
There is no seed key that suppresses the default tab: loadPersistedWorkflow only
|
||||
rehydrates drafts / the last-edited graph blob, never a userdata file by path.
|
||||
|
||||
The seed runs at most once per browser profile, guarded by hanzoWorkspaceSeed.
|
||||
Bump SEED_VERSION to force every existing profile to re-seed exactly once.
|
||||
|
||||
Idempotent: re-running strips the previous seed <script> and re-injects the
|
||||
current one, so this is safe to call on every branding pass.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
|
||||
STATIC_DIR = sys.argv[1]
|
||||
INDEX = f"{STATIC_DIR}/index.html"
|
||||
|
||||
SEED_VERSION = "v3"
|
||||
|
||||
# Curated 8-design x 3-pose matrix + the shared 4K upscaler = 25 tabs.
|
||||
DESIGNS = [
|
||||
"festival_fuchsia", "neptune_green", "floralmesh", "stripedgem",
|
||||
"redonepiece", "valentina", "red", "ches",
|
||||
]
|
||||
POSES = ["product", "hover", "shoot"]
|
||||
PATHS = [f"workflows/fashion/{pose}_{d}.json" for d in DESIGNS for pose in POSES]
|
||||
PATHS.append("workflows/fashion/upscale_4k.json")
|
||||
|
||||
# Marker id lets us find & replace our own block idempotently.
|
||||
MARKER = "hanzo-workspace-seed"
|
||||
|
||||
paths_json = "[" + ", ".join('"%s"' % p for p in PATHS) + "]"
|
||||
|
||||
SEED_SCRIPT = (
|
||||
f'<script id="{MARKER}">try{{'
|
||||
f"if(localStorage.getItem('hanzoWorkspaceSeed')!=='{SEED_VERSION}'){{"
|
||||
f"localStorage.setItem('Comfy.OpenWorkflowsPaths',JSON.stringify({paths_json}));"
|
||||
f"localStorage.setItem('Comfy.ActiveWorkflowIndex','0');"
|
||||
f"for(var i=sessionStorage.length-1;i>=0;i--){{var k=sessionStorage.key(i);"
|
||||
f"if(k&&(k.indexOf('Comfy.OpenWorkflowsPaths:')===0"
|
||||
f"||k.indexOf('Comfy.ActiveWorkflowIndex:')===0))sessionStorage.removeItem(k);}}"
|
||||
f"localStorage.setItem('hanzoWorkspaceSeed','{SEED_VERSION}');"
|
||||
f"}}}}catch(e){{}}</script>"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
with open(INDEX, "r", encoding="utf-8") as f:
|
||||
html = f.read()
|
||||
|
||||
# Remove any prior seed block (marked or the original unmarked variant).
|
||||
html = re.sub(r'<script id="%s">.*?</script>' % re.escape(MARKER), "", html,
|
||||
flags=re.DOTALL)
|
||||
html = re.sub(r"<script>try\{if\(localStorage\.getItem\('hanzoWorkspaceSeed'\).*?</script>",
|
||||
"", html, flags=re.DOTALL)
|
||||
|
||||
if MARKER not in html:
|
||||
html = html.replace("<head>", "<head>" + SEED_SCRIPT, 1)
|
||||
|
||||
with open(INDEX, "w", encoding="utf-8") as f:
|
||||
f.write(html)
|
||||
|
||||
print(f" Seeded workspace ({len(PATHS)} tabs, guard {SEED_VERSION}) into index.html")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user