fix(ui): restore workspace after reopen

This commit is contained in:
DemonGatanjieu
2026-08-03 19:16:43 +08:00
parent cc5d9c11db
commit dd80c89068
5 changed files with 44 additions and 3 deletions
+25 -1
View File
@@ -406,4 +406,28 @@
## [2026-08-03] UI ÖØ¹¹Óë¼Ü¹¹¾«¼ò
- **Recipe UI ÏÖ´ú»¯**: ºÏ²¢ÁËÈßÔ Tabs£¬½« Ä£ÐÍÓ븴ÏÖ ºÍ ²ÎÊý ºÏ²¢½øÈë ¸ÅÀÀ Ãæ°å£¬²¢½«µ×²ã²ÎÊýÊÕÈëÕÛµþ×é¼þ£¬¼«´ó¼õÇáÁËÐÅÏ¢ÔëÒô¡£
- **Åä·½¿¨Æ¬ÁÐ±í½µÔë**: ÒÆ³ýÁËÔ­Åä·½ÁÐ±í¿¨Æ¬ÉϵÄÈ«Á¿²ÎÊýչʾ£¬½ö±£Áô·âÃæÍ¼¡¢±êÌâ¡¢±êÇ©ÓëÄ£ÐÍ»ÕÕ£¬²¢Öع¹Á˲Ù×÷°´Å¥²¼¾Ö¡£
- **½»»¥Âß¼­ÐÞ¸´**: ·ÏÆúÁ˼«Ò׳ö Bug µÄ Quick Queue Âß¼­£¬Í³Ò»ÁË¡º´ò¿ªµ½»­²¼¡»Ó롺׷¼Óµ½»­²¼¡»µÄÂß¼­·â×°¡£
- **½»»¥Âß¼­ÐÞ¸´**: ·ÏÆúÁ˼«Ò׳ö Bug µÄ Quick Queue Âß¼­£¬Í³Ò»ÁË¡º´ò¿ªµ½»­²¼¡»Ó롺׷¼Óµ½»­²¼¡»µÄÂß¼­·â×°¡£
␍
## [Snapshot] 2026-08-03 - Recipe schema v5 origin extraction and rendering␍
**Implemented**␍
- Separated model physical filenames from official C-site origin names.␍
- Updated get_metadata to export model_id and version_id.␍
- Recipes now store origin info when saving/updating.␍
- UI prioritizes origin.model_name and provides a Civitai link if available.␍
- Added refresh identities button for older recipes.␍
- Localized identity phrases to be more human-centric.␍
## [Snapshot] 2026-08-03 - Fix Workspace reopen blank state and recipe identity compatibility
**Implemented**
- Rehydrated the notebook body and active section when reopening an already initialized Workspace after close.
- Kept recipe body hidden while reopening the default notebook section, preventing an empty Workspace overlay.
- Made recipe model-reference enrichment accept both the new identity-plus-origin helper result and the legacy identity-only contract.
**Validation**
- `node --check web/main.js web/modules/ui_notebooks.js web/modules/ui_sidebar.js web/modules/ui_recipes.js`
- `python_embeded/python.exe -m py_compile api/recipes.py api/metadata.py`
- `node tests/recipe_parser_roundtrip.mjs`
- `python_embeded/python.exe tests/test_recipe_roundtrip.py`
+4
View File
@@ -13,6 +13,10 @@ Opening a detail panel should capture the list's semantic location and viewport,
## 58. Overlay Close Must Restore Its Covered Panel
An overlay that only toggles its own visibility can reveal a blank host surface if the underlying panel was hidden by an earlier navigation path. Record the covered panel at overlay entry and restore it through one shared close function. Keep a safe visible fallback, such as the main grid, for stale or missing return state.
## 59. Reused Overlay DOM Must Be Rehydrated on Reopen
When an overlay is closed by hiding both its shell and its child sections, the already-initialized fast path must restore the active child section before returning. Refreshing data alone does not make a hidden DOM subtree visible and can produce a blank overlay on the second open.
这份文档旨在记录 Anomalous 插件开发过程中的架构演进、UX 设计教训以及核心 Bug 排查经验。
---
+3 -1
View File
@@ -2,6 +2,8 @@
This document provides a high-level overview of the Anomalous Model Browser plugin for ComfyUI. It is designed to quickly onboard new AI agents or developers to the project's structure, design philosophy, and critical subsystems.
Workspace lifecycle note: reopening an already initialized Workspace must restore the visible notebook body and reset the hidden recipe body before refreshing data. Hiding the overlay alone is not enough because the next open reuses the existing DOM tree.
## Current recipe interaction correction (2026-08-03)
The recipe detail view keeps Overview, Parameters, and Versions tabs. The Overview contains the model composition blocks; each resolved model name or preview is an explicit entry into the browser model detail view. Model previews are demand-loaded when Overview opens, and returning from model detail restores the recipe detail view. The model browser stores the active grid folder and scroll coordinates before opening detail and restores them when returning to the grid. List-card Append actions await the shared canvas transaction before reporting success or restoring the button state. Export confirmation uses three states (cancel, exclude, include); cancellation must stop the export flow rather than silently becoming an exclusion.
@@ -112,7 +114,7 @@ Instead of fragmenting the class scope and losing context, we extracted all UI p
* **Detail Panel (`anomalous-detail-panel`)**: Slides out when a specific model is clicked.
* **Gallery Viewer (`ui_gallery.js`)**: A fullscreen modal for viewing images.
* **Prompt Notes (`ui_notebooks.js`, legacy internal name)**: A specialized editor for composing prompts and drag-dropping LoRA/model nodes. Its shared user-facing surface is **Workspace / 创作工作台**, with **Prompt Notes / 提示词笔记** and **Workflow Recipes / 工作流配方** as its two sections. This presentation rename intentionally leaves notebook routes, storage, and internal property names stable for compatibility.
* **Workflow Recipes (`ui_recipes.js`, `ui_recipe_detail.js`)**: A second section inside the shared Workspace modal, not a separate top-level navigation surface. Recipes save the complete serialized graph and add two summary layers: adapters for common model/LoRA/prompt/sampling semantics, plus bounded generic widget summaries for every node (including third-party nodes). Save captures the serialized graph exactly once before the dialog; that snapshot, not a later live-canvas serialization, is the workflow sent to the API. Schema v3 introduced a canonical SHA-256 workflow fingerprint and explicit model-reference identity records; schema v4 adds optional recipe-owned model-preview snapshot descriptors. The card API stays light; the full graph and history are fetched only after edit, restore, or “View details.” The detail panel owns Overview and Versions tabs. The 'Models' and 'Parameters' tabs were consolidated into the Overview (with Parameters neatly folded into `<details>`) to reduce cognitive load and avoid state desyncs. The save dialog may pin any safe JSON-serializable widget, restores only pins still present on a canvas update, bind an output cover, and explicitly retain bounded model-preview snapshots. The edit dialog resolves safe primitive values from the full workflow by node ID/widget index before updating the matching serialized slot; bounded summaries are never the edit source. Structural edits are explicitly loaded onto the canvas and saved back through the same update path. Every update archives the previous full recipe locally (bounded to 20 versions). Missing-node preflight also reads authoritative workflow node types. `recipe_parser.js` never mutates the live canvas.
* **Workflow Recipes (`ui_recipes.js`, `ui_recipe_detail.js`)**: A second section inside the shared Workspace modal, not a separate top-level navigation surface. Recipes save the complete serialized graph and add two summary layers: adapters for common model/LoRA/prompt/sampling semantics, plus bounded generic widget summaries for every node (including third-party nodes). Save captures the serialized graph exactly once before the dialog; that snapshot, not a later live-canvas serialization, is the workflow sent to the API. Schema v3 introduced a canonical SHA-256 workflow fingerprint and explicit model-reference identity records; schema v4 adds optional recipe-owned model-preview snapshot descriptors. Schema v5 separates model identity from C-site Official origin metadata (model_name/model_url), ensuring UI rendering prioritizes official names over obfuscated physical filenames. The card API stays light; the full graph and history are fetched only after edit, restore, or “View details.” The detail panel owns Overview and Versions tabs. The 'Models' and 'Parameters' tabs were consolidated into the Overview (with Parameters neatly folded into `<details>`) to reduce cognitive load and avoid state desyncs. The save dialog may pin any safe JSON-serializable widget, restores only pins still present on a canvas update, bind an output cover, and explicitly retain bounded model-preview snapshots. The edit dialog resolves safe primitive values from the full workflow by node ID/widget index before updating the matching serialized slot; bounded summaries are never the edit source. Structural edits are explicitly loaded onto the canvas and saved back through the same update path. Every update archives the previous full recipe locally (bounded to 20 versions). Missing-node preflight also reads authoritative workflow node types. `recipe_parser.js` never mutates the live canvas.
* **Recipe interaction extensions (`recipe_diff.js`, `recipe_actions.js`)**: Recipe cards filter only lightweight name/notes/tags metadata. Version comparison fetches one validated historical JSON and returns bounded semantic changes without instantiating or mutating either graph. Append clones serialized nodes into the live graph with collision-free IDs, transactional link remapping, placement, selection, and rollback. Card and detail actions use a shared busy/restore discipline so a network or canvas failure cannot leave a button permanently disabled or trigger duplicate mutations. Open/Append UI actions share the same exported handlers from `ui_recipe_detail.js`. All Quick Queue functionality was removed to enforce a single execution path through the canvas.
* **Recipe detail presentation**: Copy actions use visible temporary success/failure feedback. Name, notes, and tags can be edited inline from the loaded detail view and are persisted through the existing full-recipe update contract, preserving the serialized workflow and history behavior. Workflow fingerprints and model SHA-256 values are advanced information shown only inside collapsed disclosure panels; card and detail surfaces use bounded glass-style cards, responsive grids, and restrained hover transitions. The Overview separates base model and each LoRA into one full-width composition block; sampling values remain in a compact key-value grid.
* **Doctor Panel (`ui_doctor.js`)**: Diagnoses model health for individual nodes or the entire workflow. Includes "View Profile" functionality.
+7 -1
View File
@@ -183,7 +183,13 @@ def _build_model_references(recipe):
saved_value = values[widget_index] if widget_index < len(values) else None
if not isinstance(saved_value, str) or not saved_value.strip():
continue
identity, origin = _identity_for_reference(saved_value)
identity_result = _identity_for_reference(saved_value)
if isinstance(identity_result, tuple):
identity, origin = (identity_result + (None,))[:2]
else:
# Keep compatibility with older callers/tests that provide the
# pre-origin helper contract and return identity only.
identity, origin = identity_result, None
ref_dict = {
"node_id": node.get("id"),
"node_type": _node_type(node) or "Unknown",
+5
View File
@@ -45,6 +45,11 @@ export function closeWorkspace() {
export async function showNotebooks() {
if (this.nbInitialized) {
this.nbPanel.style.display = 'flex';
if (this.notebookBody) this.notebookBody.style.display = 'flex';
if (this.recipeView) this.recipeView.style.display = 'none';
this.notebookNotesTab?.classList.add('active');
this.notebookRecipesTab?.classList.remove('active');
this.refreshNotebooks(true);
return;
}