Files
DemonGatanjieu-Anomalous_Mo…/ARCHITECTURE.md
T

15 KiB
Raw Blame History

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

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
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/utils.py owns containment and filename validation helpers.
  • api/metadata.py owns sidecar and safetensors metadata extraction.
  • api/models.py owns model listing, media serving, and model-facing routes.
  • api/scanner.py and scraper.py own scan orchestration and enrichment.
  • api/recipes.py owns recipe validation, CRUD, history, and integrity receipts.
  • 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/materials.py owns curated material persistence, private image assets, summary search/pagination, editable names/tags, and node-type lookup.
  • 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 is the extension entry and shared browser-instance owner.
  • web/modules/entry_controls.js and shortcut_controls.js own entry modes and native command/keybinding integration.
  • ui_sidebar.js, ui_grid.js, ui_detail.js, and ui_gallery.js own the primary model-browser surfaces, including the frosted-glass capsule parameter inspection button with lightweight vector SVG icons and GPU-accelerated micro-interactions.
  • ui_gallery_detail.js owns the studio modal Image Detail Workbench, multi-dimensional image navigation (keyboard shortcuts, floating glass arrows, left vertical thumbnail rail), streamlined workflow/material action buttons, seamless non-shifting click-to-copy Bento spec tiles with error retry state, keyboard accessible focus indicators, full-width generation parameter saving to Material Library, and segmented Bento parameter inspection with ComfyUI canvas injection (app.loadGraphData).
  • ui_notebooks.js, ui_recipes.js, and ui_recipe_detail.js own Workspace presentation, featuring an integrated 48px Studio topbar (.anomalous-recipe-topbar) with scope filter micro-pills (all, complete, partial), unified search & tag dropdown, and dual view modes (uniform 264px Bento grid & 44px compact list view); eliminates noisy Beta notices and visual bumpiness; cards feature 136px golden-ratio top covers, bottom-left frosted glass base model pills, top-right scope pills, real-time environment readiness indicators (🟢/🟡/🔴), quick spec tags, and one-click canvas load (🚀 载入画布 / 🧩 追加画布). Direct canvas drag-drop ("一拖直达画布") allows dragging any recipe card across the translucent modal directly onto the ComfyUI canvas to immediately load the workflow into a fresh canvas (or append subgraphs), mimicking native ComfyUI image-drop behavior. Recipe Detail Studio View (showRecipeDetail) is completely overhauled: dynamically hides outer .anomalous-recipe-topbar on entry and cleanly restores it on exit to reclaim 100% of the vertical workspace; replaces clumsy text copy buttons with sleek SVG micro-copy icons with instant green checkmark feedback; rebuilds the Parameters Tab (renderRecipeParameters) into a modern Bento Matrix (.anomalous-recipe-bento-grid, steps/CFG/sampler/resolution/denoise/seed tiles, independent base model card, and structured LoRA pill stacks); prompts section (renderPromptSection) is upgraded into full-width streamlined cards eliminating the cramped two-column split, clumsy "调整用途" buttons, and isolated square copy boxes, moving actions into a top-right action tray (save to material + micro copy) and making the role badge interactively clickable; snapshot rail replaces legacy <details> and vertical text badges with smooth hover-reveal micro-actions and a glowing green active pulse dot (.anomalous-preset-active-dot); enforces 48px bottom safety padding to eliminate clipping and overlap.
  • ui_materials.js owns the Workspace Material Library and material CRUD presentation; features an integrated single-row Studio topbar with category micro-pills (all, workflow, params, prompts), unified search with tag filtering, grid/list view mode toggle (persisted to localStorage), and a seamless split-screen side studio panel (.anomalous-material-side-studio); cards feature strict uniform height (248px) with rich micro-skeuomorphic fallbacks for non-image assets (prompt code snippets and 3D asset parameter previews) eliminating visual bumpiness, direct inline title editing, one-click dock to Prompt Mixer (🎛️), floating quick actions, floating status toast feedback, and lazy DOM rendering; delegates image inspection to ui_gallery_detail.js; node_material_actions.js owns shared transactional node application and guarded undo; the library and Node Assistant use ui_material_application.js for the same receipt.
  • ui_prompt_composer.js owns the Prompt Studio Dual-Column Workbench (提示词工坊左右双分栏工作台); replaces the cramped 380px drawer with a dedicated full-width two-column workspace (.anomalous-prompt-workbench): the Left Column hosts the Ready-to-use Prompt Cards Library (成型提示词库), aggregating user-saved prompts from Material Library and curated starter presets, completely eliminating default empty textareas; displays an on-demand inline creation card only when clicking ➕ 新建词卡, and cards support native HTML5 drag-and-drop (dragstart); the Right Column hosts the Assembler & Arranger Stage (顺序编排调音台), providing a high-visibility dropzone (.anomalous-assembly-dropzone) that ingests dragged cards into positive/negative tracks, supports bidirectional drag-and-drop reordering with ghost indicator lines, instant A/B bypass toggles (greyscale dimming without deleting), interactive category pills, one-click Smart Sort (🪄 智能理顺, Base ➔ Style ➔ Subject ➔ Trigger), and a live assembled output deck with real-time word/token counts, whole-deck canvas dragging (bindMaterialDrag onto CLIPTextEncode nodes), direct target node injection toolbar, and seamless return to Material Library; prompt_composition.js provides categorizePromptSnippet, smartSortPromptBlocks, assemblePromptBlocks, as well as backward-compatible text joining and fragment plan composition; api/materials.py validates prompt plans with extended categories (general, specific, base, style, subject, trigger).
  • material_drag.js owns temporary canvas drop listeners, target hit testing and window restoration. During active dragging, the main modal smoothly transitions to full transparency (opacity: 0; pointer-events: none) to fully reveal the underlying ComfyUI canvas, restoring on drop/cancel. Supports both node-targeted drops (materials replace, prompt notes insert text) and whole-canvas drops via dropOnCanvas (recipes load new workflows or append subgraphs).
  • material_inspector.js owns shared image-metadata parsing and node-parameter rendering used by the library and Image Detail Workbench. Node cards feature leading aligned frosted-glass checkboxes with cyan micro-interactions for batch selection. The workbench does not import the library UI.
  • recipe_parser.js, recipe_identity.js, recipe_diff.js, and recipe_actions.js own pure or transactional recipe behavior.
  • ui_doctor.js, model_picker.js, and graph_splice.js own assistant and explicit graph-edit behavior.
  • locales.js is the canonical runtime string catalog; safe_dom.js is the trusted rich-text boundary.
  • hash_resolver.js is optional workflow provenance integration. Failure there must not prevent the main browser from loading.

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.