Files
DemonGatanjieu-Anomalous_Mo…/ARCHITECTURE.md
T

16 KiB

Anomalous Model Browser Architecture

This is the short entry point for maintainers and AI agents. It describes the system map, ownership boundaries, and rules that apply across subsystems. Read only the linked topic document relevant to the task; do not load every document by default.

Reading map

Before implementation, follow AGENTS.md, the canonical development and maintenance rules. GEMINI.md is the Gemini reading entry point.

The prompt studio reads material prompt payloads through web/modules/material_prompt_data.js; list summaries do not contain prompt bodies. See the Material Library contract below.

When changing... Read...
Python routes, storage, paths, metadata, covers, or scan state docs/architecture/backend.md
Browser lifecycle, UI state, localization, media, or graph edits docs/architecture/frontend.md
Update-guide content/versioning or sidebar hover labels docs/architecture/update-guide.md
Workflow Recipes, packages, galleries, Parameter Notebooks, or prompt roles docs/architecture/recipes.md
Material Library snapshots, image parameter details, or reusable node blocks docs/architecture/material-library.md
Model Doctor, provenance hashes, missing-model recovery, or deep scanning docs/architecture/model-resolution.md
Why a current product boundary exists docs/decisions/README.md
Recurring implementation mistakes and post-mortems .agents/logs/ai_lessons.md

README.md is user-facing documentation and CHANGELOG.md is user-facing release history. Neither is the source of truth for internal architecture.

System shape

Anomalous Model Browser is a UI-only ComfyUI extension. It registers no custom nodes (NODE_CLASS_MAPPINGS is empty). ComfyUI loads the Vanilla JavaScript/CSS frontend from web/; the Python package registers /anomalous/ aiohttp routes and performs local filesystem, metadata, recipe, and scan operations.

