sd-webui-style-organizer:为 Forge 提供可视化 Style Grid 风格管理扩展
KazeKaze93/sd-webui-style-organizer
sd-webui-style-organizer 用可视化 Style Grid 取代 Stable Diffusion WebUI 默认的风格下拉菜单,支持分类浏览、来源过滤、去重 All Sources 视图、收藏与最近使用、拖拽排序、预设与备份导入导出。
Style Grid for Forge
Style Grid replaces the default style dropdown with a fast, visual interface for browsing, selecting, and applying styles in all major Stable Diffusion WebUI builds.
It supports:
- category-based browsing
- source-aware filtering
- deduplicated All Sources view with source picker
- favorites and recent styles
- LoRA cards in a dedicated 🧬 LoRA sidebar view (scanned from disk, read-only)
- drag reorder of selected styles
- presets, backup, import/export
- thumbnail generation/upload and cleanup tools
- optional CivitAI title enrichment for LoRAs (manual fetch)
See CHANGELOG.md for full release history.
Requirements
Minimum WebUI / Forge for this extension: builds that load extension javascript/*.mjs as ES modules (<script type="module">).
| Host | Minimum |
|---|---|
AUTOMATIC1111 stable-diffusion-webui |
1.1.0 and later (v1.1.0) |
| Forge / reForge | Any release (loader inherited from A1111; present from Forge’s first commit) |
Fact check (loader source): A1111 added .mjs as type="module" in commit b90cad7f (2023-02-23), merged to master as e15c4f31 via PR #8042 (2023-03-11). Loader loop (then in modules/ui.py, later moved to modules/ui_gradio_extensions.py):
for script in scripts.list_scripts("javascript", ".mjs"): head += f'<script type="module" src="{webpath(script.path)}"></script>\n'
There is no git tag v1.0.0; pre-release tag v1.0.0-pre (2023-01-24) does not include this loader. The first tagged A1111 release that does is v1.1.0 (72cd27a1, 2023-05-01). Forge (lllyasviel/stable-diffusion-webui-forge, created 2024-01-14) was based on A1111 ~1.7.0-era master and already contained the same .mjs path at its first commit 7ad2a5c1 — no separate Forge .mjs commit. reForge keeps the same modules/ui_gradio_extensions.py loader. Style Grid’s host entry is javascript/style_grid.mjs; sibling modules under javascript/style_grid/ are imported by that entry (only top-level javascript/*.mjs / *.js are auto-injected).
Older hosts that only inject *.js as classic scripts will not run this extension’s host layer.
Installation
Install from URL (recommended)
- Open Extensions in Forge.
- Go to Install from URL.
- Paste this repository URL and install.
- Restart Forge UI.
Manual install
cd /path/to/stable-diffusion-webui-forge/extensions git clone <this-repository-url> sd-webui-style-organizer
Restart Forge UI after cloning.
Quick Start
- Open
txt2imgorimg2img. - Click the Style Grid trigger button to open the panel.
- Pick a source (
All Sourcesor a specific CSV). - Search or browse categories.
- Click a style card to apply/unapply.
- Use the top bar icon buttons (right of the search box) for backup, import/export, compact/collapse, etc. Presets live in the left sidebar.
img2img support
Style Grid works in both generation tabs:
- txt2img
- img2img
Behavior is the same in both modes (source filter, deduplication, favorites/recent, context menus, previews, presets). The small tab badge in the panel header shows the active host context.
Core Workflow
1) Browse and filter styles
- Use the left sidebar for category filtering.
- Use source dropdown for CSV-level filtering.
- Use search box for instant name filtering.
Search and autocomplete
- Type in the search box to filter the grid in real time. Query tokens are AND-matched (whitespace-separated, case-insensitive): every token must appear in the haystack.
- Grid search and autocomplete both use
matchesSearch: style name (including underscore→space forms and display-name forms) plus description, withCombos:/Conflicts:reference lists stripped from the description so those lists do not pollute matches. - Autocomplete suggestions respect the active source filter (with All Sources, suggestions are deduped by name like the grid). Cap: top 8.
- Favorites, Recent, and Presets views also respect search and the active source filter (they previously ignored them).
- Use arrow keys + Enter to pick a suggestion quickly, or click the item with mouse.
2) Apply styles from cards
- Card click toggles selection and applies/unapplies style.
- Selected styles are tracked in the bottom selected bar.
- Reorder selected styles by dragging chips in the selected bar.
3) All Sources and duplicate names (source picker)
- With All Sources selected, the grid shows one card per style name even if that name appears in several CSV files.
- If the same name exists in more than one file, the first click on that card does not toggle the style yet — it opens a small source picker next to the card.
- The picker lists each duplicate as the CSV file name only (no folder path, no
.csvextension). Choose the row you want; that variant is applied like a normal selection. - Click outside the picker to close it without applying.
- If you pick a specific source in the dropdown, you always see that file’s styles only — duplicates from other files are not shown together, so the picker is not used.
4) Favorites, recent, and LoRA
- Favorites: right‑click a style card → Add to Favorites / Remove from Favorites. Favorited tiles show a small star in the top-right corner. Favorites and Recent are stored by name + CSV source, so the same name from two files can be favorited independently.
- Recent lists the last styles you applied (up to 10), grouped by category like the main grid.
- Open Favorites or Recent in the left sidebar to filter the grid to those lists (search + source filter apply here too).
- 🧬 LoRA appears in the sidebar when at least one LoRA was scanned. Cards are synthetic styles (not CSV rows), grouped by sub-folder under your LoRA roots. See LoRA support below.
5) Category context menu: wildcards and previews
Where to open it
- Right-click a category row in the left sidebar (the colored category list). This is the only category context menu.
- Right-clicking a category header inside the main grid (the sticky
▼ CATEGORY (count)row) no longer opens a menu — that duplicate entry point was removed. A normal left-click on the header still collapses/expands the section; Select All on that row is unchanged.
Menu actions
| Item | What it does |
|---|---|
| Wildcard: random (can repeat) → Whole category | Inserts a token into the positive prompt on the Forge side: {sg:<category>}. The category name is normalized to lowercase to match how styles are grouped. |
| Wildcard: random (can repeat) → Pick styles... | Opens a slice-selection mode in the grid: tick individual styles with checkboxes. Search and the source filter keep working; Select all covers only the styles currently visible after filtering. Add as wildcard inserts a slice token for exactly that selection; Cancel discards it. |
| Wildcard: shuffle (no repeats in a batch) → Whole category | Inserts a token into the positive prompt on the Forge side: {sgd:<category>}. The category name is normalized to lowercase to match how styles are grouped. |
| Wildcard: shuffle (no repeats in a batch) → Pick styles... | Opens a slice-selection mode in the grid: tick individual styles with checkboxes. Search and the source filter keep working; Select all covers only the styles currently visible after filtering. Add as wildcard inserts a slice token for exactly that selection; Cancel discards it. |
| Generate previews… | Queues thumbnail generation for styles in that category (batch job in the host). Always shown; does not report a missing-preview count. |
How {sg:…} wildcards work
- Syntax (whole category):
{sg:<category>}— curly braces, the prefixsg:, then the category label as it appears in Style Grid (e.g.ACCESSORYoraccessory). Only this pattern is special; the regex is\{sg:…\}(seestylegrid/wildcards.py). - Syntax (slice):
{sg:<category>:<spec>}. Spec entries are comma-separated style-name suffixes without the category prefix — e.g.{sg:body:Tanned,Shortstack}, notBODY_Tanned. A leading-excludes ({sg:body:-Tanned}= whole category minus that style); a trailing*is a prefix glob ({sg:body:Male_*}). - Slice resolution: include entries are unioned first, then excludes subtract. If every listed name is missing from the CSV, the token falls back to the whole category rather than breaking the prompt. The UI writes whichever form is shortest (so selecting almost an entire category often produces a compact exclude token); the resolver understands all forms regardless.
- When it runs: tokens are expanded at generation time inside Style Grid’s own processing hook (
scripts/style_grid.py), before the rest of the prompt is handled like a normal Forge prompt. - What gets inserted: one random style from the resolved pool (whole category or slice); the replacement text is that style’s
promptfield from CSV (notnegative_prompt). Category matching is case-insensitive. - Source-aware pool: if a specific CSV is selected in the source filter, wildcard replacement picks styles only from that source; with All Sources, it uses the merged style pool.
- Where you can put it: positive or negative prompt box — both strings are scanned. If the category is unknown or empty, the
{sg:…}text is left as-is (no error). - Chips: the selected bar shows each active
{sg:…}as a chip. Slice chips display the real number of styles the token can resolve to, with those style names in the tooltip.
- You can type or paste tokens manually; the context menu only inserts the same formats.
Compatibility with other “wildcard” extensions (e.g. stable-diffusion-webui-wildcards / Dynamic Prompts __file__ style)
- Those stacks usually recognize different syntax — commonly
__name__(double underscores) or other Dynamic Prompts / custom grammar — not{sg:…}. - Style Grid only looks for
{sg:…}and{sgd:…}; other extensions only interpret their patterns. The two do not use the same delimiters, so they do not fight over the same text in normal use. - You do not need the Automatic1111 wildcards extension (or any extra wildcard plugin) for Style Grid’s
{sg:…}feature — it is implemented inside this extension (Pythonresolve_sg_wildcards+ your style CSV data). Other wildcard extensions remain optional for their own__…__/ file-based workflows.
🃏 Shuffle wildcards
A shuffle wildcard draws styles without repeats. Where the random wildcard {sg:category} may pick the same style twice in a batch, {sgd:category} uses every style once before any style comes back.
- Syntax:
{sgd:category}for a whole category,{sgd:category:spec}for a slice. The spec grammar is the same as for{sg:...}(names,-excludes,Root_*globs), and the order of entries does not matter. - Insert: right-click a category and use the Wildcard: shuffle group (Whole category or Pick styles...), or type the token by hand. Shuffle chips have their own icon and can be reordered, removed and saved in presets like any other wildcard.
- Scope of uniqueness: one generation, that is batch count × batch size. A fixed seed repeats the same order, seed -1 gives a new order on every click.
- Several tokens: repeating the same token in one prompt draws a different style for each occurrence.
- Positive and negative prompts: the same shuffle token in both fields resolves to the same style for a given image, as long as it appears the same number of times in each.
- Duplicates across packs: styles with identical prompt and negative prompt count once.
- Random wildcards are unchanged. Mixing
{sg:...}and{sgd:...}in one prompt is fine, they do not influence each other. - Hires fix: the built-in hires pass reuses the first-pass pick. The post-process ✨ button re-reads the prompt field unless the expanded prompt of the selected image is available, so a shuffle token may then resolve to a different style, just as a random wildcard would.
- Presets: shuffle chips are saved in presets. Older versions of the extension load such presets as random wildcards.
6) Style card context menu
Where to open it
- Right-click a style card in the grid (card title hint: Right-click for options). (Left-click still applies the normal select / source-picker rules.)
On the tile (not menu items)
- Star (top-right) appears when the style is favorited.
- Check badge (bottom-right) appears when the style is selected.
Menu actions
| Item | What it does |
|---|---|
| Select / Deselect | Same as a left-click on the card: applies or removes the style from the active selection (and host prompt), without opening the duplicate-source picker. |
| Add to Favorites / Remove from Favorites | Toggles the star list for this style row (name + source); the tile star updates immediately. |
| Copy prompt | Copies this style’s prompt text to the clipboard (toast on success/failure). |
| Edit | Opens the host style editor for this style. |
| Duplicate | Opens the host flow to duplicate the style (typically into the same or chosen source). |
| Move to category... | Opens the host dialog to change the style’s category field. |
| Generate preview (SD) | Runs thumbnail generation for this style (Stable Diffusion–based preview in the host). |
| Upload preview image | Opens the host file picker to set a custom thumbnail image. |
| Remove preview | Shown only when the style already has a thumbnail. Deletes the cached preview for this style’s name + source (surfaces errors instead of silently no-oping). |
| Delete | Removes the style (host confirms and updates CSV). Styles from the shipped samples/ pack cannot be modified via the API (read-only). |
LoRA cards: Edit / Duplicate / Move / Generate preview / Upload preview / Remove preview / Delete are hidden. Select, Favorites, and Copy prompt remain. The server also rejects CSV save/delete for LoRA-sourced rows.
Click outside the menu to close it (moving the pointer off the menu does not dismiss it).
7) Thumbnail previews and the hover popup
Generating a preview
- Per style: open the style’s context menu (right-click the card) → Generate preview (SD). The host runs an SD render and saves an image under
data/thumbnails/(see Data and Persistence). - Per category: use the category context menu → Generate previews… (see §5).
After a successful run, the iframe is notified so the UI can refresh that style’s thumbnail version.
Thumbnail images are loaded via GET /style_grid/thumbnail?name=…&source=… (CSV styles require source = that row’s source_file). Preview URLs may also include a version for browser cache. Generate / upload / delete send the same source identity so duplicate names across CSVs keep separate previews. Generation is queued (job_id); the host polls status and can cancel.
Upgrading from older builds: thumbnails used to be stored under a name-only hash. On first GET /thumbnails/list, a unique name is renamed to the new (name, source) file automatically. If the same style name exists in more than one loaded pack, that old file is not assigned to either pack — regenerate the preview for each.
What the card shows
- The grid card is a compact label (name and category color accent). The generated image is not shown inside the tile — you see it when you hover.
Hover popup (preview window)
- Pause ~300 ms on a card to open the popup (reduces flicker when moving the mouse quickly).
- Top: the thumbnail image (if the file exists and loads). If the image is missing or fails to load, you still get the text below.
- Title: the style display name.
- Prompt: first 120 characters of the style’s
promptcolumn, with...if longer. - Negative: first 60 characters of
negative_prompt, prefixed with − and shown in a muted red tone.
The popup is fixed near the card and flips above or below depending on available space.
8) Fullscreen mode
- Click the Fullscreen button in the top-right corner of the panel to switch between floating window mode and edge-to-edge view.
- Fullscreen gives the grid and sidebar more horizontal space, which is useful for large category lists and long browsing sessions.
- Click the same button again to return to the regular floating panel size.
Top bar (icon buttons on the right)
What this means: not a separate “toolbar” window — it is the top header row of the Style Grid panel: Style Grid title, txt2img/img2img tag, source dropdown, search, then a row of small icon buttons on the right (ToolBtn with lucide icons + tooltips). Hover an icon for its label. Presets are in the left sidebar, not here. There is no Random button.
| Control | What it does |
|---|---|
| Backup CSV | Creates CSV backup snapshot(s) under data/backups/, keeping directory distinction (styles/ vs samples/ vs external paths) so same basenames do not overwrite each other. Failures, HTTP errors, or “nothing to copy” are reported via toasts (see docs/API.md § /backup). |
| Import/Export | Export/import styles, presets, usage. Style import is rejected if any imported name already exists in the library (toast lists collisions); presets still merge. |
| Clear all selected styles | Clears all selected styles in the panel and on the host; restores the user’s base prompt/negative text instead of wiping the textareas. |
| Compact mode | Toggles a denser card layout (aria-pressed when active). |
| Collapse all / Expand all | One control: Collapse all when every category is expanded; Expand all when any category is collapsed. |
| New style | Creates a style in the currently selected CSV. Disabled until a specific (non–read-only) source is selected — tooltip explains why. |
| Fetch LoRA titles | Visible only in the 🧬 LoRA sidebar view. Opt-in: calls CivitAI’s public API using modelId already present in each LoRA’s local .json metadata; caches titles under data/lora_titles.json. Rate-limited; reopen the panel after the run to see updated labels. |
| N selected | Shown when at least one style is selected. | | Style conflicts | When conflicts exist, a pulsing count badge opens a click-to-open popover (not hover-only) listing reasons; each row can remove the conflicting style. | | Fullscreen / Exit fullscreen | Toggles between the floating panel size and edge-to-edge layout. | | Close Style Grid | Closes the Style Grid panel. |
LoRA support
Style Grid can show installed LoRAs as style cards so they search, favorite, and apply/unapply like CSV styles.
| Topic | Behavior |
|---|---|
| Discovery | Best-effort auto-detect of Forge/A1111 LoRA dirs (paths_internal.models_path/Lora, cmd_opts.lora_dir, cmd_opts.lyco_dir), plus optional config/lora_roots.json (gitignored; copy from config/lora_roots.json.example). Roots are merged and deduped by realpath so junctions/symlinks do not double-scan. |
| Scan depth | Fully recursive unless max_depth is set in lora_roots.json. |
| Card identity | Synthetic source_file / source marker __style_grid_lora__. Style keys look like LORA_<stem> (disambiguated with relative folder when stems collide). Apply injects <lora:stem:weight> plus optional activation text from sibling metadata. |
| Metadata | Sibling <stem>.json (A1111/Forge extra-networks / CivitAI Browser+): preferred weight, activation / negative text, description/notes, modelId. |
| Previews | Only real sibling preview files (*.preview.* preferred, else bare image next to the model). Never SD-generated thumbnails for LoRA cards. |
| Sidebar | 🧬 LoRA special view (shown when at least one LoRA was scanned). LoRAs are excluded from the CSV source dropdown and from normal category lists. |
| Titles | Optional 🌐 toolbar action fetches CivitAI model names via GET https://civitai.com/api/v1/models/{id} (manual only; sequential + throttled; retries HTTP 429). Successful titles become display_name on cards/hover. |
| Read-only | UI hides mutate actions; save_style_to_csv / delete_style_from_csv raise if source_file is the LoRA marker. |
| Refresh | Reloading styles (GET /styles / panel refresh) rescans LoRA roots when the scan cache is cold or invalidated. |
Data and Persistence
Generated files are stored in data/:
| File/Folder | Purpose |
|---|---|
data/presets.json |
Saved presets |
data/usage.json |
Usage counters |
data/category_order.json |
Persisted category order |
data/backups/ |
CSV backups |
data/thumbnails/ |
Thumbnail image cache (CSV styles) |
data/lora_titles.json |
Cached CivitAI LoRA titles (by modelId) |
Optional local config (not committed):
| File | Purpose |
|---|---|
config/lora_roots.json |
Extra LoRA scan roots / optional max_depth (see .example) |
Local UI state is also stored in browser localStorage (active source, favorites, recent, compact/collapse preferences).
CSV Format
Primary style fields:
namepromptnegative_prompt- optional metadata:
description,category
Detailed specification: docs/CSV_FORMAT.md.
API and Developer Docs
- API reference:
docs/API.md - Development guide:
docs/DEVELOPMENT.md - V2 React bundle (iframe loading, Zustand
useShallow,selectFilteredStyles):ui/README.md(also overlaps with GET/style_grid/uiindocs/API.md).
Quality checks run locally via .githooks/pre-push (no GitHub Actions). After clone: git config core.hooksPath .githooks.
Remote / share access
When Forge is started with --listen or --share, mutating Style Grid API routes require a per-session X-StyleGrid-Token (injected into the iframe HTML). With --gradio-auth, GET /style_grid/ui and those write routes also require a Gradio login cookie (access-token / access-token-unsecure on Gradio 3.x). Prefer --gradio-auth whenever the UI is reachable beyond localhost. Details: docs/DEVELOPMENT.md § Write-route / Gradio auth.
Corrupt presets.json (or similar data files) returns HTTP 409 and refuses overwrite; restore from the sibling .bak before saving again.
Troubleshooting
| Issue | What to check |
|---|---|
| Panel does not open | Extension enabled + full Forge restart. |
| Styles missing | CSV location/encoding/header correctness. |
| Source picker not shown | Must be in All Sources, and style must exist in multiple CSVs. |
| Order seems wrong | Check active source and category order persistence rules. |
| Thumbnails not appearing | Verify generation/upload status and data/thumbnails/ permissions. CSV preview URLs and generate/upload/delete must include source (source_file). Generation is async by job_id — wait for completion / check cancel. LoRA cards only show sibling preview files on disk. |
| 🧬 LoRA missing in sidebar | No scanned models yet — check Forge LoRA folder / config/lora_roots.json, then reload / reopen the Style Grid panel so /styles rescans. |
| LoRA titles still filenames after 🌐 | Wait for fetch to finish (toast), then reopen the panel so /styles reloads with cached display_name. HTTP 429 means rate limit; retry later (failed entries are retried on the next run). |
| Import fails with “names that already exist” | Rename or remove colliding styles in the export, or delete/rename the existing library entries first. LoRA synthetic names are excluded from the collision check. |
| Cannot edit/delete a sample style | Expected: CSVs under samples/ are read-only. Copy the style into your own styles/ CSV (or another writable source) first. |
| Preset save blocked / 409 | presets.json (or another data file) is corrupt — restore from .bak next to it; Style Grid will not overwrite bad JSON. |
/style_grid/ui returns 401 |
Expected with --gradio-auth until you log in to Gradio (same cookies as the WebUI login). |
Screenshots in this repo
PNG files live under docs/screenshots/ and are named to match sections in this README. If the UI changes, replace the images but keep the same filenames (see docs/screenshots/README.md for a refresh checklist).
License
AGPL-3.0 (LICENSE)
来源:GitHub · github.com