ComfyUI frontend
  web/main.js
    -> web/modules/*                 UI, graph integration, localization
    -> /anomalous/*                 JSON and bounded media requests

ComfyUI Python server
  __init__.py
    -> api/__init__.py               route registration
    -> api/*.py                      storage and domain operations
    -> scraper.py                    explicit metadata/hash enrichment

User-owned data
  ComfyUI model folders              models and sidecars
  user/.../anomalous_recipes         workflow recipes and recipe assets
  user/.../anomalous_parameters      immutable parameter snapshots
  user/.../anomalous_materials       curated image/workflow material bundles
  user/.../anomalous_notebooks       prompt notes, with legacy originals preserved

The frontend and backend communicate through narrow JSON contracts. The frontend must not infer filesystem authority, and the backend must not depend on DOM or live LiteGraph state.

Ownership map

Backend

  • api/config.py owns configured paths and active model-folder types.
  • api/path_utils.py owns containment, filename validation, and atomic JSON writes; api/utils.py is a compatibility export surface.
  • api/metadata.py owns sidecar and safetensors metadata extraction.
  • api/model_catalog.py, api/model_resolution.py, api/model_metadata.py, and api/model_media.py own model listing, identity recovery, mutation, and covers; api/models.py is a compatibility facade.
  • api/scanner.py and scraper.py own scan orchestration and enrichment.
  • api/workflow_schema.py, api/recipe_schema.py, api/recipe_images.py, and api/recipe_store.py own recipe validation/shaping, images, CRUD, history, and integrity receipts; api/recipes.py is the HTTP facade.
  • api/recipe_packages.py owns bounded inspect-stage-commit package handling.
  • api/parameters.py owns Parameter Notebook persistence and lookup.
  • api/notebooks.py owns Prompt Note persistence and recoverable legacy copying.
  • api/material_schema.py, api/material_assets.py, and api/material_store.py own curated material shaping, private assets, persistence/cache, search and lifecycle; api/materials.py owns HTTP mapping and compatibility entry points.
  • api/media_routes.py, api/gallery_routes.py, api/translation_routes.py, and api/folder_types.py own the formerly mixed utility route families.
  • model_policies.py owns shared backend rename and protected-category policy.
  • model_identity.py owns file SHA-256 evidence shared with the standalone scanner.

Frontend

  • web/main.js coordinates extension registration. browser.js owns the shared browser class and extracted-method wiring; browser_entry.js owns the single browser instance plus floating/topbar/menu entry behavior; and interface_settings.js owns language and theme preferences.
  • ui_sidebar.js creates the browser shell and folder navigation. ui_settings_hub.js owns settings and model-card preferences; ui_toolbox.js owns the tool catalog, fixed shortcut bar, and tool dispatch; ui_browser_navigation.js owns shared panel hiding/cleanup and workspace return. Scan-wizard launch and polling live in ui_scan_wizard.js; folder visibility/order lives in ui_folder_manager.js; and help content lives in ui_help.js.
  • ui_update_guide.js owns the non-intrusive update guide modal (accessible via header icon).
  • sidebar_actions.js owns the sidebar bottom action hover-reveal short labels (100ms), singleton dynamic DOM tooltip bubbles (#anomalous-sidebar-tooltip-bubble, 600ms), click/pointerdown instant text/tooltip suppression guards, isBottomModalOpen tooltip occlusion guards, and anti-flicker pointer stability.
  • tool_registry.js centralizes metadata, SVG icons (enlarged 20px crisp vector outlines with 2px stroke, #cbd5e1 contrast), and stable IDs for the 9 catalog tools (including Prompt Notes / 提示词笔记) and 2 fixed anchors (Toolbox and Settings).
  • shortcut_layout.js provides tool layout utilities and fallbacks. The bottom shortcut bar maintains the clean fixed 4-tool setup (scan, doctor, assistant, materials) plus two anchors (toolbox, settings) housed in prominent 36px buttons with full click/active text suppression and .is-active toggled styling.
  • ui_sidebar.js's Toolbox modal strictly filters out all tools already present on the bottom bar, presenting a sleek 216px 3-row utility catalog with compact, frameless 44px tiles (providing an elevated silhouette with breathing room for catalog discovery), downward anchor caret pointing to the toolbox trigger button, 0.18s smooth spring pop-in animation, clean click action execution, and zero obstructive text or beta footers.
  • ui_grid.js and model-detail modules own model presentation: ui_detail.js coordinates detail display, ui_model_editor.js owns metadata editing, and ui_model_selector.js owns advanced selection. ui_gallery.js and ui_gallery_detail.js own generated-image browsing and workbench lifecycle, with stage interaction in ui_image_stage.js and metadata tabs in ui_image_inspector.js.
  • ui_recipes.js / ui_recipe_detail.js, ui_notebooks.js, and ui_materials.js own their respective workspace surfaces and persistence flows. ui_recipes.js owns the Workflow Recipe studio catalog workspace with search/filter tags, grid/list layout toggle, streamlined action header (preserving active workflow saving and closing while pruning unfinished package import entrypoints), and card browsing. ui_notebooks.js owns Prompt Note catalog, sidebar dual-group management (note list + floor quick jump anchor navigation with scrollspy active tracking), and persistence; ui_notebook_editor.js owns unfolded card editing (modularized into single-responsibility sub-functions adhering to the 50-line rule: sticky top action toolbar, unfolded companion models card, prompt composer with compact inline find & replace toolbar, and flat material library archiving card with clean single-icon feedback and complete bilingual dictionary coverage in locales.js), and notebook_canvas.js owns LiteGraph creation. Prompt Notes are integrated as a standard tool in the Toolbox with defensive workspace return state restoration, TDZ-safe summary initialization, and responsive empty-state fallback rendering. ui_recipe_detail.js coordinates the Workflow Recipe detail session and model composition. ui_recipe_overview.js owns the Overview prompt showcase (with entry.text fallback), and ui_recipe_parameters.js owns the responsive Parameter Presets workspace (featuring default-expanded raw node parameter inspection, a clamp(230px, 24vw, 290px) sidebar with guarded card actions, uncluttered console action bars with deferred status feedback, minmax(130px, 1fr) Bento Grid with universal click-to-copy, LoRA cards with flexbox truncation guards, and sticky editor headers). ui_materials.js owns Material Library discovery and pagination, ui_material_cards.js owns catalog cards, ui_material_detail.js owns the full detail surface, and ui_material_application.js owns selected-node tracking and explicit material application. Within recipe detail, ui_recipe_versions.js owns history comparison/restore, ui_recipe_gallery.js owns result cards and direct Image Detail Workbench handoff, ui_recipe_model_matching.js owns preview resolution and explicit local replacement, ui_recipe_metadata.js owns inline persistence, and ui_recipe_detail_dom.js owns the DOM/copy helpers shared by detail subviews. ui_recipe_catalog.js owns recipe filters and navigation, ui_recipe_cards.js owns cards and card actions, ui_recipe_dialogs.js owns save/edit dialogs, and ui_recipe_media.js owns shared cover helpers.
  • ui_prompt_composer.js owns the standalone Prompt Studio drawer. Its child views are ui_prompt_source_deck.js, ui_prompt_workbench.js, and ui_prompt_inspector.js. Assembly plan data, track-vs-role separation, and cross-role tail smart-sorting are owned by prompt_composition.js and prompt_studio_data.js. ui_prompt_source_deck.js owns the card preview popover with narrow bridging corridors, differentiated hide timers, and fast dismissal when hovering or clicking library blank space.
  • ui_prompt_translator.js owns the standalone translator. Both translator and studio use ui_lifecycle.js for global listeners, request cancellation and resize cleanup. Translation requests go through translation_service.js.
  • ui_dom.js provides small DOM/JSON helpers; material_inspector.js owns material-specific metadata and parameter rendering.
  • ui_doctor.js owns diagnostics and global scans; ui_node_assistant.js owns selected-node assistant history, ui_node_model_picker.js owns native combo replacement, and ui_node_presets.js owns parameter preset rendering and application. model_picker.js, node_material_actions.js, and graph_splice.js own the remaining explicit graph changes.
  • ui_model_sources.js owns the Model Source Hub (模型来源统一中控中心), providing dual-scope (Workflow and Library) source inspection, external platform jumping, canvas Note node generation, workflow.extra.anomalous_model_sources metadata synchronization, automated asynchronous model metadata resolution (resolveWorkflowModelsMetadata) via /anomalous/resolve_paths_to_previews with local sidecar priority detection, and protected read-only link display with deliberate edit-mode unlocking and dirty-state dynamic local persistence (hiding redundant [Save Local] buttons until links are modified).
  • locales.js is the shared runtime string catalog. Existing inline bilingual UI strings remain migration debt; new strings belong in the catalog.
  • styles.css is the ordered import manifest for web/styles/*.css, which own presentation and theme overrides. Color values, dimensions and visual design descriptions are not duplicated as architectural contracts.

Cross-system invariants

These rules are intentionally summarized here and specified in the linked topic documents.

  1. Identity is provenance, not naming. Model Doctor may use a cryptographic hash, exact byte size under the allowed category policy, and target category. Paths, filenames, display names, previews, and fuzzy similarity are never identity evidence.
  2. Filesystem input is untrusted. Backend request paths must pass the shared containment and filename helpers. Checking only for .. is insufficient on Windows and in the presence of alternate separators, UNC paths, or symlinks.
  3. User files are changed transactionally and conservatively. Recipe writes, imports, graph mutations, and sidecar operations validate before mutation and either complete coherently or restore the prior state.
  4. The event loop stays responsive. Recursive walks, hashing, metadata parsing, and other potentially large disk operations run off the aiohttp event loop. UI rendering and media loading are bounded and cancellable.
  5. Host state is preserved. Browser panels are mutually exclusive, Workspace/model-detail transitions are recoverable, and graph edits use ComfyUI's change/callback contracts.
  6. Runtime strings are localized safely. User-visible copy comes from locales.js; dynamic values stay outside dictionaries and enter the DOM as text. Only allowlisted rich content goes through safe_dom.js.
  7. Optional integrations fail locally. Missing graph APIs, metadata, network availability, or an optional resolver may disable that capability but must not make the main extension disappear.
  8. Presentation data is not authority. Covers, thumbnails, summaries, cached names, and workflow fingerprints never replace the authoritative serialized workflow or model provenance record.

Data and compatibility boundaries

  • Runtime settings and newly saved API keys live in api/config.json. scraper.py may read the legacy root config.json only as a compatibility fallback. API keys are not stored in browser localStorage.
  • Recipes, Parameter Notebooks, and Material Library records/assets are user data outside the extension directory. They are never bundled with or silently migrated into the plugin source.
  • The extension may integrate with Civitai and optional translation services only through explicit product behavior. External content and dependencies keep their own terms.
  • Project code and documentation use the MIT license. TRADEMARKS.md separately defines the project-name and official-branding boundary.
  • Internal property names and established routes may remain stable when a user-facing surface is renamed. Do not churn compatibility contracts merely to match presentation wording.

Change and snapshot protocol

Every product-code change should end as one coherent local Git snapshot:

  1. Run checks proportional to the changed behavior.
  2. Update architecture documentation only when the change modifies a module owner, data flow, public/internal interface contract, persistence format, security boundary, or critical invariant.
  3. When architecture changes, update the narrowest relevant topic document. Update this entry point only if the system map, cross-system invariants, or reading map changed.
  4. Do not add an architecture entry merely to say that existing boundaries were unchanged. Ordinary fixes belong in code, tests, Git history, and—when useful to users—CHANGELOG.md.
  5. Record a durable lesson in .agents/logs/ai_lessons.md only for a recurring trap or a critical failure mode, not as a turn-by-turn work log.
  6. Create a local commit after verification. Keep unrelated work out of the snapshot and do not push without explicit user authorization.
  7. Leave a clean worktree, or identify every intentional uncommitted file in the handoff.

Decision records explain enduring choices; they are not a chronological diary. Git history is the authoritative record of implementation changes. Planning-only documents must be clearly labeled as proposals and must not describe unshipped behavior as current architecture.