5 Commits
Author SHA1 Message Date
Nid All 7c837d1ba4 Generate app summary PDF 2026-03-07 13:30:39 +01:00
Nid All 60e84c559c Restore stable PromptStyler node 2026-03-07 12:56:41 +01:00
Nid All d3e3da791d Implement PromptStyler UX plan 2026-03-07 11:23:45 +01:00
Nid All d88930b07a Release 0.3.1 2026-01-31 22:31:58 +01:00
NidAll d52dfdd16b Remove 'Unreleased' section from CHANGELOG 2026-01-31 17:27:05 +01:00
28 changed files with 14814 additions and 566 deletions
+24
View File
@@ -0,0 +1,24 @@
name: ci
on:
pull_request:
push:
branches:
- main
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Version sync
run: python tools/version_sync.py check
- name: Validate styles
run: python tools/validate_styles.py
- name: Audit styles
run: python tools/audit_styles.py
- name: Runtime regression tests
run: python -m unittest discover -s tests
+1 -1
View File
@@ -1,3 +1,3 @@
{
".": "0.2.0"
".": "0.3.2"
}
+24
View File
@@ -6,6 +6,30 @@ The format is based on Keep a Changelog, and this project follows Semantic Versi
## [Unreleased]
## [0.3.2] - 2026-03-07
### Added
- CI checks for version sync, style validation, style audit, and runtime regression tests.
- `tools/sync_legacy_styles.py` and `tools/version_sync.py` for compatibility snapshot and release metadata maintenance.
- New medium-first style packs: `styles/packs/32_print_reprographic.json`, `styles/packs/42_decorative_arts_material_processes.json`, and `styles/packs/43_digital_craft.json`.
- Expanded `styles/packs/36_mixed_media.json` to deepen the mixed-media family with 18 new tactile/process-driven styles.
- `STYLE_TAXONOMY.md` documenting pack purpose, target size, and routing rules for future additions.
- Runtime regression coverage for legacy prompt composition and conditioning validation.
### Changed
- `PromptStyler: Prompt -> Conditioning (Style Picker)` now uses the narrow legacy-compatible prompt-to-conditioning contract again for stability.
- Broken or non-finite conditioning now raises an explicit node error instead of silently returning a malformed output.
- `styles/styles_v1.json` is now treated as a generated compatibility snapshot of merged packs.
- README and maintainer documentation now describe the restored stable-node runtime surface and release workflow.
## [0.3.1] - 2026-01-31
### Added
- New Fine Art artist-reference pack: `styles/packs/34_fine_art_artists.json` (25 styles under `Fine Art/Artists`).
## [0.3.0] - 2026-01-31
### Added
+101 -99
View File
@@ -1,44 +1,26 @@
# ComfyUI PromptStyler
PromptStyler is a **custom ComfyUI node** that helps you keep your prompts consistent by applying a selected “style template” to your **positive prompt**, then outputting **CONDITIONING** for KSampler.
PromptStyler is a custom ComfyUI node pack for applying curated style templates to a positive prompt and returning `CONDITIONING` for KSampler.
If you are new to ComfyUI: you can think of CONDITIONING as the “encoded text prompt” that the sampler uses. PromptStyler builds a *styled prompt string*, encodes it with the model’s text encoder (CLIP), and passes the result to KSampler.
This release intentionally splits the runtime surface into a stable default node and an opt-in advanced node.
## What This Node Does (Exact Behavior)
## Nodes
Node name in ComfyUI:
Stable node:
- `PromptStyler: Prompt -> Conditioning (Style Picker)`
- outputs: `positive`, `styled_prompt`
- behavior: legacy-compatible prompt composition and direct conditioning output
Step-by-step:
Advanced node:
1. You type a normal prompt in `prompt` (your subject + what you want).
2. You pick one style from the dropdown (the node loads style templates from JSON packs).
3. If `apply_style` is enabled:
- It builds a new prompt: `style.prefix + your prompt + style.suffix`
- For `template_variant=default`, it merges comma-separated phrases and de-dupes duplicates.
- For `template_variant=flux_2_klein`, it appends a short prose style guide after your prompt (when available).
4. It tokenizes and encodes the final prompt using the `text_encoder` (CLIP).
5. It outputs:
- `positive` (CONDITIONING) for KSampler
- `styled_prompt` (STRING) so you can see the exact final prompt
- `PromptStyler: Advanced Prompt -> Conditioning`
- outputs: `positive`, `styled_prompt`, `resolved_style_id`, `resolved_style_meta`
- behavior: search, overlays, stronger filtering controls, and JSON debug metadata
Important: styles are written to describe **style only** (medium/process/lighting/palette/texture/composition) and to avoid injecting unrelated objects that you didn’t ask for.
Use the stable node unless you specifically need the advanced controls.
## Install (Beginner Friendly)
1. Copy or clone this repo into:
```text
ComfyUI/custom_nodes/ComfyUI_PromptStyler/
```
2. Restart ComfyUI (or use **Reload custom nodes**).
3. Confirm the node exists by searching for `PromptStyler` in the node menu.
## How To Use (Recommended Wiring)
Typical workflow:
## Recommended Wiring
1. Add `CheckpointLoaderSimple`
2. Add `PromptStyler: Prompt -> Conditioning (Style Picker)`
@@ -47,43 +29,62 @@ Typical workflow:
Connect:
- `CheckpointLoaderSimple.CLIP` -> `PromptStyler.text_encoder`
- Your text -> `PromptStyler.prompt`
- your text source -> `PromptStyler.prompt`
- `PromptStyler.positive` -> `KSampler.positive`
Optional (debugging):
`styled_prompt` is the exact text that was encoded.
- Use the `styled_prompt` output to verify that the style template is doing what you expect.
## Stable Node Inputs
## Inputs / Outputs (Explained)
- `prompt`: your positive prompt
- `apply_style`: bypass styling while still encoding the prompt
- `style`: dropdown entry in `Category | Name | id` format
- `template_variant`: `default` or any model-specific variant present in the packs
- `style_id_override`: direct id selection for newly-added or scripted styles
- `text_encoder`: connect from your checkpoint/model loader
Inputs:
Stable node rules:
- `prompt` (STRING): your prompt text (subject/action/attributes). Works best with comma+space phrases.
- `apply_style` (BOOLEAN): when off, PromptStyler passes your prompt through unchanged (still outputs conditioning).
- `style` (dropdown): pick **one** style. Dropdown entries are `Category | Name | id`.
- `template_variant`:
- `default`: uses comma+space “phrase tokens” (best for SD-like prompt token lists).
- `flux_2_klein`: uses prose guidance when present (helpful for Flux-like models).
- `style_id_override` (STRING): advanced. If set, the node uses that style `id` directly (useful right after adding a new style).
- `text_encoder` (CLIP): wire this from your checkpoint/model loader.
- `style_id_override` takes precedence over the dropdown when set
- `default` uses legacy comma-phrase composition and de-dupe behavior
- model-specific variants use their explicit variant text and otherwise fall back to `default`
- malformed conditioning now raises an explicit node error instead of silently returning a broken output
Outputs:
## Advanced Node Inputs
- `positive` (CONDITIONING): connect to KSampler `positive`.
- `styled_prompt` (STRING): the final prompt string used for conditioning.
Core inputs are the same as the stable node, plus:
## Style Library (Where Styles Live)
- `selection_mode`: `dropdown`, `search`, or `id`
- `style_search`: free-text search used by `selection_mode=search`
- `category_hint`: optional substring filter applied before search scoring
- `tag_hint`: optional comma-separated tag filter applied before search scoring
- `style_strength`: `subtle`, `normal`, or `strong`
- `dedupe_mode`: `smart` or `off`
- `overlay_style_id`: optional secondary style id, restricted to categories starting with `Color Grade`
- `on_missing_style`: `error` or `passthrough`
Styles live in JSON packs:
Advanced node outputs:
- `styles/packs/*.json` (merged at runtime in filename order)
- Legacy fallback (only used if packs are missing): `styles/styles_v1.json`
- `resolved_style_id`: the final base style id that was resolved
- `resolved_style_meta`: JSON payload describing resolution inputs and composition details
Pack file naming:
## Style Library
- Numeric prefixes control merge order (example: `10_cinema.json` loads before `20_photography.json`).
Primary runtime source:
Style entry schema:
- `styles/packs/*.json`
Compatibility snapshot:
- `styles/styles_v1.json`
Pack filenames use numeric prefixes for merge order, for example:
- `10_cinema.json`
- `96_color_grades.json`
- `99_user_custom.json`
Schema:
```json
{
@@ -96,22 +97,11 @@ Style entry schema:
}
```
## Adding Your Own Styles (No Coding Required)
## Adding Your Own Styles
This repo includes a helper tool that writes a custom pack file (gitignored by default):
Default user-local pack:
- Default user pack: `styles/packs/99_user_custom.json`
Wizard (recommended):
```powershell
C:\Comfyui\python_embeded\python.exe .\tools\add_styles.py wizard
```
After adding a style:
- Copy the printed `id`
- Paste it into `style_id_override` to use it immediately (no ComfyUI reload required)
- `styles/packs/99_user_custom.json`
List categories:
@@ -119,65 +109,77 @@ List categories:
C:\Comfyui\python_embeded\python.exe .\tools\add_styles.py categories
```
Add a style directly:
Launch the wizard:
```powershell
C:\Comfyui\python_embeded\python.exe .\tools\add_styles.py wizard
```
Add one style directly:
```powershell
C:\Comfyui\python_embeded\python.exe .\tools\add_styles.py add --name "Ink Noir (Modern)" --category "Fine Art" --core "ink wash, chiaroscuro, high contrast" --details "paper texture, soft bleed, dramatic mood" --tags "ink,noir"
```
## Prompting Tips (So Styles Stay Accurate)
After adding a style, paste the generated id into `style_id_override` to use it immediately.
- Put **subjects/objects** in your `prompt` (the user input), not in the style template.
- Write prompts as comma+space phrases: `", "` is the splitter used by the node’s default template mode.
- If a style is explicitly specific (its name is a scene/subject), it may include those nouns. Otherwise templates aim to stay subject-agnostic.
## Maintainer Commands
## Validate / Audit (Recommended Before Editing Packs)
Validate required fields and uniqueness:
Validate packs:
```powershell
C:\Comfyui\python_embeded\python.exe .\tools\validate_styles.py
```
Optional consistency audit (also flags banned gear terms):
Audit pack consistency:
```powershell
C:\Comfyui\python_embeded\python.exe .\tools\audit_styles.py
```
## Regenerate Curated Packs (For Maintainers)
Sync the legacy compatibility snapshot:
Curated packs are generated by:
```powershell
C:\Comfyui\python_embeded\python.exe .\tools\sync_legacy_styles.py
```
Regenerate curated packs and refresh the legacy snapshot:
```powershell
C:\Comfyui\python_embeded\python.exe .\tools\generate_style_packs.py
```
After regenerating packs, restart ComfyUI (or reload custom nodes) to refresh the dropdown.
Check release/version metadata consistency:
```powershell
C:\Comfyui\python_embeded\python.exe .\tools\version_sync.py check
```
Run runtime regression tests:
```powershell
python -m unittest discover -s tests
```
## Troubleshooting
- Node doesn’t show up:
- Verify the folder path is exactly `ComfyUI/custom_nodes/ComfyUI_PromptStyler/`
- Restart ComfyUI or use **Reload custom nodes**
- Styles missing or dropdown says “(no styles found)”:
- Check that `styles/packs/*.json` exists and is valid JSON
- Run `.\tools\validate_styles.py`
- A style feels like it “adds extra objects”:
- Try `template_variant=default` first (token style)
- Keep your subject strongly stated in the user `prompt`
- If you find a template adding unwanted nouns, it should be fixed in the pack (PRs welcome)
- Stable node returns an error instead of an image:
- inspect `styled_prompt`
- confirm the connected `text_encoder` matches the active model family
- if the error mentions invalid conditioning, compare the same `styled_prompt` through stock `CLIPTextEncode`
- Advanced search fails unexpectedly:
- relax `category_hint` and `tag_hint`
- inspect `resolved_style_meta`
- Styles look stale:
- rerun `tools/sync_legacy_styles.py` or `tools/generate_style_packs.py`
- reload custom nodes in ComfyUI
## Versioning / GitHub Releases (Preparing v0.3.0)
## Release Notes
Source of truth:
Version metadata is managed from `VERSION`.
- Version: `VERSION` (mirrored in `__init__.py` as `__version__`)
- Changelog: `CHANGELOG.md` (Keep a Changelog)
- `__init__.py` reads `VERSION` at runtime
- `.release-please-manifest.json` should be synced from `VERSION` with `tools/version_sync.py write`
- `CHANGELOG.md` follows Keep a Changelog
Suggested release checklist:
1. Run `.\tools\validate_styles.py` and `.\tools\audit_styles.py` (include output in your PR).
2. Ensure `README.md` describes new behavior/inputs.
3. Ensure `CHANGELOG.md` has the release notes under the new version.
4. Push to GitHub and let Release Please create the release PR (see `RELEASING.md`).
See `RELEASING.md` for the maintainer flow.
+39 -14
View File
@@ -1,25 +1,50 @@
# Releasing
This repo uses **Semantic Versioning** and **Release Please** to keep releases organized.
This repo uses Semantic Versioning and Release Please.
## Normal Release Flow (recommended)
## Source Of Truth
1. Merge changes into `main`.
2. The `release-please` workflow opens/updates a **Release PR** that:
- bumps the version
- updates `CHANGELOG.md`
3. Merge the Release PR.
4. Release Please creates a Git tag (e.g. `v0.2.0`) and a GitHub Release.
Human-edited version source:
## Manual Release (if automation is disabled)
- `VERSION`
1. Update `__init__.py` `__version__` and `VERSION` to the new version.
2. Add notes under `CHANGELOG.md` and move them into a new version section.
3. Commit with a message like: `chore(release): vX.Y.Z`
4. Tag and push:
Mirrors and checks:
- `__init__.py` reads `VERSION` at runtime
- `.release-please-manifest.json` is synced from `VERSION` with `python tools/version_sync.py write`
- CI verifies version alignment with `python tools/version_sync.py check`
## Normal Maintainer Flow
1. Update `VERSION`.
2. Run `python tools/version_sync.py write`.
3. Run `python tools/validate_styles.py`.
4. Run `python tools/audit_styles.py`.
5. If packs changed, run `python tools/sync_legacy_styles.py` or `python tools/generate_style_packs.py`.
6. Update `CHANGELOG.md` and any user-facing docs that changed behavior.
7. Merge to `main`.
8. Release Please updates or opens the release PR.
9. Merge the release PR.
## CI
The repository CI job runs on pushes to `main` and on pull requests. It checks:
- version sync
- style validation
- style audit
## Manual Release
If automation is disabled:
1. Update `VERSION`.
2. Run `python tools/version_sync.py write`.
3. Update `CHANGELOG.md`.
4. Commit the release metadata.
5. Tag the commit:
```bash
git tag vX.Y.Z
git push --tags
```
+11 -1
View File
@@ -2,7 +2,7 @@
## Current Library Snapshot (as of 2026-01-31)
- Total styles: 500 (merged from `styles/packs/*.json` in filename order).
- Total styles: 525 (merged from `styles/packs/*.json` in filename order).
- Categories are consistent and already cover the big buckets: Photography, Cinema, Illustration, Fine Art, Graphic Design, 3D/CG, Architecture/Interior, Fashion/Product, Nature, Experimental, Color Grade, plus focused sub-buckets (e.g. `Photography/Alt Process`, `Decorative Arts/Textiles`, `Color Grade/Film Lab`).
- Style format is stable: each style has `id`, `name`, `category`, and `default.prefix`/`default.suffix` (comma+space separated phrases). The node de-dupes phrases case-insensitively.
@@ -40,6 +40,16 @@ When tightening a technique or movement, prefer authoritative glossaries and mus
- Cyanotype process characteristics: National Gallery of Art (USA) and similar museum education references.
- Film noir traits (low-key lighting, high-contrast look, mood conventions): established film history references (e.g., Britannica / major film institutes).
## New Additions: Fine Art/Artists Pack
Added `styles/packs/34_fine_art_artists.json` with 25 artist-referenced styles under `Fine Art/Artists`. Each style is written to stay **subject-agnostic** (palette, brushwork, surface, composition, light) and avoid photo/cinema gear terms.
Research references used while drafting these templates (high quality / museum / encyclopedia sources):
- Encyclopaedia Britannica (artist biographies + movement/technique definitions): https://www.britannica.com/
- The Metropolitan Museum of Art (movement essays and collection learning resources): https://www.metmuseum.org/
- The Museum of Modern Art (artist/movement resources, e.g. action painting / Pollock): https://www.moma.org/
## New Additions: 500-style Expansion Packs
Added four focused packs (25 styles each) to reach 500 total styles:
+50
View File
@@ -0,0 +1,50 @@
# Style Taxonomy
This document defines how curated packs should be organized and expanded.
## Pack Policy
- Default organization is medium/process/material first.
- Target size per pack is 15-25 styles.
- Split a pack when it grows past about 30 styles or starts mixing unrelated subfamilies.
- Preserve existing style ids and visible category labels unless a migration is explicitly planned.
- Extend an existing pack when the new styles share the same medium or production logic.
- Create a new pack only when a coherent process family would otherwise become a grab bag inside an unrelated pack.
## Current Curated Packs
| Pack | Purpose | Categories Covered | Target Range |
| --- | --- | --- | --- |
| `10_cinema.json` | Film still, cinematography, and cinematic look packages | `Cinema` | 15-25 |
| `20_photography.json` | General photographic genres, techniques, and perspective looks | `Photography` | 25-35 |
| `21_photography_alt_process.json` | Darkroom and historical photographic chemistry/process looks | `Photography/Alt Process` | 15-25 |
| `30_illustration.json` | General hand-drawn, painted, manga, vector, pixel, and technical illustration styles | `Illustration`, `Anime/Manga`, `Pixel Art`, `Vector`, `Technical` | 25-35 |
| `31_printmaking_expanded.json` | Historical and fine-art printmaking processes | `Printmaking/Expanded` | 20-30 |
| `32_print_reprographic.json` | Mechanical duplication, proofing, repro, and press-surface looks | `Printmaking/Expanded`, `Photography/Alt Process`, `Graphic Design` | 14-22 |
| `34_fine_art_artists.json` | Artist-reference fine art styles | `Fine Art/Artists` | 20-30 |
| `35_fine_art.json` | Movement and material-led fine art styles | `Fine Art` | 25-35 |
| `36_mixed_media.json` | Layered, tactile, composite-media processes | `Mixed Media` | 20-30 |
| `37_street_art.json` | Street-applied paint, poster, and wall-surface styles | `Street Art` | 12-20 |
| `38_decorative_arts.json` | Core decorative crafts and surface-rich artisan media | `Decorative Arts` | 12-20 |
| `39_internet_aesthetics.json` | Mood-first internet aesthetic families | `Aesthetics/*` | 30-45 |
| `40_graphic_design.json` | Posters, layouts, branding, and print-graphic looks | `Graphic Design` | 25-35 |
| `41_decorative_arts_textiles.json` | Textile, fiber, and woven decorative processes | `Decorative Arts/Textiles` | 20-30 |
| `42_decorative_arts_material_processes.json` | Glass, ceramic, metal, cast, etched, and inlay process styles | `Decorative Arts` | 15-25 |
| `43_digital_craft.json` | Diagrammatic, vector, and constrained-color digital craft styles | `Technical`, `Vector`, `Pixel Art` | 12-20 |
| `50_3d_cg.json` | 3D, render, miniature-set, and CG material looks | `3D/CG` | 15-25 |
| `60_architecture_interior.json` | Architecture and interior presentation looks | `Architecture/Interior` | 15-25 |
| `70_fashion.json` | Fashion/editorial looks | `Fashion` | 12-20 |
| `80_product.json` | Product and commercial object presentation looks | `Product` | 10-18 |
| `90_nature.json` | Landscape and nature presentation looks | `Nature` | 12-20 |
| `95_experimental.json` | Abstract, prismatic, and process-bending visual styles | `Experimental` | 12-20 |
| `96_color_grades.json` | General color grade overlays | `Color Grade` | 15-25 |
| `97_color_grade_film_lab.json` | Film-lab process, scan, and stock-behavior overlays | `Color Grade/Film Lab` | 20-30 |
## Routing Rules For New Styles
- Add tactile layered work to `36_mixed_media.json` before considering any new mixed-media pack.
- Add glass, ceramic, metal, lacquer, and inlay surfaces to the decorative arts family before using `Fine Art` as a fallback.
- Add repro, proofing, duplication, and press artifacts to `32_print_reprographic.json` instead of scattering them across `Graphic Design` or `Experimental`.
- Add `Technical`, `Vector`, and `Pixel Art` growth to `43_digital_craft.json` so those categories stop living as incidental outliers inside the broad illustration pack.
- Use `39_internet_aesthetics.json` only when a style is fundamentally mood/subculture driven rather than process driven.
- Prefer extending an existing process family over creating a new pack with fewer than about 12 coherent styles.
+119
View File
@@ -0,0 +1,119 @@
# PromptStyler Maintainer Notes
This document is for maintainers. It tracks architecture, pack behavior, and the rules that matter when changing runtime style resolution.
## Architecture
Runtime surface:
- `nodes.py`: ComfyUI node definition and input/output wiring
- `style_library.py`: shared style loading, indexing, search, filtering, prompt composition, and legacy snapshot generation
Maintainer tooling:
- `tools/validate_styles.py`: strict schema and uniqueness validation
- `tools/audit_styles.py`: consistency audit and quality checks
- `tools/add_styles.py`: user/custom pack helper
- `tools/generate_style_packs.py`: curated pack generation plus legacy snapshot refresh
- `tools/sync_legacy_styles.py`: regenerate `styles/styles_v1.json` from merged packs
- `tools/version_sync.py`: check or write release metadata derived from `VERSION`
- `STYLE_TAXONOMY.md`: pack purpose, target size, and routing rules for future style additions
Data sources:
- `styles/packs/*.json`: primary runtime source
- `styles/styles_v1.json`: generated compatibility snapshot
## Load Policies
`style_library.py` exposes two load modes:
- `strict`: fail on malformed files or required-field issues
- `lenient`: warn and skip unusable files so the runtime node can stay available
Use `strict` in maintainer tooling and CI.
Use `lenient` in the ComfyUI runtime node.
## Runtime Resolution Model
The node resolves one base style and optionally one overlay style.
Base style resolution modes:
- `dropdown`
- `id`
- `search`
Search ranking order:
1. exact id match
2. exact name match
3. token matches across id, name, category, and tags
Search filters are applied before scoring:
- `category_hint`
- `tag_hint`
If multiple candidates tie for the best score, resolution fails with the top candidates listed.
## Prompt Composition Rules
Recommended composition model:
- base style
- optional color-grade overlay
- optional search-driven style lookup
Default phrase-based variant:
- `style_strength=subtle`: first 3 prefix phrases and first 3 suffix phrases
- `style_strength=normal`: first 6 prefix phrases and first 6 suffix phrases
- `style_strength=strong`: all phrases
- `dedupe_mode=smart`: remove only style-added duplicates, never collapse the raw user prompt
- `dedupe_mode=off`: preserve all style phrases in original order
Overlay rule:
- `overlay_style_id` must resolve to a category starting with `Color Grade`
- the overlay contributes suffix-only styling
Variant rule:
- `template_variant=default` uses phrase-based composition
- model-specific variants use the style's model entry when available and fall back to `default` otherwise
## Pack Authoring Rules
Required fields per style:
- `id`
- `name`
- `default.prefix`
- `default.suffix`
Conventions:
- `id` must be unique and `snake_case`
- phrase-based text should use comma+space separators
- keep style text subject-agnostic unless the style itself is explicitly subject-specific
- avoid photo/cinema gear terms in curated styles
## Release Metadata
Version metadata flow:
- edit `VERSION`
- run `python tools/version_sync.py write`
- CI enforces that `VERSION`, `__init__.py`, and `.release-please-manifest.json` are aligned
## Maintenance Checklist
When changing curated style packs:
1. regenerate or update the packs
2. run `python tools/validate_styles.py`
3. run `python tools/audit_styles.py`
4. run `python tools/sync_legacy_styles.py` if needed
5. update `README.md` when user-visible behavior changed
6. update `CHANGELOG.md`
+1 -1
View File
@@ -1 +1 @@
0.3.0
0.3.2
+12 -2
View File
@@ -5,9 +5,19 @@ Drop this folder into: ComfyUI/custom_nodes/ComfyUI_PromptStyler
Then restart ComfyUI (or use "Reload custom nodes").
"""
__version__ = "0.3.0"
from __future__ import annotations
import os
def _read_version() -> str:
here = os.path.dirname(os.path.realpath(__file__))
with open(os.path.join(here, "VERSION"), "r", encoding="utf-8") as handle:
return handle.read().strip()
__version__ = _read_version()
from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "__version__"]
+1 -1
View File
@@ -277,4 +277,4 @@ NODE_CLASS_MAPPINGS = {
NODE_DISPLAY_NAME_MAPPINGS = {
"PromptStylerConditioning": "PromptStyler: Prompt -> Conditioning (Style Picker)",
}
}
Binary file not shown.
+859
View File
@@ -0,0 +1,859 @@
from __future__ import annotations
import json
import os
import re
from dataclasses import dataclass
from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple
ROOT = os.path.dirname(os.path.realpath(__file__))
LEGACY_STYLES_PATH = os.path.join(ROOT, "styles", "styles_v1.json")
STYLE_PACKS_DIR = os.path.join(ROOT, "styles", "packs")
LOAD_POLICY_LENIENT = "lenient"
LOAD_POLICY_STRICT = "strict"
DEFAULT_VARIANT = "default"
DEFAULT_PHRASE_SEPARATOR = ", "
NO_STYLES_CHOICE = "(no styles found) | (no styles) | __none__"
Z_SEP = ", "
# Shared prompt fragments reused by the generator and user-style tooling.
CATEGORY_BASE_PREFIX = {
"Cinema": (
"cinematic film still",
"professional cinematography",
"cinematic framing",
"carefully composed shot",
"atmospheric depth",
"realistic lighting",
),
"Photography": (
"professional photography",
"realistic lighting",
"natural color rendition",
"coherent shadows",
"true-to-life textures",
),
"Product": (
"commercial product photography",
"controlled studio lighting",
"minimal background",
"controlled reflections",
"clean composition",
),
"Fashion": (
"fashion editorial photography",
"magazine look",
"high-end styling",
"clean composition",
"stylized studio lighting",
),
"Architecture/Interior": (
"architectural photography",
"straight verticals",
"clean geometry",
"wide field of view",
"realistic materials",
"balanced natural light",
),
"Nature": (
"nature photography",
"natural light",
"atmospheric depth",
"fine detail",
"realistic textures",
"environmental realism",
),
"3D/CG": (
"3d render",
"high quality render",
"clean geometry",
"global illumination",
"studio lighting",
"soft shadows",
),
"Graphic Design": (
"graphic design",
"poster-like composition",
"clear hierarchy",
"bold shapes",
"limited palette",
"print-ready layout",
),
"Illustration": (
"detailed illustration",
"cohesive style",
"readable shapes",
"intentional composition",
"handcrafted look",
),
"Anime/Manga": (
"anime/manga illustration",
"clean linework",
"cel shading",
"expressive character design",
"vibrant palette",
),
"Pixel Art": ("pixel art", "16-bit", "limited palette", "pixel-perfect"),
"Vector": ("vector illustration", "flat colors", "clean shapes", "crisp edges", "smooth curves"),
"Technical": ("technical illustration", "clean lines", "diagrammatic layout", "precise geometry", "labels and callouts"),
"Printmaking": ("printmaking", "ink on paper", "handcrafted texture", "carved lines", "high contrast"),
"Experimental": ("experimental aesthetic", "bold composition", "graphic lighting", "textural detail"),
"Color Grade": ("intentional color grade", "filmic tone mapping"),
"Fine Art": ("fine art", "museum-quality", "traditional techniques", "gallery piece", "handcrafted"),
"Mixed Media": ("mixed media artwork", "layered textures", "handcrafted marks", "tactile surface"),
"Street Art": ("street art mural", "spray paint texture", "urban wall", "bold graphic shapes"),
}
CATEGORY_BASE_SUFFIX = {
"Cinema": (
"filmic color science",
"smooth highlight rolloff",
"deep blacks",
"subtle film grain",
"soft halation",
"high dynamic range",
),
"Photography": (
"sharp focus",
"high detail",
"balanced exposure",
"fine texture detail",
"natural contrast",
"clean highlights",
),
"Product": (
"crisp edges",
"clean specular highlights",
"premium look",
"high detail",
"minimal clutter",
),
"Fashion": (
"editorial color grade",
"sharp focus",
"skin texture",
"catchlights",
"high detail",
),
"Architecture/Interior": (
"crisp lines",
"balanced exposure",
"clean shadows",
"high detail",
"material realism",
),
"Nature": (
"natural color",
"clean contrast",
"high detail",
"fine microtexture",
"realistic atmosphere",
),
"3D/CG": (
"clean render",
"high detail",
"sharp edges",
"high resolution",
"clean shading",
),
"Graphic Design": (
"crisp edges",
"high contrast",
"clean alignment",
"balanced negative space",
"print texture",
),
"Illustration": ("cohesive palette", "clean composition", "high detail", "believable texture"),
"Anime/Manga": ("high clarity", "crisp edges", "clean gradients", "sharp eyes"),
"Pixel Art": ("crisp pixels", "dithered shading", "readable silhouette", "sprite-like clarity"),
"Vector": ("clean layout", "consistent stroke weight", "crisp edges", "smooth curves"),
"Technical": ("high legibility", "crisp lines", "precise layout", "flat lighting"),
"Printmaking": ("paper texture", "ink texture", "rough edges", "high contrast"),
"Experimental": ("intentional design", "high contrast", "tactile texture", "coherent palette"),
"Color Grade": ("smooth highlight rolloff", "clean shadows", "color separation", "balanced contrast"),
"Fine Art": ("high detail", "handcrafted surface texture", "gallery lighting", "balanced composition"),
"Mixed Media": ("high detail", "tactile texture", "paper grain", "layered depth", "cohesive composition"),
"Street Art": ("bold color", "high contrast", "wall texture", "urban grit", "spray paint overspray"),
}
class StyleLibraryError(RuntimeError):
pass
@dataclass(frozen=True)
class StyleTemplate:
id: str
name: str
category: str
prefix: str
suffix: str
tags: Tuple[str, ...]
variants: Dict[str, Tuple[str, str]]
def variant_prompt(self, variant: str) -> Tuple[str, str, str]:
if variant == DEFAULT_VARIANT:
return DEFAULT_VARIANT, self.prefix, self.suffix
prefix, suffix = self.variants.get(variant, ("", ""))
if prefix.strip() or suffix.strip():
return variant, prefix, suffix
return DEFAULT_VARIANT, self.prefix, self.suffix
@dataclass(frozen=True)
class StyleLibrary:
styles: Tuple[StyleTemplate, ...]
by_id: Dict[str, StyleTemplate]
choices: List[str]
variants: Tuple[str, ...]
@dataclass(frozen=True)
class SearchCandidate:
style: StyleTemplate
score: int
matched_fields: Tuple[str, ...]
_CACHE_SIGS: Dict[str, Tuple[Tuple[str, float, int], ...]] = {}
_CACHE_LIBRARIES: Dict[str, StyleLibrary] = {}
def norm_space(value: str) -> str:
return " ".join((value or "").replace("\r", " ").replace("\n", " ").split())
def split_phrases(value: str, sep: str = DEFAULT_PHRASE_SEPARATOR) -> List[str]:
value = norm_space(value)
if not value:
return []
return [part for part in (p.strip() for p in value.split(sep)) if part]
def dedupe_phrases(phrases: Sequence[str]) -> List[str]:
seen = set()
out: List[str] = []
for phrase in phrases:
key = phrase.casefold()
if key in seen:
continue
seen.add(key)
out.append(phrase)
return out
def z_join(parts: Sequence[str]) -> str:
return Z_SEP.join(dedupe_phrases([p.strip() for p in parts if (p or "").strip()]))
def flux_join_sentences(parts: Sequence[str]) -> str:
parts2 = [p.strip().rstrip(".") for p in parts if (p or "").strip()]
return ". ".join(parts2).strip() + ("." if parts2 else "")
def style_to_dict(style: StyleTemplate) -> Dict[str, Any]:
models = {
name: {"prefix": prefix, "suffix": suffix}
for name, (prefix, suffix) in sorted(style.variants.items())
if prefix or suffix
}
return {
"id": style.id,
"name": style.name,
"category": style.category,
"default": {"prefix": style.prefix, "suffix": style.suffix},
"models": models,
"tags": list(style.tags),
}
def _pack_sort_key(name: str) -> Tuple[int, str]:
match = re.match(r"^(\d+)", name)
if not match:
return (9999, name.casefold())
return (int(match.group(1)), name.casefold())
def iter_pack_paths() -> List[str]:
if not os.path.isdir(STYLE_PACKS_DIR):
return []
out: List[str] = []
for name in sorted(os.listdir(STYLE_PACKS_DIR), key=_pack_sort_key):
if name.lower().endswith(".json"):
out.append(os.path.join(STYLE_PACKS_DIR, name))
return out
def _file_sig(path: str) -> Tuple[str, float, int]:
try:
st = os.stat(path)
return (path, float(st.st_mtime), int(st.st_size))
except OSError:
return (path, -1.0, -1)
def style_sources_sig(legacy_path: str = LEGACY_STYLES_PATH) -> Tuple[Tuple[str, float, int], ...]:
pack_paths = iter_pack_paths()
sources = (pack_paths + [legacy_path]) if pack_paths else [legacy_path]
return tuple(_file_sig(path) for path in sources)
def _emit_problem(load_policy: str, message: str) -> None:
if load_policy == LOAD_POLICY_STRICT:
raise StyleLibraryError(message)
print(f"PromptStyler: WARN: {message}")
def _read_json(path: str, load_policy: str) -> Optional[Dict[str, Any]]:
try:
with open(path, "r", encoding="utf-8") as handle:
data = json.load(handle) or {}
except Exception as exc:
_emit_problem(load_policy, f"unable to read style pack: {path} ({exc})")
return None
if not isinstance(data, dict):
_emit_problem(load_policy, f"style file must be a JSON object: {path}")
return None
return data
def _iter_raw_style_entries(path: str, load_policy: str) -> List[Dict[str, Any]]:
data = _read_json(path, load_policy)
if data is None:
return []
styles = data.get("styles", [])
if not isinstance(styles, list):
_emit_problem(load_policy, f"'styles' must be a list in: {path}")
return []
return [item for item in styles if isinstance(item, dict)]
def _normalize_variants(raw_models: Any, *, path: str, index: int, load_policy: str) -> Dict[str, Tuple[str, str]]:
if raw_models is None:
return {}
if not isinstance(raw_models, dict):
_emit_problem(load_policy, f"styles[{index}] models must be an object in: {path}")
return {}
out: Dict[str, Tuple[str, str]] = {}
for key, raw_value in raw_models.items():
if not isinstance(key, str) or not key.strip():
_emit_problem(load_policy, f"styles[{index}] has a blank model key in: {path}")
continue
if not isinstance(raw_value, dict):
_emit_problem(load_policy, f"styles[{index}] model '{key}' must be an object in: {path}")
continue
prefix = str(raw_value.get("prefix", "") or "")
suffix = str(raw_value.get("suffix", "") or "")
out[key] = (prefix, suffix)
return out
def _normalize_style(raw: Dict[str, Any], *, path: str, index: int, load_policy: str) -> Optional[StyleTemplate]:
sid = raw.get("id")
name = raw.get("name")
if sid is None or name is None:
_emit_problem(load_policy, f"styles[{index}] missing id or name in: {path}")
return None
default = raw.get("default", {})
if not isinstance(default, dict):
_emit_problem(load_policy, f"styles[{index}] default must be an object in: {path}")
return None
if "prefix" not in default or "suffix" not in default:
_emit_problem(load_policy, f"styles[{index}] default must include prefix and suffix in: {path}")
return None
tags_raw = raw.get("tags", []) or []
if not isinstance(tags_raw, list):
_emit_problem(load_policy, f"styles[{index}] tags must be a list in: {path}")
tags_raw = []
variants = _normalize_variants(raw.get("models", {}), path=path, index=index, load_policy=load_policy)
return StyleTemplate(
id=str(sid),
name=str(name),
category=str(raw.get("category", "Uncategorized")),
prefix=str(default.get("prefix", "") or ""),
suffix=str(default.get("suffix", "") or ""),
tags=tuple(str(tag).strip() for tag in tags_raw if str(tag).strip()),
variants=variants,
)
def _normalize_styles(raw_styles: Sequence[Dict[str, Any]], *, path: str, load_policy: str) -> List[StyleTemplate]:
out: List[StyleTemplate] = []
seen_ids = set()
seen_names = set()
for index, raw in enumerate(raw_styles):
style = _normalize_style(raw, path=path, index=index, load_policy=load_policy)
if style is None:
continue
if style.id in seen_ids:
_emit_problem(load_policy, f"duplicate id '{style.id}' in: {path}")
continue
if style.name in seen_names:
_emit_problem(load_policy, f"duplicate name '{style.name}' in: {path}")
continue
seen_ids.add(style.id)
seen_names.add(style.name)
out.append(style)
return out
def load_style_library(load_policy: str = LOAD_POLICY_LENIENT, legacy_path: str = LEGACY_STYLES_PATH) -> StyleLibrary:
if load_policy not in (LOAD_POLICY_LENIENT, LOAD_POLICY_STRICT):
raise ValueError(f"Unsupported load_policy: {load_policy}")
pack_paths = iter_pack_paths()
styles: List[StyleTemplate] = []
by_id: Dict[str, StyleTemplate] = {}
variants = {DEFAULT_VARIANT}
if not pack_paths:
raw_styles = _iter_raw_style_entries(legacy_path, load_policy)
styles = _normalize_styles(raw_styles, path=legacy_path, load_policy=load_policy)
else:
for path in pack_paths:
raw_styles = _iter_raw_style_entries(path, load_policy)
normalized = _normalize_styles(raw_styles, path=path, load_policy=load_policy)
for style in normalized:
if style.id in by_id:
_emit_problem(load_policy, f"duplicate id '{style.id}' across packs")
continue
styles.append(style)
by_id[style.id] = style
variants.update(style.variants.keys())
if not styles:
if load_policy == LOAD_POLICY_STRICT:
raise StyleLibraryError("no usable style packs found")
raw_styles = _iter_raw_style_entries(legacy_path, load_policy)
styles = _normalize_styles(raw_styles, path=legacy_path, load_policy=load_policy)
if not by_id:
for style in styles:
if style.id not in by_id:
by_id[style.id] = style
variants.update(style.variants.keys())
choices = choices_for_styles(styles) if styles else [NO_STYLES_CHOICE]
return StyleLibrary(
styles=tuple(styles),
by_id=by_id,
choices=choices,
variants=tuple([DEFAULT_VARIANT] + sorted(v for v in variants if v != DEFAULT_VARIANT)),
)
def get_cached_style_library(load_policy: str = LOAD_POLICY_LENIENT, legacy_path: str = LEGACY_STYLES_PATH) -> StyleLibrary:
sig = style_sources_sig(legacy_path)
if _CACHE_SIGS.get(load_policy) == sig:
return _CACHE_LIBRARIES[load_policy]
library = load_style_library(load_policy=load_policy, legacy_path=legacy_path)
_CACHE_SIGS[load_policy] = sig
_CACHE_LIBRARIES[load_policy] = library
return library
def choices_for_styles(styles: Sequence[StyleTemplate]) -> List[str]:
styles_sorted = sorted(styles, key=lambda style: (style.category.casefold(), style.name.casefold(), style.id))
return [f"{style.category} | {style.name} | {style.id}" for style in styles_sorted]
def style_by_choice(styles: Sequence[StyleTemplate], choice: str) -> Optional[StyleTemplate]:
if not choice:
return None
sid = choice.rsplit("|", 1)[-1].strip()
for style in styles:
if style.id == sid:
return style
return None
def resolve_style_legacy(
library: StyleLibrary,
*,
choice: str,
style_id_override: str,
) -> StyleTemplate:
style_id_override = (style_id_override or "").strip()
if style_id_override:
chosen = library.by_id.get(style_id_override)
if chosen is None:
raise StyleLibraryError(f"Unknown style_id_override: {style_id_override}")
return chosen
chosen = style_by_choice(library.styles, choice)
if chosen is None:
raise StyleLibraryError("No style selected.")
return chosen
def parse_tag_hint(value: str) -> Tuple[str, ...]:
if not value:
return ()
return tuple(part.strip().casefold() for part in value.split(",") if part.strip())
def _style_matches_filters(style: StyleTemplate, *, category_hint: str, tags: Sequence[str]) -> bool:
if category_hint and category_hint.casefold() not in style.category.casefold():
return False
if tags:
style_tags = {tag.casefold() for tag in style.tags}
if any(tag not in style_tags for tag in tags):
return False
return True
def filter_styles(
styles: Sequence[StyleTemplate],
*,
category_hint: str = "",
tag_hint: str = "",
) -> List[StyleTemplate]:
category_hint = category_hint.strip()
tags = parse_tag_hint(tag_hint)
return [style for style in styles if _style_matches_filters(style, category_hint=category_hint, tags=tags)]
def _search_tokens(query: str) -> List[str]:
return [token for token in re.split(r"[^a-z0-9_]+", query.casefold()) if token]
def _search_candidate(style: StyleTemplate, query: str, tokens: Sequence[str]) -> Optional[SearchCandidate]:
query_cf = query.casefold()
if query_cf == style.id.casefold():
return SearchCandidate(style=style, score=10000, matched_fields=("id_exact",))
if query_cf == style.name.casefold():
return SearchCandidate(style=style, score=9000, matched_fields=("name_exact",))
matched_fields: List[str] = []
score = 0
id_cf = style.id.casefold()
name_cf = style.name.casefold()
category_cf = style.category.casefold()
tags_cf = [tag.casefold() for tag in style.tags]
for token in tokens:
token_score = 0
if token in id_cf:
token_score += 60
matched_fields.append("id")
if token in name_cf:
token_score += 40
matched_fields.append("name")
if token in category_cf:
token_score += 25
matched_fields.append("category")
if any(token == tag for tag in tags_cf):
token_score += 35
matched_fields.append("tag_exact")
elif any(token in tag for tag in tags_cf):
token_score += 15
matched_fields.append("tag")
score += token_score
if score <= 0:
return None
score += min(len(tokens), 5)
return SearchCandidate(style=style, score=score, matched_fields=tuple(matched_fields))
def search_styles(
styles: Sequence[StyleTemplate],
query: str,
*,
category_hint: str = "",
tag_hint: str = "",
) -> List[SearchCandidate]:
query = query.strip()
if not query:
return []
filtered = filter_styles(styles, category_hint=category_hint, tag_hint=tag_hint)
tokens = _search_tokens(query)
if not tokens:
return []
candidates: List[SearchCandidate] = []
for style in filtered:
candidate = _search_candidate(style, query, tokens)
if candidate is not None:
candidates.append(candidate)
candidates.sort(
key=lambda item: (
-item.score,
item.style.category.casefold(),
item.style.name.casefold(),
item.style.id,
)
)
return candidates
def resolve_style(
library: StyleLibrary,
*,
selection_mode: str,
choice: str,
style_id_override: str,
style_search: str,
category_hint: str = "",
tag_hint: str = "",
) -> StyleTemplate:
style_id_override = (style_id_override or "").strip()
category_hint = (category_hint or "").strip()
tag_hint = (tag_hint or "").strip()
# Preserve old graphs: if a saved workflow already uses style_id_override and
# the new selection mode input is left at its default, still honor the id.
if selection_mode == "id" or (selection_mode == "dropdown" and style_id_override):
chosen = library.by_id.get(style_id_override)
if chosen is None:
raise StyleLibraryError(f"Unknown style_id_override: {style_id_override}")
return chosen
if selection_mode == "dropdown":
chosen = style_by_choice(library.styles, choice)
if chosen is None:
raise StyleLibraryError("No style selected.")
return chosen
if selection_mode != "search":
raise StyleLibraryError(f"Unsupported selection_mode: {selection_mode}")
matches = search_styles(
library.styles,
style_search,
category_hint=category_hint,
tag_hint=tag_hint,
)
if not matches:
raise StyleLibraryError(
f"No styles matched search query: {style_search.strip() or '(empty query)'}"
)
top = matches[0]
tied = [item for item in matches if item.score == top.score]
if len(tied) > 1:
preview = "; ".join(
f"{item.style.category} | {item.style.name} | {item.style.id}"
for item in matches[:5]
)
raise StyleLibraryError(f"Ambiguous style search '{style_search}': {preview}")
return top.style
def _style_limit(style_strength: str) -> Optional[int]:
return {
"subtle": 3,
"normal": 6,
"strong": None,
}.get(style_strength, None)
def _limit_phrases(phrases: Sequence[str], style_strength: str) -> List[str]:
limit = _style_limit(style_strength)
if limit is None:
return list(phrases)
return list(phrases[:limit])
def compose_default_prompt(
prompt: str,
style: StyleTemplate,
*,
style_strength: str = "strong",
dedupe_mode: str = "smart",
overlay_style: Optional[StyleTemplate] = None,
) -> Tuple[str, Dict[str, Any]]:
base_prefix = _limit_phrases(split_phrases(style.prefix), style_strength)
base_suffix = _limit_phrases(split_phrases(style.suffix), style_strength)
overlay_suffix = _limit_phrases(split_phrases(overlay_style.suffix), style_strength) if overlay_style else []
user_phrase_keys = {phrase.casefold() for phrase in split_phrases(prompt)}
style_phrase_keys = set()
def include_phrase(phrase: str) -> bool:
key = phrase.casefold()
if dedupe_mode == "off":
return True
if key in style_phrase_keys:
return False
if key in user_phrase_keys:
return False
style_phrase_keys.add(key)
return True
prefix_phrases = [phrase for phrase in base_prefix if include_phrase(phrase)]
suffix_phrases = [phrase for phrase in base_suffix if include_phrase(phrase)]
overlay_phrases = [phrase for phrase in overlay_suffix if include_phrase(phrase)]
parts = [
DEFAULT_PHRASE_SEPARATOR.join(prefix_phrases),
prompt,
DEFAULT_PHRASE_SEPARATOR.join(suffix_phrases + overlay_phrases),
]
styled_prompt = DEFAULT_PHRASE_SEPARATOR.join([part for part in parts if part])
return styled_prompt, {
"base_prefix_phrases": prefix_phrases,
"base_suffix_phrases": suffix_phrases,
"overlay_suffix_phrases": overlay_phrases,
}
def compose_prompt(
prompt: str,
style: StyleTemplate,
*,
template_variant: str,
style_strength: str = "strong",
dedupe_mode: str = "smart",
overlay_style: Optional[StyleTemplate] = None,
) -> Tuple[str, Dict[str, Any]]:
prompt = prompt or ""
applied_variant, prefix, suffix = style.variant_prompt(template_variant)
overlay_variant = DEFAULT_VARIANT
overlay_suffix = ""
if overlay_style is not None:
overlay_variant, _overlay_prefix, overlay_suffix = overlay_style.variant_prompt(template_variant)
if applied_variant == DEFAULT_VARIANT:
styled_prompt, details = compose_default_prompt(
prompt,
style,
style_strength=style_strength,
dedupe_mode=dedupe_mode,
overlay_style=overlay_style,
)
details["variant"] = applied_variant
details["overlay_variant"] = overlay_variant if overlay_style else ""
return styled_prompt, details
parts = [prompt, prefix, suffix, overlay_suffix]
styled_prompt = norm_space(" ".join(part for part in parts if part))
return styled_prompt, {
"variant": applied_variant,
"overlay_variant": overlay_variant if overlay_style else "",
"base_prefix_phrases": [],
"base_suffix_phrases": [],
"overlay_suffix_phrases": [],
}
def compose_prompt_legacy(
prompt: str,
style: StyleTemplate,
*,
template_variant: str,
) -> Tuple[str, Dict[str, Any]]:
prompt = prompt or ""
applied_variant, prefix, suffix = style.variant_prompt(template_variant)
if applied_variant != DEFAULT_VARIANT:
styled_prompt = norm_space(" ".join(part for part in (prompt, prefix, suffix) if part))
return styled_prompt, {
"variant": applied_variant,
"base_prefix_phrases": [],
"base_suffix_phrases": [],
}
prefix_phrases = split_phrases(style.prefix)
prompt_phrases = split_phrases(prompt)
suffix_phrases = split_phrases(style.suffix)
phrases = dedupe_phrases(prefix_phrases + prompt_phrases + suffix_phrases)
styled_prompt = DEFAULT_PHRASE_SEPARATOR.join(phrase for phrase in phrases if phrase)
return styled_prompt, {
"variant": DEFAULT_VARIANT,
"base_prefix_phrases": prefix_phrases,
"base_suffix_phrases": suffix_phrases,
}
def is_color_grade_style(style: StyleTemplate) -> bool:
return style.category.startswith("Color Grade")
def resolve_overlay_style(library: StyleLibrary, overlay_style_id: str) -> Optional[StyleTemplate]:
overlay_style_id = (overlay_style_id or "").strip()
if not overlay_style_id:
return None
chosen = library.by_id.get(overlay_style_id)
if chosen is None:
raise StyleLibraryError(f"Unknown overlay_style_id: {overlay_style_id}")
if not is_color_grade_style(chosen):
raise StyleLibraryError(
f"overlay_style_id must reference a Color Grade style: {overlay_style_id}"
)
return chosen
def make_style_meta(
*,
chosen_style: Optional[StyleTemplate],
overlay_style: Optional[StyleTemplate],
requested_variant: str,
applied_variant: str,
selection_mode: str,
style_search: str,
category_hint: str,
tag_hint: str,
style_strength: str,
dedupe_mode: str,
on_missing_style: str,
prompt_details: Mapping[str, Any],
error: str = "",
) -> str:
payload = {
"status": "error" if error else "ok",
"error": error,
"selection_mode": selection_mode,
"style_search": style_search,
"category_hint": category_hint,
"tag_hint": tag_hint,
"requested_variant": requested_variant,
"applied_variant": applied_variant,
"style_strength": style_strength,
"dedupe_mode": dedupe_mode,
"on_missing_style": on_missing_style,
"style": {
"id": chosen_style.id if chosen_style else "",
"name": chosen_style.name if chosen_style else "",
"category": chosen_style.category if chosen_style else "",
"tags": list(chosen_style.tags) if chosen_style else [],
},
"overlay": {
"id": overlay_style.id if overlay_style else "",
"name": overlay_style.name if overlay_style else "",
"category": overlay_style.category if overlay_style else "",
"tags": list(overlay_style.tags) if overlay_style else [],
},
"prompt_details": dict(prompt_details),
}
return json.dumps(payload, ensure_ascii=True, sort_keys=True)
def available_categories(styles: Sequence[StyleTemplate]) -> List[str]:
categories = {style.category for style in styles if style.category.strip()}
categories.update(CATEGORY_BASE_PREFIX.keys())
categories.update(CATEGORY_BASE_SUFFIX.keys())
return sorted(categories, key=lambda value: value.casefold())
def write_legacy_snapshot(path: str = LEGACY_STYLES_PATH, load_policy: str = LOAD_POLICY_STRICT) -> int:
library = load_style_library(load_policy=load_policy)
payload = {"version": 1, "styles": [style_to_dict(style) for style in library.styles]}
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as handle:
json.dump(payload, handle, indent=2, ensure_ascii=True)
handle.write("\n")
return len(library.styles)
+285
View File
@@ -0,0 +1,285 @@
{
"version": 1,
"styles": [
{
"id": "repro_mimeograph_duplication",
"name": "Reprographic: Mimeograph Duplication",
"category": "Printmaking/Expanded",
"default": {
"prefix": "printmaking, ink on paper, handcrafted texture, carved lines, high contrast, mimeograph duplication, stencil master, inked drum texture, duplicated sheets",
"suffix": "slight misregistration, inky density shifts, porous paper texture, office-print rhythm, paper texture, ink texture, rough edges, high contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mimeograph duplication with stencil-master imperfections. Lighting: flat even light preserving paper and ink density variation. Texture: inked drum transfer, porous paper, and repeated-sheet inconsistency. Key cues: mimeograph duplication, stencil master, inked drum texture, duplicated sheets. Finish cues: slight misregistration, inky density shifts, porous paper texture, office-print rhythm. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"mimeograph",
"print"
]
},
{
"id": "repro_spirit_duplicator_copy",
"name": "Reprographic: Spirit Duplicator",
"category": "Printmaking/Expanded",
"default": {
"prefix": "printmaking, ink on paper, handcrafted texture, carved lines, high contrast, spirit duplicator copy, purple aniline tones, soft duplicate texturing, school-office print",
"suffix": "lavender cast, soft edges, faded pressure transfer, paper softness, paper texture, ink texture, rough edges, high contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: spirit-duplicator copy with characteristic purple cast. Lighting: flat diffuse light that keeps the faded duplicate texture legible. Texture: soft transfer edges, pale dye density, and absorbent paper. Key cues: spirit duplicator copy, purple aniline tones, soft duplicate texturing, school-office print. Finish cues: lavender cast, soft edges, faded pressure transfer, paper softness. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"spirit_duplicator",
"print"
]
},
{
"id": "repro_diazo_blueprint",
"name": "Reprographic: Diazo Blueprint",
"category": "Photography/Alt Process",
"default": {
"prefix": "cyanotype print, blueprint blue, pale white linework, coated paper texture, graphic plan aesthetic",
"suffix": "paper grain, soft process fading, crisp line edges, matte coated sheet, controlled contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: cyanotype-like blueprint print with pale linework on deep blue paper. Lighting: flat even light preserving paper grain and line clarity. Texture: matte coated paper, soft process fading, and crisp pale lines. Key cues: blueprint blue, pale white linework, coated paper texture, graphic plan aesthetic. Finish cues: paper grain, soft process fading, crisp line edges, matte coated sheet. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"diazo",
"blueprint"
]
},
{
"id": "repro_whiteprint_copy",
"name": "Reprographic: Whiteprint Copy",
"category": "Photography/Alt Process",
"default": {
"prefix": "whiteprint aesthetic, pale paper ground, soft blue linework, engineering-print look, matte sheet texture",
"suffix": "fine paper grain, faint blue tint, clean line edges, document paper wear, restrained contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: whiteprint-style reproduction with blue linework on pale paper. Lighting: neutral even light preserving line clarity and sheet texture. Texture: matte paper, faint blue tint, and lightly worn print edges. Key cues: pale paper ground, soft blue linework, engineering-print look, matte sheet texture. Finish cues: fine paper grain, faint blue tint, clean line edges, document paper wear. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"whiteprint",
"technical_copy"
]
},
{
"id": "repro_offset_litho_misregistration",
"name": "Reprographic: Offset Misregistration",
"category": "Graphic Design",
"default": {
"prefix": "offset print look, slight CMYK drift, layered overprint color, poster ink texture, graphic print surface",
"suffix": "registration drift, ink overlap edges, paper tooth, press imperfection texture, bold printed contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: offset print with slight CMYK misregistration and overprinted color layers. Lighting: flat print-preview light with minimal shading. Texture: paper tooth, ink overlap edges, and gentle press drift. Key cues: offset print look, slight CMYK drift, layered overprint color, poster ink texture. Finish cues: registration drift, ink overlap edges, paper tooth, press imperfection texture. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"offset",
"graphic"
]
},
{
"id": "repro_newsprint_halftone",
"name": "Reprographic: Newsprint Halftone",
"category": "Graphic Design",
"default": {
"prefix": "newsprint print look, coarse halftone dots, absorbent paper stock, editorial print texture, monochrome or limited color",
"suffix": "dot gain, soft ink spread, paper fuzz, cheap stock texture, gritty printed contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: newsprint reproduction with coarse halftone dots and absorbent paper texture. Lighting: flat editorial light keeping dot structure readable. Texture: dot gain, soft ink spread, and cheap stock fuzz. Key cues: coarse halftone dots, absorbent paper stock, editorial print texture, limited-color print. Finish cues: dot gain, soft ink spread, paper fuzz, cheap stock texture. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"newsprint",
"halftone"
]
},
{
"id": "repro_xerox_zine_photocopy",
"name": "Reprographic: Xerox Zine Photocopy",
"category": "Graphic Design",
"default": {
"prefix": "photocopy zine aesthetic, stark black toner, rough collage print, DIY copy-art texture, matte office paper",
"suffix": "toner speckle, edge shadowing, paper curl, rough copy texture, gritty contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: photocopy zine print with stark black toner and rough copy-art texture. Lighting: flat copy-table light with no dramatic shading. Texture: toner speckle, edge shadowing, and matte office paper curl. Key cues: photocopy zine aesthetic, stark black toner, rough collage print, DIY copy-art texture. Finish cues: toner speckle, edge shadowing, paper curl, rough copy texture. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"photocopy",
"zine"
]
},
{
"id": "repro_carbon_copy_manifold",
"name": "Reprographic: Carbon Copy Manifold",
"category": "Printmaking/Expanded",
"default": {
"prefix": "printmaking, ink on paper, handcrafted texture, carved lines, high contrast, carbon copy manifold, pressure transfer typing, duplicate form, ghosted text impression",
"suffix": "soft duplicate offset, paper stack pressure marks, form-stock texture, typed imprint feel, paper texture, ink texture, rough edges, high contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: carbon-copy manifold form with pressure-transferred duplicate impression. Lighting: neutral even light revealing paper stack pressure and duplicate offset. Texture: soft transfer, form-stock grain, and faint stacked-sheet marks. Key cues: carbon copy manifold, pressure transfer typing, duplicate form, ghosted text impression. Finish cues: soft duplicate offset, paper stack pressure marks, form-stock texture, typed imprint feel. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"carbon_copy",
"office_print"
]
},
{
"id": "repro_contact_sheet_proof",
"name": "Reprographic: Contact Sheet Proof",
"category": "Photography/Alt Process",
"default": {
"prefix": "contact-sheet darkroom print, small photo borders, glossy proof paper, analog print-sheet aesthetic",
"suffix": "glossy paper sheen, narrow frame borders, archival print texture, soft darkroom proof feel"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: darkroom contact-sheet print with small bordered frames and glossy proof paper. Lighting: soft even light preserving paper sheen and border traces. Texture: glossy proof paper, archival print surface, and subtle frame spacing. Key cues: contact-sheet darkroom print, small photo borders, glossy proof paper, analog print-sheet aesthetic. Finish cues: glossy paper sheen, narrow frame borders, archival print texture, soft darkroom proof feel. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"contact_sheet",
"analog_print"
]
},
{
"id": "repro_rotogravure_ink_pattern",
"name": "Reprographic: Rotogravure Ink Pattern",
"category": "Printmaking/Expanded",
"default": {
"prefix": "printmaking, ink on paper, handcrafted texture, carved lines, high contrast, rotogravure print, copper-cell ink pattern, smooth tonal reproduction, press cylinder texture",
"suffix": "fine cell grain, velvety darks, printed paper texture, high-volume press character, paper texture, ink texture, rough edges, high contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: rotogravure reproduction with smooth tonal printing and fine cell patterning. Lighting: flat print-preview light preserving tonal reproduction and cell grain. Texture: velvety darks, fine gravure grain, and coated paper response. Key cues: rotogravure print, copper-cell ink pattern, smooth tonal reproduction, press cylinder texture. Finish cues: fine cell grain, velvety darks, printed paper texture, high-volume press character. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"rotogravure",
"print"
]
},
{
"id": "repro_cmyk_registration_proof",
"name": "Reprographic: CMYK Registration Proof",
"category": "Graphic Design",
"default": {
"prefix": "CMYK proof print, layered process color, slight registration drift, print-shop proof texture, offset ink surface",
"suffix": "ink alignment variation, paper tooth, printed color layering, proof-sheet texture, clean graphic contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: CMYK proof print with layered process color and slight registration drift. Lighting: neutral inspection light with minimal glare. Texture: paper tooth, printed color layering, and subtle alignment variation. Key cues: CMYK proof print, layered process color, slight registration drift, print-shop proof texture. Finish cues: ink alignment variation, paper tooth, printed color layering, proof-sheet texture. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"cmyk",
"proof"
]
},
{
"id": "repro_thermographic_raised_ink",
"name": "Reprographic: Thermographic Raised Ink",
"category": "Printmaking/Expanded",
"default": {
"prefix": "printmaking, ink on paper, handcrafted texture, carved lines, high contrast, thermographic raised ink, powder-fused lettering, slight gloss, printed stationery",
"suffix": "raised ink relief, subtle sheen, paper texture, formal print finish, ink texture, rough edges, high contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: thermographic print with powder-fused raised ink. Lighting: soft raking light revealing slight relief and glossy edges. Texture: raised lettering, subtle sheen, and fine stationery stock. Key cues: thermographic raised ink, powder-fused lettering, slight gloss, printed stationery. Finish cues: raised ink relief, subtle sheen, paper texture, formal print finish. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"thermography",
"raised_ink"
]
},
{
"id": "repro_rubber_stamp_overprint",
"name": "Reprographic: Rubber Stamp Overprint",
"category": "Printmaking/Expanded",
"default": {
"prefix": "printmaking, ink on paper, handcrafted texture, carved lines, high contrast, rubber stamp overprint, layered ink stamps, imperfect repeat, manual registration",
"suffix": "ink pooling, stamp edge chatter, paper tooth, hand-pressed irregularity, paper texture, ink texture, rough edges, high contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: rubber-stamped overprint with imperfect manual registration. Lighting: flat diffuse light preserving ink pooling and stamp edge chatter. Texture: hand-pressed irregularity, paper tooth, and layered ink density. Key cues: rubber stamp overprint, layered ink stamps, imperfect repeat, manual registration. Finish cues: ink pooling, stamp edge chatter, paper tooth, hand-pressed irregularity. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"rubber_stamp",
"overprint"
]
},
{
"id": "repro_dot_gain_press_sheet",
"name": "Reprographic: Dot Gain Press Sheet",
"category": "Graphic Design",
"default": {
"prefix": "screened print texture, dot gain, soft ink spread, absorbent press paper, calibration-print look",
"suffix": "softened dot edges, paper absorbency, density variation, press-sheet texture, matte printed surface"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: screened print showing dot gain and soft ink spread on absorbent paper. Lighting: neutral inspection light that keeps screened texture readable. Texture: softened dot edges, density variation, and matte press paper. Key cues: screened print texture, dot gain, soft ink spread, absorbent press paper. Finish cues: softened dot edges, paper absorbency, density variation, press-sheet texture. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"reprographic",
"dot_gain",
"press"
]
}
]
}
+405
View File
@@ -0,0 +1,405 @@
{
"version": 1,
"styles": [
{
"id": "artist_leonardo_da_vinci_sfumato",
"name": "Artist: Leonardo da Vinci (Sfumato + Subtle Chiaroscuro)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Leonardo da Vinci, High Renaissance, sfumato, subtle tonal transitions, delicate glazing, gentle chiaroscuro, atmospheric perspective",
"suffix": "smooth gradients, refined realism, calm harmony, soft atmospheric depth, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: High Renaissance painting in the style of Leonardo da Vinci, refined realism with sfumato. Lighting: gentle chiaroscuro with soft shadow transitions. Technique: delicate glazing, subtle tonal gradients, atmospheric perspective. Key cues: in the style of Leonardo da Vinci, High Renaissance, sfumato, subtle tonal transitions, delicate glazing, gentle chiaroscuro, atmospheric perspective. Finish cues: smooth gradients, refined realism, calm harmony, soft atmospheric depth. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "leonardo_da_vinci", "high_renaissance", "sfumato"]
},
{
"id": "artist_raphael_harmonious_renaissance",
"name": "Artist: Raphael (Harmonious High Renaissance)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Raphael, High Renaissance, harmonious composition, idealized forms, graceful linework, clear perspective, soft modeling, balanced symmetry",
"suffix": "serene mood, smooth paint handling, gentle highlights, harmonious color, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: High Renaissance painting in the style of Raphael, clarity and harmony. Composition: balanced symmetry, clear perspective, idealized forms, graceful contours. Color: harmonious palette with soft transitions and calm unity. Key cues: in the style of Raphael, High Renaissance, harmonious composition, idealized forms, graceful linework, clear perspective, balanced symmetry. Finish cues: serene mood, smooth paint handling, gentle highlights, harmonious color. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "raphael", "high_renaissance"]
},
{
"id": "artist_michelangelo_monumental_fresco",
"name": "Artist: Michelangelo (Monumental Forms + Fresco)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Michelangelo, High Renaissance, monumental forms, sculptural anatomy, dramatic foreshortening, powerful gestures, fresco painting, bold modeling",
"suffix": "monumental scale, dramatic value range, strong highlights and shadows, dynamic composition, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: monumental High Renaissance fresco painting in the style of Michelangelo. Form: sculptural anatomy, heroic scale, dramatic foreshortening, powerful gestures. Shading: bold modeling with a wide value range and clear form separation. Key cues: in the style of Michelangelo, High Renaissance, monumental forms, sculptural anatomy, dramatic foreshortening, fresco painting, bold modeling. Finish cues: monumental scale, dramatic value range, strong highlights and shadows, dynamic composition. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "michelangelo", "high_renaissance", "fresco"]
},
{
"id": "artist_botticelli_linear_grace_tempera",
"name": "Artist: Sandro Botticelli (Linear Grace + Tempera)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Sandro Botticelli, Early Renaissance, linear grace, flowing contours, delicate tempera-like handling, decorative patterning, clear silhouettes, gentle pastel palette",
"suffix": "crisp contours, delicate shading, ornamental detail, elegant rhythm, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Early Renaissance painting in the style of Sandro Botticelli, emphasizing linear grace. Line: flowing contours, clear silhouettes, elegant rhythm, ornamental detail. Technique: tempera-like precision with delicate shading and gentle color harmony. Key cues: in the style of Sandro Botticelli, Early Renaissance, linear grace, flowing contours, decorative patterning, clear silhouettes. Finish cues: crisp contours, delicate shading, ornamental detail, elegant rhythm. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "botticelli", "early_renaissance"]
},
{
"id": "artist_caravaggio_tenebrism",
"name": "Artist: Caravaggio (Tenebrism + Dramatic Realism)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Caravaggio, Baroque, tenebrism, intense chiaroscuro, dramatic realism, strong directional light, deep shadow fields, theatrical contrast",
"suffix": "high contrast, hard-edged light falloff, rich darks, warm highlights, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Baroque oil painting in the style of Caravaggio, dramatic realism with tenebrism. Lighting: intense chiaroscuro with deep shadow fields and hard-edged light falloff. Mood: tense and theatrical, with bold value contrasts. Key cues: in the style of Caravaggio, Baroque, tenebrism, intense chiaroscuro, dramatic realism, strong directional light. Finish cues: high contrast, rich darks, warm highlights, hard-edged light falloff. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "caravaggio", "baroque", "tenebrism"]
},
{
"id": "artist_rembrandt_chiaroscuro_impasto",
"name": "Artist: Rembrandt van Rijn (Chiaroscuro + Impasto)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Rembrandt van Rijn, Dutch Golden Age, warm chiaroscuro, impasto highlights, rich earth tones, painterly brushwork, subtle glazing, psychological depth",
"suffix": "thick paint texture, warm amber light, deep soft shadows, rich tonal range, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Dutch Golden Age oil painting in the style of Rembrandt van Rijn, warm chiaroscuro and painterly depth. Lighting: dramatic but warm light with deep soft shadows and strong form modeling. Texture: impasto highlights with visible brushwork and subtle glazing in midtones. Key cues: in the style of Rembrandt van Rijn, Dutch Golden Age, warm chiaroscuro, impasto highlights, rich earth tones, painterly brushwork. Finish cues: thick paint texture, warm amber light, deep soft shadows, rich tonal range. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "rembrandt", "dutch_golden_age", "chiaroscuro", "impasto"]
},
{
"id": "artist_vermeer_luminous_daylight",
"name": "Artist: Johannes Vermeer (Luminous Soft Daylight)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Johannes Vermeer, Dutch Golden Age, luminous daylight, soft diffused side light, tranquil composition, subtle color harmony, smooth glazing, quiet realism",
"suffix": "pearl-like highlights, clean edges, calm atmosphere, delicate texture, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Dutch Golden Age painting in the style of Johannes Vermeer, luminous realism and calm clarity. Lighting: soft diffused daylight from one side, gentle shadow transitions, pearl-like highlights. Technique: smooth glazing, clean edges, subtle color harmony, quiet atmosphere. Key cues: in the style of Johannes Vermeer, Dutch Golden Age, luminous daylight, soft diffused side light, smooth glazing, subtle color harmony. Finish cues: pearl-like highlights, clean edges, calm atmosphere, delicate texture. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "vermeer", "dutch_golden_age", "luminous"]
},
{
"id": "artist_velazquez_tonal_realism",
"name": "Artist: Diego Velázquez (Tonal Realism + Loose Brushwork)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Diego Velázquez, Spanish Baroque, tonal realism, loose confident brushwork, subdued palette, atmospheric space, naturalistic rendering, understated highlights",
"suffix": "painterly edges, tonal unity, subtle highlights, atmospheric depth, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Spanish Baroque painting in the style of Diego Velázquez, naturalistic tonal realism. Brushwork: loose and confident with painterly edges and subtle transitions. Color: subdued palette with tonal unity and understated highlights, emphasizing atmosphere. Key cues: in the style of Diego Velázquez, Spanish Baroque, tonal realism, loose confident brushwork, subdued palette, atmospheric space. Finish cues: painterly edges, tonal unity, subtle highlights, atmospheric depth. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "velazquez", "spanish_baroque", "tonal_painting"]
},
{
"id": "artist_goya_expressive_romanticism",
"name": "Artist: Francisco Goya (Expressive Romanticism)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Francisco Goya, Romanticism, expressive brushwork, somber palette, dramatic contrast, psychological intensity, raw painterly texture, moody atmosphere",
"suffix": "bold value contrasts, rough brush texture, dark tonal weight, intense mood, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Romantic-era painting in the style of Francisco Goya, expressive and psychologically intense. Tone: somber palette with dark tonal weight and bold value contrasts. Brushwork: raw painterly texture, energetic marks, heightened dramatic contrast. Key cues: in the style of Francisco Goya, Romanticism, expressive brushwork, somber palette, dramatic contrast, psychological intensity. Finish cues: bold value contrasts, rough brush texture, dark tonal weight, intense mood. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "goya", "romanticism", "expressive"]
},
{
"id": "artist_turner_luminous_atmosphere",
"name": "Artist: J. M. W. Turner (Luminous Atmosphere)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of J. M. W. Turner, Romanticism, luminous atmosphere, dissolving forms, swirling light, vaporous haze, radiant color, atmospheric perspective",
"suffix": "glowing light effects, soft edges, misty diffusion, energetic paint movement, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Romantic painting in the style of J. M. W. Turner, luminous atmosphere and radiant color. Atmosphere: vaporous haze, dissolving forms, soft edges, misty diffusion. Light: glowing light effects with energetic paint movement and dramatic tonal transitions. Key cues: in the style of J. M. W. Turner, Romanticism, luminous atmosphere, dissolving forms, swirling light, vaporous haze. Finish cues: glowing light effects, soft edges, misty diffusion, radiant color. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "turner", "romanticism", "atmospheric"]
},
{
"id": "artist_monet_impressionist_light",
"name": "Artist: Claude Monet (Impressionist Light)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Claude Monet, Impressionism, broken brushstrokes, plein-air light, shimmering color, soft edges, optical color mixing, atmospheric haze",
"suffix": "sparkling highlights, loose strokes, airy atmosphere, light-drenched palette, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Impressionist painting in the style of Claude Monet, focused on light and atmosphere. Brushwork: short broken strokes with soft edges and shimmering color transitions. Color: optical mixing and plein-air light effects with a light-drenched palette and gentle haze. Key cues: in the style of Claude Monet, Impressionism, broken brushstrokes, plein-air light, shimmering color, atmospheric haze. Finish cues: sparkling highlights, loose strokes, airy atmosphere, light-drenched palette. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "monet", "impressionism"]
},
{
"id": "artist_seurat_pointillism",
"name": "Artist: Georges Seurat (Pointillism)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Georges Seurat, Neo-Impressionism, pointillism, tiny color dots, optical color mixing, precise composition, clear silhouettes, controlled palette",
"suffix": "granular dot texture, luminous color vibration, crisp contours, structured composition, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Neo-Impressionist painting in the style of Georges Seurat, built from pointillist color dots. Technique: tiny dots and dabs that create optical color mixing and luminous vibration at a distance. Composition: precise and structured with crisp contours and controlled rhythm. Key cues: in the style of Georges Seurat, Neo-Impressionism, pointillism, tiny color dots, optical color mixing, precise composition. Finish cues: granular dot texture, luminous color vibration, crisp contours, structured composition. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "seurat", "neo_impressionism", "pointillism"]
},
{
"id": "artist_cezanne_constructive_brushwork",
"name": "Artist: Paul Cézanne (Constructive Brushwork)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Paul Cézanne, Post-Impressionism, constructive brushwork, chromatic planes, simplified geometry, underlying structure, warm-cool modulation, architectonic composition",
"suffix": "blocky color planes, structured forms, balanced weight, textured strokes, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Post-Impressionist painting in the style of Paul Cézanne, emphasizing structure and form. Brushwork: constructive strokes that build planes of color and underlying geometry. Color: warm-cool modulation and chromatic planes that create solid, architectonic composition. Key cues: in the style of Paul Cézanne, Post-Impressionism, constructive brushwork, chromatic planes, simplified geometry, underlying structure. Finish cues: blocky color planes, structured forms, balanced weight, textured strokes. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "cezanne", "post_impressionism", "constructive"]
},
{
"id": "artist_van_gogh_expressive_impasto",
"name": "Artist: Vincent van Gogh (Expressive Impasto)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Vincent van Gogh, Post-Impressionism, striking color, emphatic brushwork, thick impasto, swirling directional strokes, bold outlines, high-chroma palette",
"suffix": "visible paint texture, energetic strokes, intense complementary contrast, dynamic movement, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Post-Impressionist oil painting in the style of Vincent van Gogh, energetic and expressive. Brushwork: emphatic directional strokes with thick impasto and visible paint texture. Color: striking high-chroma palette with intense complementary contrasts and bold outlines. Key cues: in the style of Vincent van Gogh, Post-Impressionism, striking color, emphatic brushwork, thick impasto, bold outlines. Finish cues: visible paint texture, energetic strokes, intense complementary contrast, dynamic movement. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "van_gogh", "post_impressionism", "impasto", "expressive"]
},
{
"id": "artist_gauguin_synthetism",
"name": "Artist: Paul Gauguin (Synthetism)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Paul Gauguin, Post-Impressionism, synthetism, bold flat color areas, simplified shapes, strong outlines, symbolic color, decorative pattern",
"suffix": "flat planes, clean contours, saturated color blocks, poster-like clarity, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Post-Impressionist painting in the style of Paul Gauguin, synthetist and decorative. Shape: simplified forms with strong outlines and bold flat color areas. Color: symbolic, saturated blocks with poster-like clarity and patterned surfaces. Key cues: in the style of Paul Gauguin, Post-Impressionism, synthetism, bold flat color areas, simplified shapes, strong outlines. Finish cues: flat planes, clean contours, saturated color blocks, poster-like clarity. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "gauguin", "post_impressionism", "synthetism"]
},
{
"id": "artist_matisse_fauvist_color",
"name": "Artist: Henri Matisse (Fauvist Color)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Henri Matisse, Fauvism, bold saturated color, expressive contours, flattened space, decorative pattern, simplified forms, lively composition",
"suffix": "vivid palette, clean color fields, strong outlines, rhythmic shapes, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Fauvist painting in the style of Henri Matisse, emphasizing bold expressive color. Color: vivid saturated palette with large luminous patches and simplified forms. Line: expressive contours and flattened space with decorative rhythm and strong outlines. Key cues: in the style of Henri Matisse, Fauvism, bold saturated color, expressive contours, flattened space, decorative pattern. Finish cues: vivid palette, clean color fields, strong outlines, rhythmic shapes. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "matisse", "fauvism", "color"]
},
{
"id": "artist_picasso_analytical_cubism",
"name": "Artist: Pablo Picasso (Analytical Cubism)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Pablo Picasso, Cubism, multiple viewpoints, fractured planes, faceted geometry, flattened picture plane, angular forms, limited earthy palette",
"suffix": "geometric fragmentation, sharp edges, planar shading, abstracted structure, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: analytical Cubism in the style of Pablo Picasso, fractured structure and multiple viewpoints. Form: faceted geometry, angular planes, flattened picture plane, abstracted construction. Color: limited earthy palette with planar shading and sharp edges. Key cues: in the style of Pablo Picasso, Cubism, multiple viewpoints, fractured planes, faceted geometry, flattened picture plane. Finish cues: geometric fragmentation, sharp edges, planar shading, abstracted structure. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "picasso", "cubism", "geometric"]
},
{
"id": "artist_kandinsky_geometric_abstraction",
"name": "Artist: Wassily Kandinsky (Geometric Abstraction)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Wassily Kandinsky, abstract art, geometric abstraction, dynamic lines, bold shapes, color-form relationships, rhythmic composition, nonobjective",
"suffix": "vibrant color interplay, clean shapes, energetic balance, abstract spatial tension, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: geometric abstraction in the style of Wassily Kandinsky, nonobjective and rhythmic. Composition: dynamic lines and bold shapes arranged with energetic balance and abstract spatial tension. Color: intentional color-form relationships with vibrant interplay and clear structure. Key cues: in the style of Wassily Kandinsky, abstract art, geometric abstraction, dynamic lines, bold shapes, color-form relationships. Finish cues: vibrant color interplay, clean shapes, energetic balance, abstract spatial tension. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "kandinsky", "abstract", "geometric"]
},
{
"id": "artist_mondrian_de_stijl_grid",
"name": "Artist: Piet Mondrian (De Stijl Grid)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Piet Mondrian, De Stijl, neoplasticism, orthogonal grid, primary colors, black lines, flat planes, strict geometry",
"suffix": "clean grid structure, flat color blocks, sharp edges, minimal composition, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: De Stijl abstraction in the style of Piet Mondrian, rigorous geometric order. Composition: orthogonal grid with black lines and flat planes of primary colors. Design: minimal, balanced, and precise with sharp edges and clean structure. Key cues: in the style of Piet Mondrian, De Stijl, neoplasticism, orthogonal grid, primary colors, black lines. Finish cues: clean grid structure, flat color blocks, sharp edges, minimal composition. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "mondrian", "de_stijl", "neoplasticism"]
},
{
"id": "artist_klimt_ornamental_gold",
"name": "Artist: Gustav Klimt (Ornamental Gold)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Gustav Klimt, Vienna Secession, ornamental patterning, gold leaf accents, flat decorative surfaces, linear drawing, mosaic-like texture, symbolic elegance",
"suffix": "golden shimmer, intricate ornament, flat stylization, rich decorative detail, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Vienna Secession painting in the style of Gustav Klimt, ornamental and richly decorative. Surface: flat stylization with linear drawing, intricate patterning, and gold leaf accents. Texture: mosaic-like ornament with golden shimmer and refined symbolic elegance. Key cues: in the style of Gustav Klimt, Vienna Secession, ornamental patterning, gold leaf accents, flat decorative surfaces, linear drawing. Finish cues: golden shimmer, intricate ornament, flat stylization, rich decorative detail. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "klimt", "vienna_secession", "ornament", "gold_leaf"]
},
{
"id": "artist_dali_surreal_precision",
"name": "Artist: Salvador Dalí (Surreal Precision)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Salvador Dalí, Surrealism, dreamlike atmosphere, meticulous realism, crisp detail, uncanny juxtaposition, smooth gradients, sharp highlights",
"suffix": "surreal mood, highly detailed realism, clean edges, uncanny clarity, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Surrealist painting in the style of Salvador Dalí, dreamlike and uncanny with meticulous realism. Detail: crisp rendering and clean edges with smooth gradients and sharp highlights. Mood: surreal and disquieting, emphasizing precise clarity and uncanny juxtaposition. Key cues: in the style of Salvador Dalí, Surrealism, dreamlike atmosphere, meticulous realism, crisp detail, uncanny juxtaposition. Finish cues: surreal mood, highly detailed realism, clean edges, uncanny clarity. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "dali", "surrealism", "meticulous"]
},
{
"id": "artist_pollock_action_painting",
"name": "Artist: Jackson Pollock (Action Painting Drips)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Jackson Pollock, Abstract Expressionism, action painting, drip and pour technique, all-over composition, layered splatter lines, energetic rhythm, tangled paint trails",
"suffix": "dense layered drips, dynamic movement, intertwined lines, raw texture, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Abstract Expressionist action painting in the style of Jackson Pollock, energetic and all-over. Technique: drip and pour marks with layered splatter lines and tangled paint trails across the full surface. Texture: dense intertwined lines and raw motion, emphasizing gesture and rhythm. Key cues: in the style of Jackson Pollock, Abstract Expressionism, action painting, drip and pour technique, all-over composition, layered splatter lines. Finish cues: dense layered drips, dynamic movement, intertwined lines, raw texture. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "pollock", "abstract_expressionism", "action_painting"]
},
{
"id": "artist_warhol_pop_art_silkscreen",
"name": "Artist: Andy Warhol (Pop Art Silkscreen)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Andy Warhol, Pop Art, silkscreen print, bold flat colors, repetition, high-contrast graphic shapes, commercial print aesthetic, halftone texture",
"suffix": "flat color fields, crisp edges, screenprint texture, repeating motif structure, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Pop Art print in the style of Andy Warhol, bold and graphic with silkscreen aesthetics. Color: flat high-contrast fields with repetition and commercial print clarity. Texture: screenprint feel with halftone texture and crisp edges, emphasizing iconic simplicity. Key cues: in the style of Andy Warhol, Pop Art, silkscreen print, bold flat colors, repetition, commercial print aesthetic. Finish cues: flat color fields, crisp edges, screenprint texture, repeating motif structure. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "warhol", "pop_art", "silkscreen"]
},
{
"id": "artist_hokusai_ukiyo_e_woodblock",
"name": "Artist: Katsushika Hokusai (Ukiyo-e Woodblock)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Katsushika Hokusai, ukiyo-e, Japanese woodblock print, bold fluid outlines, flat color planes, stylized perspective, patterned surfaces, clear silhouettes",
"suffix": "crisp linework, flat ink-and-color finish, paper texture, clean negative space, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: ukiyo-e woodblock print in the style of Katsushika Hokusai, graphic and refined. Line: bold fluid outlines with clear silhouettes and stylized perspective. Color: flat planes and patterned surfaces with a crisp ink-and-color finish and paper texture. Key cues: in the style of Katsushika Hokusai, ukiyo-e, Japanese woodblock print, bold fluid outlines, flat color planes, patterned surfaces. Finish cues: crisp linework, flat ink-and-color finish, paper texture, clean negative space. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "hokusai", "ukiyo_e", "woodblock"]
},
{
"id": "artist_kahlo_symbolic_realism",
"name": "Artist: Frida Kahlo (Symbolic Realism)",
"category": "Fine Art/Artists",
"default": {
"prefix": "fine art, museum-quality, traditional techniques, gallery piece, handcrafted, in the style of Frida Kahlo, Mexican modernism, folk art influence, vivid palette, symbolic surrealism, detailed brushwork, frontal composition, crisp outlines",
"suffix": "high-detail rendering, saturated colors, emblematic symbolism, strong central framing, high detail, handcrafted surface texture, gallery lighting, balanced composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Mexican modernist painting in the style of Frida Kahlo, symbolic and vividly rendered. Composition: frontal, centered, with crisp outlines and strong central framing. Color: saturated palette with folk-art influence and emblematic surreal symbolism, rendered with high detail. Key cues: in the style of Frida Kahlo, Mexican modernism, folk art influence, vivid palette, symbolic surrealism, frontal composition. Finish cues: high-detail rendering, saturated colors, emblematic symbolism, strong central framing. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": ["fine_art", "artist", "kahlo", "mexican_modernism", "symbolic"]
}
]
}
+396 -36
View File
@@ -1,6 +1,278 @@
{
"version": 1,
"styles": [
{
"id": "mixed_acrylic_sgraffito_ground",
"name": "Acrylic Sgraffito Ground",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, acrylic sgraffito, scraped paint layers, revealed underpainting, layered acrylic ground",
"suffix": "incised grooves, color reveal, matte paint texture, layered scraping marks, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media acrylic surface worked with sgraffito scraping. Lighting: soft raking light that catches incised grooves and revealed color layers. Texture: matte acrylic, scraped channels, and exposed underpainting. Key cues: acrylic sgraffito, scraped paint layers, revealed underpainting, layered acrylic ground. Finish cues: incised grooves, color reveal, matte paint texture, layered scraping marks. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"sgraffito",
"acrylic"
]
},
{
"id": "mixed_assemblage_artwork",
"name": "Assemblage Artwork",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, assemblage, found objects, mixed materials, handcrafted",
"suffix": "tactile texture, rough surfaces, layered depth, high detail, paper grain, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: assemblage artwork built from found objects and mixed materials. Lighting: soft gallery spotlights with gentle falloff and controlled highlights. Texture: rough surfaces, layered depth, tactile materials. Key cues: assemblage, found objects, mixed materials, handcrafted. Finish cues: tactile texture, rough surfaces, layered depth. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media"
]
},
{
"id": "mixed_cyanotype_print_analog",
"name": "Cyanotype Print (Analog)",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, cyanotype print, blue monochrome, sun print, paper texture",
"suffix": "grain, chemical texture, vintage print, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: analog cyanotype print on paper. Lighting: soft gallery spotlights with gentle falloff and controlled highlights. Mood: minimal, photographic, deep blue. Key cues: cyanotype print, blue monochrome, sun print, paper texture. Finish cues: grain, chemical texture, vintage print. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media"
]
},
{
"id": "mixed_decollage_poster_peel",
"name": "Decollage Poster Peel",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, decollage poster peel, torn poster layers, weathered paste, exposed underlayers",
"suffix": "ragged tears, glue residue, paper strata, urban paper texture, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media decollage with peeled poster layers and exposed strata. Lighting: flat daylight that keeps the layered tears and residue readable. Texture: ragged tears, glue residue, and stacked paper history. Key cues: decollage poster peel, torn poster layers, weathered paste, exposed underlayers. Finish cues: ragged tears, glue residue, paper strata, urban paper texture. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"decollage",
"paper"
]
},
{
"id": "mixed_encaustic_collage",
"name": "Encaustic Collage",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, encaustic collage, translucent wax layers, embedded paper fragments, scraped surface",
"suffix": "wax bloom, subtle crackle, tactile depth, layered translucency, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media encaustic collage with embedded paper fragments. Lighting: soft gallery light with gentle falloff and mild surface sheen. Texture: translucent wax layers, scraped marks, and tactile depth. Key cues: encaustic collage, translucent wax layers, embedded paper fragments, scraped surface. Finish cues: wax bloom, subtle crackle, tactile depth, layered translucency. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"encaustic",
"collage"
]
},
{
"id": "mixed_fabric_scrap_collage",
"name": "Fabric Scrap Collage",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, fabric scrap collage, stitched textile fragments, raw edges, patchwork layering",
"suffix": "frayed fibers, cloth weave, stitched joins, soft tactile depth, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media collage assembled from layered textile scraps. Lighting: soft diffuse light revealing cloth weave and stitched joins. Texture: frayed fibers, raw edges, and patchwork depth. Key cues: fabric scrap collage, stitched textile fragments, raw edges, patchwork layering. Finish cues: frayed fibers, cloth weave, stitched joins, soft tactile depth. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"fabric",
"collage"
]
},
{
"id": "mixed_foil_leaf_overlay",
"name": "Foil Leaf Overlay",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, foil leaf overlay, metal leaf fragments, mixed surface, hand-applied shimmer",
"suffix": "broken foil edges, micro-reflections, adhesive texture, layered contrast, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media surface with hand-applied foil leaf overlays. Lighting: controlled light that catches metallic fragments without blowing them out. Texture: broken foil edges, adhesive texture, and small reflective flashes. Key cues: foil leaf overlay, metal leaf fragments, mixed surface, hand-applied shimmer. Finish cues: broken foil edges, micro-reflections, adhesive texture, layered contrast. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"foil",
"leaf"
]
},
{
"id": "mixed_frottage_rubbing_study",
"name": "Frottage Rubbing Study",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, frottage rubbing, found-surface textures, graphite transfer, pressure marks",
"suffix": "rubbed grain, uneven density, paper drag marks, tactile impressions, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media frottage study built from rubbed surface textures. Lighting: soft raking light that reveals paper tooth and rubbing variation. Texture: transferred grain, pressure marks, and uneven graphite density. Key cues: frottage rubbing, found-surface textures, graphite transfer, pressure marks. Finish cues: rubbed grain, uneven density, paper drag marks, tactile impressions. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"frottage",
"rubbing"
]
},
{
"id": "mixed_gel_medium_image_transfer",
"name": "Gel Medium Image Transfer",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, gel medium image transfer, lifted image layers, translucent medium, paper support",
"suffix": "soft edge lifting, adhesive sheen, surface irregularities, layered translucency, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media gel-medium transfer with lifted translucent image layers. Lighting: soft gallery light with enough angle to reveal transfer texture. Texture: adhesive sheen, edge lifting, and layered translucent film. Key cues: gel medium image transfer, lifted image layers, translucent medium, paper support. Finish cues: soft edge lifting, adhesive sheen, surface irregularities, layered translucency. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"transfer",
"acrylic"
]
},
{
"id": "mixed_gel_plate_monoprint",
"name": "Gel Plate Monoprint",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, gel plate monoprint, ink layers, brayer texture, printmaking",
"suffix": "paper grain, ink transfer artifacts, handmade print, high detail, tactile texture, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: gel plate monoprint with layered ink textures. Lighting: soft gallery spotlights with gentle falloff and controlled highlights. Texture: ink transfer artifacts, paper grain, layered prints. Key cues: gel plate monoprint, ink layers, brayer texture, printmaking. Finish cues: paper grain, ink transfer artifacts, handmade print. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media"
]
},
{
"id": "mixed_gesso_charcoal_board",
"name": "Gesso and Charcoal Board",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, gesso ground, charcoal marks, chalky board surface, erased highlights",
"suffix": "powdery smudge, toothy ground, lifted erasures, matte surface, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media board with gesso ground and charcoal drawing. Lighting: soft studio light revealing chalky tooth and erased highlights. Texture: powdery charcoal, matte gesso, and lifted erasure marks. Key cues: gesso ground, charcoal marks, chalky board surface, erased highlights. Finish cues: powdery smudge, toothy ground, lifted erasures, matte surface. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"gesso",
"charcoal"
]
},
{
"id": "mixed_handmade_paper_pulp_relief",
"name": "Handmade Paper Pulp Relief",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, handmade paper pulp, cast relief, fibrous surface, pressed pulp forms",
"suffix": "deckled edges, fiber clumps, raised paper relief, soft shadow depth, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media handmade paper relief with cast pulp forms. Lighting: soft raking light to reveal paper relief and deckled edges. Texture: fibrous pulp, soft relief shadows, and visible paper formation. Key cues: handmade paper pulp, cast relief, fibrous surface, pressed pulp forms. Finish cues: deckled edges, fiber clumps, raised paper relief, soft shadow depth. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"paper",
"relief"
]
},
{
"id": "mixed_mixed_media_acrylic_ink",
"name": "Mixed Media Acrylic + Ink",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, mixed media, acrylic paint, ink linework, layered marks",
"suffix": "texture paste, splatter, handmade imperfections, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media artwork combining acrylic paint and ink linework. Lighting: soft gallery spotlights with gentle falloff and controlled highlights. Texture: layered paint, ink marks, tactile surface. Key cues: mixed media, acrylic paint, ink linework, layered marks. Finish cues: texture paste, splatter, handmade imperfections. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media"
]
},
{
"id": "mixed_monoprint_collage",
"name": "Monoprint Collage",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, monoprint collage, ink pull textures, layered cut paper, one-off print fragments",
"suffix": "brayer texture, ink ghosts, paper layering, soft registration drift, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media collage assembled from monoprint pulls and paper fragments. Lighting: soft gallery light keeping ink texture and layered edges visible. Texture: brayer marks, ink ghosts, and collage depth. Key cues: monoprint collage, ink pull textures, layered cut paper, one-off print fragments. Finish cues: brayer texture, ink ghosts, paper layering, soft registration drift. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"monoprint",
"collage"
]
},
{
"id": "mixed_paper_collage_mixed_media",
"name": "Paper Collage (Mixed Media)",
@@ -38,75 +310,63 @@
]
},
{
"id": "mixed_mixed_media_acrylic_ink",
"name": "Mixed Media Acrylic + Ink",
"id": "mixed_resin_sealed_collage",
"name": "Resin-Sealed Collage",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, mixed media, acrylic paint, ink linework, layered marks",
"suffix": "texture paste, splatter, handmade imperfections, high detail, tactile texture, paper grain, layered depth, cohesive composition"
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, resin-sealed collage, encapsulated layers, embedded fragments, glossy surface",
"suffix": "glass-like sheen, suspended depth, trapped inclusions, smooth surface reflections, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media artwork combining acrylic paint and ink linework. Lighting: soft gallery spotlights with gentle falloff and controlled highlights. Texture: layered paint, ink marks, tactile surface. Key cues: mixed media, acrylic paint, ink linework, layered marks. Finish cues: texture paste, splatter, handmade imperfections. Instruction: preserve the user prompt subject and add only stylistic cues."
"suffix": "Style: mixed media collage sealed under a glossy resin layer. Lighting: controlled light with smooth reflections and visible embedded depth. Texture: glass-like sheen, suspended fragments, and encapsulated layers. Key cues: resin-sealed collage, encapsulated layers, embedded fragments, glossy surface. Finish cues: glass-like sheen, suspended depth, trapped inclusions, smooth surface reflections. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media"
"mixed_media",
"resin",
"collage"
]
},
{
"id": "mixed_gel_plate_monoprint",
"name": "Gel Plate Monoprint",
"id": "mixed_sand_pigment_ground",
"name": "Sand and Pigment Ground",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, gel plate monoprint, ink layers, brayer texture, printmaking",
"suffix": "paper grain, ink transfer artifacts, handmade print, high detail, tactile texture, layered depth, cohesive composition"
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, sand and pigment ground, granular surface, earth pigments, rough substrate",
"suffix": "gritty relief, dry brush drag, mineral texture, matte layered surface, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: gel plate monoprint with layered ink textures. Lighting: soft gallery spotlights with gentle falloff and controlled highlights. Texture: ink transfer artifacts, paper grain, layered prints. Key cues: gel plate monoprint, ink layers, brayer texture, printmaking. Finish cues: paper grain, ink transfer artifacts, handmade print. Instruction: preserve the user prompt subject and add only stylistic cues."
"suffix": "Style: mixed media surface built from sanded ground and dry pigments. Lighting: raking light to reveal granular relief and dry brush drag. Texture: mineral grit, matte layering, and rough substrate variation. Key cues: sand and pigment ground, granular surface, earth pigments, rough substrate. Finish cues: gritty relief, dry brush drag, mineral texture, matte layered surface. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media"
"mixed_media",
"sand",
"pigment"
]
},
{
"id": "mixed_cyanotype_print_analog",
"name": "Cyanotype Print (Analog)",
"id": "mixed_scratchboard_ink_wash",
"name": "Scratchboard Ink Wash",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, cyanotype print, blue monochrome, sun print, paper texture",
"suffix": "grain, chemical texture, vintage print, high detail, tactile texture, paper grain, layered depth, cohesive composition"
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, scratchboard, ink wash, scraped highlights, black ground",
"suffix": "incised lines, white reveal marks, ink pooling, high contrast surface, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: analog cyanotype print on paper. Lighting: soft gallery spotlights with gentle falloff and controlled highlights. Mood: minimal, photographic, deep blue. Key cues: cyanotype print, blue monochrome, sun print, paper texture. Finish cues: grain, chemical texture, vintage print. Instruction: preserve the user prompt subject and add only stylistic cues."
"suffix": "Style: scratchboard mixed media with ink wash and scraped highlights. Lighting: controlled studio light emphasizing incised lines and contrast. Texture: sharp reveal marks, pooled ink, and a dense black ground. Key cues: scratchboard, ink wash, scraped highlights, black ground. Finish cues: incised lines, white reveal marks, ink pooling, high contrast surface. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media"
]
},
{
"id": "mixed_assemblage_artwork",
"name": "Assemblage Artwork",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, assemblage, found objects, mixed materials, handcrafted",
"suffix": "tactile texture, rough surfaces, layered depth, high detail, paper grain, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: assemblage artwork built from found objects and mixed materials. Lighting: soft gallery spotlights with gentle falloff and controlled highlights. Texture: rough surfaces, layered depth, tactile materials. Key cues: assemblage, found objects, mixed materials, handcrafted. Finish cues: tactile texture, rough surfaces, layered depth. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media"
"mixed_media",
"scratchboard",
"ink"
]
},
{
@@ -126,6 +386,106 @@
"tags": [
"mixed_media"
]
},
{
"id": "mixed_thread_stitched_paper_collage",
"name": "Thread-Stitched Paper Collage",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, thread-stitched paper collage, pierced paper, visible seams, layered cut paper",
"suffix": "thread relief, needle holes, paper grain, hand-sewn irregularity, high detail, tactile texture, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media paper collage held together with visible stitched seams. Lighting: soft raking light that reveals thread relief and pierced paper. Texture: needle holes, paper grain, and hand-sewn irregularity. Key cues: thread-stitched paper collage, pierced paper, visible seams, layered cut paper. Finish cues: thread relief, needle holes, paper grain, hand-sewn irregularity. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"thread",
"paper"
]
},
{
"id": "mixed_tissue_paper_layering",
"name": "Tissue Paper Layering",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, tissue paper layering, translucent color veils, wrinkled paper films, stacked fragments",
"suffix": "soft wrinkles, color veiling, feathered overlap edges, lightweight translucency, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media layering built from translucent tissue fragments. Lighting: soft top light preserving the color veils and overlap shadows. Texture: tissue wrinkles, feathered edges, and lightweight translucency. Key cues: tissue paper layering, translucent color veils, wrinkled paper films, stacked fragments. Finish cues: soft wrinkles, color veiling, feathered overlap edges, lightweight translucency. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"tissue_paper",
"layered"
]
},
{
"id": "mixed_vellum_overlay_assemblage",
"name": "Vellum Overlay Assemblage",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, vellum overlays, translucent sheets, stacked drawing layers, soft veiling",
"suffix": "semi-transparent layers, paper curl, subtle overlap shadows, airy depth, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media assemblage built from translucent vellum overlays. Lighting: soft top light creating delicate overlap shadows between sheets. Texture: vellum curl, semi-transparent stacking, and layered veiling. Key cues: vellum overlays, translucent sheets, stacked drawing layers, soft veiling. Finish cues: semi-transparent layers, paper curl, subtle overlap shadows, airy depth. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"vellum",
"assemblage"
]
},
{
"id": "mixed_wax_resist_layers",
"name": "Wax Resist Mixed Media",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, wax resist, layered inks, masked paper, drawn resist lines",
"suffix": "resist crackle, ink pooling, paper tooth, layered transparency, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media wax-resist artwork with masked lines and layered inks. Lighting: soft diffuse light that keeps paper texture and layered transparencies visible. Texture: resist crackle, pooled pigment, and masked edge variation. Key cues: wax resist, layered inks, masked paper, drawn resist lines. Finish cues: resist crackle, ink pooling, paper tooth, layered transparency. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"wax_resist",
"layered"
]
},
{
"id": "mixed_xerox_transfer_collage",
"name": "Xerox Transfer Collage",
"category": "Mixed Media",
"default": {
"prefix": "mixed media artwork, layered textures, handcrafted marks, tactile surface, xerox transfer collage, toner transfer, distressed paper, layered copy fragments",
"suffix": "toner wear, uneven transfer, matte paper texture, zine-like edges, high detail, tactile texture, paper grain, layered depth, cohesive composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: mixed media collage built from toner-transfer copy fragments. Lighting: flat diffuse light that keeps paper layers readable. Texture: toner wear, distressed edges, and matte paper grain. Key cues: xerox transfer collage, toner transfer, distressed paper, layered copy fragments. Finish cues: toner wear, uneven transfer, matte paper texture, zine-like edges. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"mixed_media",
"transfer",
"photocopy"
]
}
]
}
@@ -0,0 +1,325 @@
{
"version": 1,
"styles": [
{
"id": "decor_copper_foil_stained_glass",
"name": "Copper Foil Stained Glass",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, copper foil stained glass, soldered seams, colored glass pieces, translucent mosaic",
"suffix": "backlit glow, solder line detail, glass texture, jewel-toned translucency, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: copper-foil stained glass with soldered seams and translucent color. Lighting: backlit illumination creating jewel-like glow through the glass. Texture: solder line detail, subtle rippled glass, and luminous color panels. Key cues: copper foil stained glass, soldered seams, colored glass pieces, translucent mosaic. Finish cues: backlit glow, solder line detail, glass texture, jewel-toned translucency. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"glass",
"foil"
]
},
{
"id": "decor_pate_de_verre_glass",
"name": "Pate de Verre Glass",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, pate de verre glass, granular cast glass, soft translucency, powdered glass color",
"suffix": "frosted surface, subtle translucence, cast texture, powder-soft color, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: pate de verre glass with softly cast translucent color. Lighting: soft transmitted light with gentle highlights on frosted surfaces. Texture: granular cast glass, matte translucency, and powder-soft color depth. Key cues: pate de verre glass, granular cast glass, soft translucency, powdered glass color. Finish cues: frosted surface, subtle translucence, cast texture, powder-soft color. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"glass",
"cast"
]
},
{
"id": "decor_slumped_kiln_formed_glass",
"name": "Slumped Kiln-Formed Glass",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, slumped kiln-formed glass, flowed glass contours, heat-shaped form, smooth translucent surface",
"suffix": "soft edge rounding, subtle thickness variation, glass sheen, formed contour shadows, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: kiln-formed slumped glass with heat-shaped contours. Lighting: controlled studio light emphasizing contour thickness and translucency. Texture: smooth glass sheen, softened edges, and gentle thickness variation. Key cues: slumped kiln-formed glass, flowed glass contours, heat-shaped form, smooth translucent surface. Finish cues: soft edge rounding, subtle thickness variation, glass sheen, formed contour shadows. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"glass",
"kiln_formed"
]
},
{
"id": "decor_raku_crackle_ceramic",
"name": "Raku Crackle Ceramic",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, raku ceramic, crackle glaze, smoke-fired surface, irregular luster",
"suffix": "black crack lines, smoky glaze variation, porous clay texture, unpredictable firing marks, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: raku-fired ceramic with crackled glaze and smoky variation. Lighting: soft studio light revealing crackle networks and firing irregularities. Texture: black crack lines, porous clay, and unpredictable glaze shifts. Key cues: raku ceramic, crackle glaze, smoke-fired surface, irregular luster. Finish cues: black crack lines, smoky glaze variation, porous clay texture, unpredictable firing marks. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"ceramic",
"raku"
]
},
{
"id": "decor_pit_fired_ceramic",
"name": "Pit-Fired Ceramic",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, pit-fired ceramic, fire clouds, smoke marks, unglazed clay surface",
"suffix": "burnished sheen, smoke staining, earthy mineral variation, handmade surface, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: pit-fired ceramic with smoke clouds and earthy firing marks. Lighting: warm studio light showing burnished highlights and smoke variation. Texture: unglazed clay, mineral staining, and atmospheric fire marking. Key cues: pit-fired ceramic, fire clouds, smoke marks, unglazed clay surface. Finish cues: burnished sheen, smoke staining, earthy mineral variation, handmade surface. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"ceramic",
"pit_fired"
]
},
{
"id": "decor_celadon_glaze_stoneware",
"name": "Celadon Glaze Stoneware",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, celadon glaze, pale green stoneware, pooled glaze, refined ceramic surface",
"suffix": "glaze pooling, cool translucence, soft crackle, subtle sheen, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: celadon-glazed stoneware with cool translucent pooling. Lighting: soft controlled light preserving glaze depth and subtle crackle. Texture: pooled glaze, pale green translucence, and refined ceramic sheen. Key cues: celadon glaze, pale green stoneware, pooled glaze, refined ceramic surface. Finish cues: glaze pooling, cool translucence, soft crackle, subtle sheen. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"ceramic",
"celadon"
]
},
{
"id": "decor_crystalline_glaze_porcelain",
"name": "Crystalline Glaze Porcelain",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, crystalline glaze porcelain, starburst crystal blooms, glossy fired surface, porcelain body",
"suffix": "crystal clusters, gloss reflections, glaze depth, smooth porcelain surface, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: crystalline-glazed porcelain with visible starburst crystal blooms. Lighting: controlled highlights emphasizing glossy glaze and crystal structure. Texture: smooth porcelain, glossy depth, and flower-like crystal growth. Key cues: crystalline glaze porcelain, starburst crystal blooms, glossy fired surface, porcelain body. Finish cues: crystal clusters, gloss reflections, glaze depth, smooth porcelain surface. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"ceramic",
"crystalline_glaze"
]
},
{
"id": "decor_mishima_inlay_pottery",
"name": "Mishima Inlay Pottery",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, mishima inlay pottery, incised lines, contrasting slip inlay, refined ceramic pattern",
"suffix": "filled linework, matte glaze, fine carving detail, quiet surface contrast, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: Mishima pottery with incised patterns filled by contrasting slip. Lighting: soft studio light revealing carved linework and matte ceramic surface. Texture: filled incisions, quiet glaze, and fine carved detail. Key cues: mishima inlay pottery, incised lines, contrasting slip inlay, refined ceramic pattern. Finish cues: filled linework, matte glaze, fine carving detail, quiet surface contrast. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"ceramic",
"inlay"
]
},
{
"id": "decor_sgraffito_pottery",
"name": "Sgraffito Pottery",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, sgraffito pottery, scratched slip decoration, revealed clay body, layered ceramic surface",
"suffix": "incised motifs, matte slip texture, fine carving marks, earthy fired surface, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: sgraffito-decorated pottery with scratched slip and revealed clay. Lighting: soft angled light that catches carved ceramic marks. Texture: matte slip, incised motifs, and an earthy fired surface. Key cues: sgraffito pottery, scratched slip decoration, revealed clay body, layered ceramic surface. Finish cues: incised motifs, matte slip texture, fine carving marks, earthy fired surface. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"ceramic",
"sgraffito"
]
},
{
"id": "decor_hammered_copper_patina",
"name": "Hammered Copper Patina",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, hammered copper, hand-raised metal surface, aged patina, warm metallic tones",
"suffix": "hammer marks, oxidized color shifts, soft metallic sheen, worked metal relief, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: hammered copper decorative metalwork with aged patina. Lighting: warm studio light emphasizing hammered relief and patinated color shifts. Texture: hand-raised dimples, oxidized surface, and soft metallic sheen. Key cues: hammered copper, hand-raised metal surface, aged patina, warm metallic tones. Finish cues: hammer marks, oxidized color shifts, soft metallic sheen, worked metal relief. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"metal",
"patina"
]
},
{
"id": "decor_repousse_metalwork",
"name": "Repousse Metalwork",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, repousse metalwork, raised relief ornament, chased detailing, worked sheet metal",
"suffix": "relief contours, fine chased lines, aged metallic finish, ornamental depth, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: repousse metalwork with raised relief and chased detail. Lighting: directional studio light to reveal relief contours and fine tooling. Texture: raised ornament, chased lines, and aged metallic finish. Key cues: repousse metalwork, raised relief ornament, chased detailing, worked sheet metal. Finish cues: relief contours, fine chased lines, aged metallic finish, ornamental depth. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"metal",
"repousse"
]
},
{
"id": "decor_guilloche_enamel",
"name": "Guilloche Enamel",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, guilloche enamel, engine-turned pattern, translucent enamel layer, radiating metal geometry",
"suffix": "subsurface pattern shimmer, gloss enamel finish, precise engraved rhythm, luxury surface, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: guilloche enamel over engine-turned metal patterning. Lighting: controlled highlights that reveal the pattern beneath translucent enamel. Texture: glossy enamel depth, precise engraved rhythm, and subsurface shimmer. Key cues: guilloche enamel, engine-turned pattern, translucent enamel layer, radiating metal geometry. Finish cues: subsurface pattern shimmer, gloss enamel finish, precise engraved rhythm, luxury surface. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"enamel",
"guilloche"
]
},
{
"id": "decor_acid_etched_glass",
"name": "Acid-Etched Glass",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, acid-etched glass, frosted transparency, matte glass surface, ornamental etching",
"suffix": "soft diffusion, satin glass finish, etched motif edges, subtle translucence, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: acid-etched glass with frosted translucency and engraved motifs. Lighting: soft transmitted light with minimal glare to preserve the satin finish. Texture: matte frosted glass, subtle translucence, and etched boundaries. Key cues: acid-etched glass, frosted transparency, matte glass surface, ornamental etching. Finish cues: soft diffusion, satin glass finish, etched motif edges, subtle translucence. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"glass",
"etched"
]
},
{
"id": "decor_cut_crystal_faceting",
"name": "Cut Crystal Faceting",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, cut crystal, faceted glass, prismatic refraction, precision cutting",
"suffix": "sharp facets, caustic sparkle, high clarity, polished reflections, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: cut crystal decorative glass with sharp faceting and prismatic sparkle. Lighting: crisp controlled highlights and small caustics across polished facets. Texture: sharp facet edges, high clarity, and jewel-like reflections. Key cues: cut crystal, faceted glass, prismatic refraction, precision cutting. Finish cues: sharp facets, caustic sparkle, high clarity, polished reflections. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"glass",
"crystal"
]
},
{
"id": "decor_lost_wax_cast_bronze",
"name": "Lost-Wax Cast Bronze",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, lost-wax cast bronze, cast metal surface, hand-finished patina, sculptural metalwork",
"suffix": "cast texture, warm bronze highlights, subtle tooling marks, aged patina depth, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: lost-wax cast bronze with hand-finished patina. Lighting: warm directional light showing cast surface, tooling, and metallic depth. Texture: cast grain, hand-finished marks, and aged bronze patina. Key cues: lost-wax cast bronze, cast metal surface, hand-finished patina, sculptural metalwork. Finish cues: cast texture, warm bronze highlights, subtle tooling marks, aged patina depth. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"bronze",
"cast"
]
},
{
"id": "decor_pietra_dura_inlay",
"name": "Pietra Dura Stone Inlay",
"category": "Decorative Arts",
"default": {
"prefix": "decorative arts, handcrafted, artisan-made, museum-quality, pietra dura inlay, polished stone mosaic, hardstone floral inlay, precise stone fitting",
"suffix": "polished stone sheen, crisp inlay seams, rich mineral color, luxury surface detail, tactile surface, premium craftsmanship, controlled studio lighting, clean composition"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: pietra dura hardstone inlay with tightly fitted polished stone pieces. Lighting: soft controlled light revealing polished mineral surfaces and seam precision. Texture: crisp stone joins, polished sheen, and rich mineral color variation. Key cues: pietra dura inlay, polished stone mosaic, hardstone floral inlay, precise stone fitting. Finish cues: polished stone sheen, crisp inlay seams, rich mineral color, luxury surface detail. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"decorative_arts",
"stone",
"inlay"
]
}
]
}
+245
View File
@@ -0,0 +1,245 @@
{
"version": 1,
"styles": [
{
"id": "tech_exploded_cutaway_diagram",
"name": "Exploded Cutaway Diagram",
"category": "Technical",
"default": {
"prefix": "technical illustration, clean lines, precise geometry, exploded cutaway view, separated components, sectional clarity",
"suffix": "clean line hierarchy, crisp geometry, sectional detail, flat rendering, high legibility"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: technical cutaway illustration with separated components and crisp geometry. Lighting: flat even rendering with no dramatic shading. Composition: clear separations, sectional clarity, and measured structure. Key cues: exploded cutaway view, separated components, technical linework, precise geometry. Finish cues: clean line hierarchy, crisp geometry, sectional detail, flat rendering. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"technical",
"cutaway",
"diagram"
]
},
{
"id": "tech_orthographic_projection_plate",
"name": "Orthographic Projection Plate",
"category": "Technical",
"default": {
"prefix": "technical line illustration, orthographic views, aligned geometry, drafting clarity, multiview structure",
"suffix": "clean projection lines, measured spacing, flat rendering, crisp technical linework, restrained contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: orthographic technical illustration with aligned multiview geometry. Lighting: flat drafting presentation with minimal tonal variation. Composition: measured spacing, crisp geometry, and projection clarity. Key cues: orthographic views, aligned geometry, drafting clarity, multiview structure. Finish cues: clean projection lines, measured spacing, flat rendering, crisp technical linework. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"technical",
"orthographic",
"blueprint"
]
},
{
"id": "tech_patent_plate_diagram",
"name": "Patent Plate Diagram",
"category": "Technical",
"default": {
"prefix": "technical engraving style, fine drafting lines, archival patent aesthetic, precise mechanical illustration, paper grain",
"suffix": "fine linework, etched line texture, paper grain, restrained contrast, crisp technical detail"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: archival patent-style technical illustration with fine drafting lines and paper grain. Lighting: flat document light that keeps linework readable. Texture: etched line detail, paper grain, and restrained contrast. Key cues: technical engraving style, archival patent aesthetic, precise mechanical illustration, fine drafting lines. Finish cues: fine linework, etched line texture, paper grain, restrained contrast. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"technical",
"patent",
"diagram"
]
},
{
"id": "tech_section_elevation_drawing",
"name": "Section and Elevation Drawing",
"category": "Technical",
"default": {
"prefix": "technical architectural drawing, section cut hatching, elevation linework, measured geometry, clean drafting",
"suffix": "hatching clarity, precise line weights, crisp geometry, flat rendering, restrained document contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: architectural technical drawing with section hatching and elevation linework. Lighting: flat presentation emphasizing geometry and line-weight hierarchy. Composition: measured structure, clean hatch patterns, and crisp drafting. Key cues: section cut hatching, elevation linework, measured geometry, clean drafting. Finish cues: hatching clarity, precise line weights, crisp geometry, flat rendering. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"technical",
"section",
"elevation"
]
},
{
"id": "vec_gradient_mesh_illustration",
"name": "Gradient Mesh Illustration",
"category": "Vector",
"default": {
"prefix": "vector illustration, flat colors, clean shapes, crisp edges, smooth curves, gradient mesh vector, smooth tonal blends, scalable curves, clean vector shapes",
"suffix": "smooth color transitions, crisp bezier edges, clean layout, polished vector finish, consistent stroke weight, crisp edges, smooth curves"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: vector illustration built from gradient-mesh color transitions. Lighting: graphic flat-light treatment with emphasis on controlled tonal blending. Texture: none; clean scalable curves and polished vector edges. Key cues: gradient mesh vector, smooth tonal blends, scalable curves, clean vector shapes. Finish cues: smooth color transitions, crisp bezier edges, clean layout, polished vector finish. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"vector",
"gradient_mesh",
"illustration"
]
},
{
"id": "vec_monoline_icon_system",
"name": "Monoline Icon System",
"category": "Vector",
"default": {
"prefix": "vector illustration, clean shapes, monoline forms, uniform stroke weight, modular geometry, flat graphic style",
"suffix": "consistent line weight, crisp curves, balanced spacing, polished vector edges, clean flat finish"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: monoline vector illustration with modular geometry and uniform stroke weight. Lighting: flat graphic treatment with no material shading. Composition: balanced spacing, crisp curves, and scalable clarity. Key cues: monoline forms, uniform stroke weight, modular geometry, flat graphic style. Finish cues: consistent line weight, crisp curves, balanced spacing, polished vector edges. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"vector",
"icon",
"monoline"
]
},
{
"id": "vec_wayfinding_graphic_system",
"name": "Wayfinding Graphic System",
"category": "Vector",
"default": {
"prefix": "vector illustration, bold graphic symbols, flat colors, clean geometry, strong directional shapes, transit-graphic aesthetic",
"suffix": "high contrast clarity, clean alignment, modular spacing, crisp vector edges, polished flat finish"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: bold vector graphics with strong directional shapes and flat-color clarity. Lighting: flat design presentation with emphasis on readability. Composition: modular spacing, crisp geometry, and strong symbol silhouettes. Key cues: bold graphic symbols, clean geometry, strong directional shapes, transit-graphic aesthetic. Finish cues: high contrast clarity, clean alignment, modular spacing, crisp vector edges. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"vector",
"wayfinding",
"infographic"
]
},
{
"id": "vec_flat_isometric_explainer",
"name": "Flat Isometric Explainer",
"category": "Vector",
"default": {
"prefix": "vector illustration, flat isometric geometry, clean planes, simplified depth, crisp graphic shapes",
"suffix": "crisp isometric edges, controlled flat shadows, clean geometry, polished vector finish, restrained contrast"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: flat isometric vector illustration with clean geometric depth. Lighting: minimal graphic shading with controlled flat shadows. Composition: tidy geometry, crisp edges, and scalable forms. Key cues: flat isometric geometry, clean planes, simplified depth, crisp graphic shapes. Finish cues: crisp isometric edges, controlled flat shadows, clean geometry, polished vector finish. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"vector",
"isometric",
"diagram"
]
},
{
"id": "pix_one_bit_dithered_pixel",
"name": "One-Bit Dithered Pixel",
"category": "Pixel Art",
"default": {
"prefix": "pixel art, 16-bit, limited palette, pixel-perfect, one-bit pixel art, black and white dithering, high contrast sprite look, coarse pixel grid",
"suffix": "checker dither, crisp pixels, readable silhouette, hard-edged contrast, dithered shading, sprite-like clarity"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: one-bit pixel art using black and white dithering only. Lighting: implied through dither pattern rather than gradients. Texture: coarse pixel grid, checker dithers, and hard-edged contrast. Key cues: one-bit pixel art, black and white dithering, high contrast sprite look, coarse pixel grid. Finish cues: checker dither, crisp pixels, readable silhouette, hard-edged contrast. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"pixel_art",
"one_bit",
"dither"
]
},
{
"id": "pix_four_color_handheld_pixel",
"name": "Four-Color Handheld Pixel",
"category": "Pixel Art",
"default": {
"prefix": "pixel art, 16-bit, limited palette, pixel-perfect, four-color handheld pixel art, tiny tile shading, portable-screen look",
"suffix": "restricted tones, crisp pixels, readable forms, small-scale tile texture, dithered shading, readable silhouette, sprite-like clarity"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: limited-palette handheld pixel art with only four tonal values. Lighting: implied with tiny tile shading and palette restriction. Texture: small pixel grid, restrained tones, and compact sprite readability. Key cues: four-color handheld pixel art, limited palette, tiny tile shading, portable-screen look. Finish cues: restricted tones, crisp pixels, readable forms, small-scale tile texture. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"pixel_art",
"handheld",
"limited_palette"
]
},
{
"id": "pix_isometric_sprite_sheet",
"name": "Isometric Sprite Sheet",
"category": "Pixel Art",
"default": {
"prefix": "pixel art, 16-bit, limited palette, pixel-perfect, isometric sprite sheet, tile-based game art, modular sprites, pixel grid alignment",
"suffix": "crisp pixels, modular repetition, readable silhouette, dithered shading, sprite-like clarity"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: isometric sprite-sheet pixel art built from modular game tiles. Lighting: compact pixel shading supporting isometric form readability. Texture: crisp grid alignment, modular repetition, and dithered shading. Key cues: isometric sprite sheet, tile-based game art, modular sprites, pixel grid alignment. Finish cues: crisp pixels, modular repetition, readable silhouette, dithered shading. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"pixel_art",
"sprite_sheet",
"isometric"
]
},
{
"id": "pix_tilemap_interface",
"name": "Tilemap Interface",
"category": "Pixel Art",
"default": {
"prefix": "pixel art, limited palette, crisp pixel grid, retro game overlay aesthetic, chunky interface framing, modular tiles",
"suffix": "sharp pixels, clean tile seams, limited tones, compact shading, readable silhouette, sprite clarity"
},
"models": {
"flux_2_klein": {
"prefix": "",
"suffix": "Style: retro pixel-art overlay with chunky framing and modular tile structure. Lighting: flat pixel shading with palette-limited contrast. Texture: crisp grid alignment, clean tile seams, and sharp pixels. Key cues: retro game overlay aesthetic, chunky interface framing, modular tiles, crisp pixel grid. Finish cues: sharp pixels, clean tile seams, limited tones, compact shading. Instruction: preserve the user prompt subject and add only stylistic cues."
}
},
"tags": [
"pixel_art",
"tilemap",
"interface"
]
}
]
}
+11329 -16
View File
File diff suppressed because it is too large Load Diff
+150
View File
@@ -0,0 +1,150 @@
from __future__ import annotations
import unittest
import nodes
import style_library
class FakeTextEncoder:
def __init__(self):
self.last_text = None
self.last_tokens = None
def tokenize(self, text):
self.last_text = text
self.last_tokens = {"text": text}
return self.last_tokens
def encode_from_tokens_scheduled(self, tokens):
return [[tokens, {"source": tokens["text"]}]]
class PromptStylerTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.library = style_library.get_cached_style_library(load_policy=style_library.LOAD_POLICY_STRICT)
cls.stable_node = nodes.PromptStylerConditioning()
cls.primary_choice = next(choice for choice in cls.library.choices if choice.endswith("| cinematic_anamorphic"))
cls.secondary_choice = next(choice for choice in cls.library.choices if choice.endswith("| cin_film_noir"))
def test_apply_style_false_returns_raw_prompt(self):
encoder = FakeTextEncoder()
conditioning, styled_prompt = self.stable_node.encode(
prompt="red fox",
apply_style=False,
style=self.primary_choice,
template_variant="default",
style_id_override="",
text_encoder=encoder,
)
self.assertEqual("red fox", styled_prompt)
self.assertEqual("red fox", encoder.last_text)
self.assertEqual(1, len(conditioning))
def test_style_id_override_takes_precedence(self):
encoder = FakeTextEncoder()
conditioning, styled_prompt = self.stable_node.encode(
prompt="portrait",
apply_style=True,
style=self.secondary_choice,
template_variant="default",
style_id_override="cinematic_anamorphic",
text_encoder=encoder,
)
expected_style = style_library.resolve_style_legacy(
self.library,
choice=self.primary_choice,
style_id_override="cinematic_anamorphic",
)
expected_prompt, _details = style_library.compose_prompt_legacy(
"portrait",
expected_style,
template_variant="default",
)
self.assertEqual(expected_prompt, styled_prompt)
self.assertEqual(expected_prompt, encoder.last_text)
self.assertEqual(1, len(conditioning))
def test_legacy_default_variant_matches_expected_fixture(self):
encoder = FakeTextEncoder()
conditioning, styled_prompt = self.stable_node.encode(
prompt="cinematic film still, robot detective",
apply_style=True,
style=self.primary_choice,
template_variant="default",
style_id_override="",
text_encoder=encoder,
)
expected = (
"cinematic film still, professional cinematography, cinematic framing, carefully composed shot, "
"atmospheric depth, realistic lighting, cinematic, anamorphic look, shallow depth of field, "
"dramatic lighting, robot detective, film grain, subtle halation, rich contrast, color graded, "
"filmic color science, smooth highlight rolloff, deep blacks, subtle film grain, soft halation, "
"high dynamic range"
)
self.assertEqual(expected, styled_prompt)
self.assertEqual(expected, encoder.last_text)
self.assertEqual(1, len(conditioning))
def test_flux_variant_uses_variant_text_when_present(self):
encoder = FakeTextEncoder()
conditioning, styled_prompt = self.stable_node.encode(
prompt="robot detective",
apply_style=True,
style=self.primary_choice,
template_variant="flux_2_klein",
style_id_override="",
text_encoder=encoder,
)
expected_style = self.library.by_id["cinematic_anamorphic"]
expected_prompt, _details = style_library.compose_prompt_legacy(
"robot detective",
expected_style,
template_variant="flux_2_klein",
)
self.assertEqual(expected_prompt, styled_prompt)
self.assertEqual(expected_prompt, encoder.last_text)
self.assertEqual(1, len(conditioning))
def test_missing_variant_falls_back_to_default(self):
style = self.library.by_id["cinematic_anamorphic"]
default_prompt, _default_details = style_library.compose_prompt_legacy(
"robot detective",
style,
template_variant="default",
)
fallback_prompt, details = style_library.compose_prompt_legacy(
"robot detective",
style,
template_variant="missing_variant",
)
self.assertEqual(default_prompt, fallback_prompt)
self.assertEqual(style_library.DEFAULT_VARIANT, details["variant"])
def test_missing_text_encoder_raises_explicit_error(self):
with self.assertRaisesRegex(RuntimeError, "text_encoder input is invalid"):
self.stable_node.encode(
prompt="robot detective",
apply_style=True,
style=self.primary_choice,
template_variant="default",
style_id_override="",
text_encoder=None,
)
def test_unknown_style_id_override_raises_error(self):
encoder = FakeTextEncoder()
with self.assertRaisesRegex(ValueError, "Unknown style_id_override"):
self.stable_node.encode(
prompt="robot detective",
apply_style=True,
style=self.primary_choice,
template_variant="default",
style_id_override="does_not_exist",
text_encoder=encoder,
)
if __name__ == "__main__":
unittest.main()
+88
View File
@@ -0,0 +1,88 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ComfyUI PromptStyler - App Summary</title>
<style>
@page { size: Letter; margin: 0.42in; }
:root { --ink:#13212f; --muted:#526171; --rule:#d7dee7; --panel:#f4f7fa; --accent:#1f5f8b; --accent-soft:#dbeaf5; }
* { box-sizing: border-box; }
html, body { margin:0; padding:0; font-family:"Segoe UI", Arial, sans-serif; color:var(--ink); background:#fff; }
body { font-size:10.3pt; line-height:1.28; }
.hero { display:grid; grid-template-columns:1.55fr 0.95fr; gap:14px; margin-bottom:10px; padding-bottom:10px; border-bottom:1px solid var(--rule); }
.title { margin:0 0 4px 0; font-size:22pt; line-height:1.05; letter-spacing:-0.02em; }
.subtitle { margin:0; color:var(--muted); font-size:10pt; }
.badge { display:inline-block; margin-bottom:8px; padding:4px 8px; border-radius:999px; background:var(--accent-soft); color:var(--accent); font-size:8.5pt; font-weight:700; text-transform:uppercase; letter-spacing:0.06em; }
.meta { background:linear-gradient(180deg, #f9fbfd 0%, var(--panel) 100%); border:1px solid var(--rule); border-radius:10px; padding:10px 11px; }
.meta-row { margin:0 0 8px 0; }
.meta-row:last-child { margin-bottom:0; }
.label { display:block; margin-bottom:2px; color:var(--muted); font-size:8.2pt; font-weight:700; text-transform:uppercase; letter-spacing:0.05em; }
.value { margin:0; font-size:9.9pt; }
.grid { display:grid; grid-template-columns:1fr 1fr; gap:12px 14px; }
section { break-inside:avoid; }
h2 { margin:0 0 6px 0; padding-bottom:3px; border-bottom:1px solid var(--rule); font-size:10pt; letter-spacing:0.02em; text-transform:uppercase; }
p { margin:0 0 6px 0; }
ul { margin:0; padding-left:16px; }
li { margin:0 0 4px 0; }
.architecture { grid-column:1 / -1; }
.small { font-size:9.1pt; color:var(--muted); }
.code { font-family:Consolas, "Courier New", monospace; font-size:9pt; }
</style>
</head>
<body>
<div class="hero">
<div>
<div class="badge">Repo Summary</div>
<h1 class="title">ComfyUI PromptStyler</h1>
<p class="subtitle">Version 3.1.1. Custom ComfyUI node pack that applies curated style templates to a positive prompt and returns conditioning for downstream generation. It is structured around JSON style packs plus lightweight Python runtime code that composes the final prompt text at node execution time.</p>
</div>
<div class="meta">
<div class="meta-row">
<span class="label">Who It’s For</span>
<p class="value">Primary persona: ComfyUI users building image-generation workflows who want reusable, curated prompt styling without hand-writing long style suffixes.</p>
</div>
<div class="meta-row">
<span class="label">Install Shape</span>
<p class="value">Drop the repo into <span class="code">ComfyUI/custom_nodes/ComfyUI_PromptStyler</span> and reload custom nodes.</p>
</div>
</div>
</div>
<div class="grid">
<section>
<h2>What It Does</h2>
<ul>
<li>Exposes a ComfyUI node named <span class="code">PromptStyler: Prompt -&gt; Conditioning (Style Picker)</span>.</li>
<li>Loads style templates from <span class="code">styles/packs/*.json</span> in filename order.</li>
<li>Falls back to <span class="code">styles/styles_v1.json</span> if packs are missing or unusable.</li>
<li>Presents styles as categorized dropdown choices in <span class="code">Category | Name | id</span> format.</li>
<li>Supports direct <span class="code">style_id_override</span> and a <span class="code">template_variant</span> selector.</li>
<li>Builds a deduplicated styled prompt, then tokenizes and encodes it through the connected CLIP text encoder.</li>
<li>Ships maintainer tools to validate, audit, generate, sync, and add style packs.</li>
</ul>
</section>
<section>
<h2>How To Run</h2>
<ul>
<li>Clone or copy this folder into <span class="code">ComfyUI/custom_nodes/ComfyUI_PromptStyler</span>.</li>
<li>Restart ComfyUI or use "Reload custom nodes".</li>
<li>Add <span class="code">CheckpointLoaderSimple</span>, PromptStyler, and <span class="code">KSampler</span> to a workflow.</li>
<li>Wire <span class="code">CLIP -&gt; text_encoder</span>, your prompt text into <span class="code">prompt</span>, and <span class="code">positive -&gt; KSampler.positive</span>.</li>
<li>Optional repo validation: run <span class="code">tools/validate_styles.py</span>; audit with <span class="code">tools/audit_styles.py</span>.</li>
</ul>
</section>
<section class="architecture">
<h2>How It Works</h2>
<ul>
<li><span class="code">__init__.py</span> is the ComfyUI entry point: it reads <span class="code">VERSION</span> and exports node mappings.</li>
<li><span class="code">nodes.py</span> defines the runtime node that accepts the user prompt, style selection inputs, and a ComfyUI <span class="code">CLIP</span> text encoder connection.</li>
<li>The node scans <span class="code">styles/packs/</span>, merges JSON packs in sorted order, or falls back to the legacy single-file library.</li>
<li>Selected style prefix/suffix phrases are combined with the user prompt, normalized, and de-duplicated before encoding.</li>
<li>The encoded result is returned as <span class="code">CONDITIONING</span> plus the exact <span class="code">styled_prompt</span> string for inspection/debugging.</li>
<li><span class="code">style_library.py</span>, tests, and maintainer docs describe broader search/overlay logic, but an advanced node implementation was <strong>Not found in repo</strong> in the current <span class="code">nodes.py</span>.</li>
<li><span class="code">.github/workflows/ci.yml</span> runs version sync, style validation, style audit, and unit tests on pushes and pull requests.</li>
</ul>
<p class="small">If you need external services, hosted APIs, or a packaged installer: <strong>Not found in repo</strong>.</p>
</section>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

+161 -252
View File
@@ -1,63 +1,43 @@
"""
PromptStyler style pack tool.
This script helps you add more styles without editing python source by hand.
This script helps you add more styles without editing JSON by hand.
It writes (or updates) a JSON pack under styles/packs/, e.g.:
styles/packs/99_user_custom.json
Usage examples:
# List categories that exist (including ones supported by the generator)
C:\\Comfyui\\python_embeded\\python.exe .\\tools\\add_styles.py categories
# Add one style (core -> goes into prefix, details -> goes into suffix)
C:\\Comfyui\\python_embeded\\python.exe .\\tools\\add_styles.py add ^
--name "Anime Style (Soft Shading)" ^
--category "Anime/Manga" ^
--core "soft cel shading, warm palette, gentle gradients" ^
--details "clean line weight, subtle texture, high clarity" ^
--tags "anime, manga, illustration"
# Bulk add from CSV (columns: name,category,core,details,tags)
C:\\Comfyui\\python_embeded\\python.exe .\\tools\\add_styles.py bulk --csv .\\tools\\new_styles.csv
"""
from __future__ import annotations
import argparse
import csv
import importlib.util
import json
import os
import re
import sys
from collections import Counter
from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple
from typing import Any, Dict, List, Optional, Sequence
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
if ROOT not in sys.path:
sys.path.insert(0, ROOT)
from style_library import (
CATEGORY_BASE_PREFIX,
CATEGORY_BASE_SUFFIX,
LOAD_POLICY_STRICT,
StyleLibraryError,
available_categories,
flux_join_sentences,
load_style_library,
z_join,
)
PACKS_DIR = os.path.join(ROOT, "styles", "packs")
DEFAULT_PACK_PATH = os.path.join(PACKS_DIR, "99_user_custom.json")
def _load_generator_module():
"""
Import tools/generate_style_packs.py as a module so we can reuse:
- CATEGORY_BASE_PREFIX / CATEGORY_BASE_SUFFIX
- z_join() (which de-dupes)
"""
path = os.path.join(ROOT, "tools", "generate_style_packs.py")
spec = importlib.util.spec_from_file_location("promptstyler_generate_style_packs", path)
if spec is None or spec.loader is None:
raise RuntimeError(f"Unable to import generator module: {path}")
mod = importlib.util.module_from_spec(spec)
# dataclasses (on some Python versions) expects the module to already exist in sys.modules
# when evaluating annotations. Insert before exec_module to avoid NoneType errors.
sys.modules[spec.name] = mod
spec.loader.exec_module(mod)
return mod
def _slugify(name: str) -> str:
base = "".join(ch.lower() if ch.isalnum() else "_" for ch in (name or "")).strip("_")
while "__" in base:
@@ -66,12 +46,6 @@ def _slugify(name: str) -> str:
def _normalize_user_subcategory(value: str) -> str:
"""
Normalize a user subcategory string for "User/<subcategory>" categories:
- keep letters/numbers/spaces
- replace everything else with spaces
- collapse whitespace
"""
cleaned = "".join(ch if (ch.isalnum() or ch.isspace()) else " " for ch in (value or ""))
cleaned = " ".join(cleaned.split()).strip()
return cleaned or "Custom"
@@ -80,137 +54,52 @@ def _normalize_user_subcategory(value: str) -> str:
def _split_csv_list(value: Optional[str]) -> List[str]:
if not value:
return []
return [p.strip() for p in value.split(",") if p.strip()]
return [part.strip() for part in value.split(",") if part.strip()]
def _read_json(path: str) -> Dict[str, Any]:
with open(path, "r", encoding="utf-8") as f:
return json.load(f) or {}
with open(path, "r", encoding="utf-8") as handle:
return json.load(handle) or {}
def _write_json(path: str, data: Dict[str, Any]) -> None:
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=True)
f.write("\n")
with open(path, "w", encoding="utf-8") as handle:
json.dump(data, handle, indent=2, ensure_ascii=True)
handle.write("\n")
def _iter_pack_paths() -> Iterable[str]:
if not os.path.isdir(PACKS_DIR):
return []
for name in sorted(os.listdir(PACKS_DIR)):
if name.lower().endswith(".json"):
yield os.path.join(PACKS_DIR, name)
def _load_library():
return load_style_library(load_policy=LOAD_POLICY_STRICT)
def _load_all_styles() -> List[Dict[str, Any]]:
styles: List[Dict[str, Any]] = []
for path in _iter_pack_paths():
try:
data = _read_json(path)
except Exception:
# If a pack is broken, keep going; validate_styles.py will catch it.
continue
styles.extend(data.get("styles", []) or [])
return styles
def _existing_identity_sets() -> tuple[set[str], set[str]]:
library = _load_library()
return ({style.id for style in library.styles}, {style.name for style in library.styles})
def _ensure_unique_id(style_id: str, existing_ids: set) -> str:
def _ensure_unique_id(style_id: str, existing_ids: set[str]) -> str:
if style_id not in existing_ids:
return style_id
i = 2
while True:
cand = f"{style_id}_{i}"
if cand not in existing_ids:
return cand
candidate = f"{style_id}_{i}"
if candidate not in existing_ids:
return candidate
i += 1
def _ensure_unique_name(name: str, existing_names: set) -> str:
def _ensure_unique_name(name: str, existing_names: set[str]) -> str:
if name not in existing_names:
return name
i = 2
while True:
cand = f"{name} ({i})"
if cand not in existing_names:
return cand
candidate = f"{name} ({i})"
if candidate not in existing_names:
return candidate
i += 1
def _available_categories(existing_styles: Sequence[Dict[str, Any]], gen_mod) -> List[str]:
cats = set()
for s in existing_styles:
c = s.get("category")
if isinstance(c, str) and c.strip():
cats.add(c.strip())
# Also include categories we have base templates for.
cats.update((gen_mod.CATEGORY_BASE_PREFIX or {}).keys())
cats.update((gen_mod.CATEGORY_BASE_SUFFIX or {}).keys())
return sorted(cats, key=lambda x: x.casefold())
def _make_style_entry(
gen_mod,
*,
name: str,
category: str,
core: Sequence[str],
details: Sequence[str],
tags: Sequence[str],
style_id: Optional[str],
id_prefix: str,
flux_suffix: Optional[str] = None,
existing_ids: Optional[set] = None,
existing_names: Optional[set] = None,
) -> Dict[str, Any]:
if existing_ids is None or existing_names is None:
all_styles = _load_all_styles()
existing_ids = {str(s.get("id")) for s in all_styles if s.get("id") is not None}
existing_names = {str(s.get("name")) for s in all_styles if s.get("name") is not None}
base_id = style_id.strip() if style_id else f"{id_prefix}_{_slugify(name)}"
final_id = _ensure_unique_id(base_id, existing_ids)
final_name = _ensure_unique_name(name.strip(), existing_names)
existing_ids.add(final_id)
existing_names.add(final_name)
base_prefix = tuple((gen_mod.CATEGORY_BASE_PREFIX or {}).get(category, ()))
base_suffix = tuple((gen_mod.CATEGORY_BASE_SUFFIX or {}).get(category, ()))
prefix = gen_mod.z_join(tuple(base_prefix) + tuple(core))
suffix = gen_mod.z_join(tuple(details) + tuple(base_suffix))
tags2 = [t.strip() for t in tags if (t or "").strip()]
if not tags2:
tags2 = ["user"]
if flux_suffix is None:
core_hint = gen_mod.z_join(core[:12]) if core else ""
detail_hint = gen_mod.z_join(details[:12]) if details else ""
parts = [f"Style: {final_name}"]
if core_hint:
parts.append(f"Core cues: {core_hint}")
if detail_hint:
parts.append(f"Details: {detail_hint}")
parts.append("Lighting: coherent and intentional")
parts.append("Mood: consistent with the user prompt")
flux_suffix2 = gen_mod.flux_join_sentences(parts)
else:
flux_suffix2 = str(flux_suffix or "").strip()
if flux_suffix2 and not re.search(r"[.!?]$", flux_suffix2):
flux_suffix2 = flux_suffix2 + "."
entry: Dict[str, Any] = {
"id": final_id,
"name": final_name,
"category": category,
"default": {"prefix": prefix, "suffix": suffix},
"models": {"flux_2_klein": {"prefix": "", "suffix": flux_suffix2}},
"tags": tags2,
}
return entry
def _load_or_init_pack(path: str) -> Dict[str, Any]:
if os.path.isfile(path):
data = _read_json(path)
@@ -224,41 +113,109 @@ def _load_or_init_pack(path: str) -> Dict[str, Any]:
return {"version": 1, "styles": []}
def cmd_categories(args) -> int:
gen_mod = _load_generator_module()
styles = _load_all_styles()
cats = _available_categories(styles, gen_mod)
counts = Counter(s.get("category", "Uncategorized") for s in styles)
for c in cats:
print(f"{c} ({counts.get(c, 0)})")
def _make_style_entry(
*,
name: str,
category: str,
core: Sequence[str],
details: Sequence[str],
tags: Sequence[str],
style_id: Optional[str],
id_prefix: str,
flux_suffix: Optional[str] = None,
existing_ids: Optional[set[str]] = None,
existing_names: Optional[set[str]] = None,
) -> Dict[str, Any]:
if existing_ids is None or existing_names is None:
existing_ids, existing_names = _existing_identity_sets()
base_id = style_id.strip() if style_id else f"{id_prefix}_{_slugify(name)}"
final_id = _ensure_unique_id(base_id, existing_ids)
final_name = _ensure_unique_name(name.strip(), existing_names)
existing_ids.add(final_id)
existing_names.add(final_name)
prefix = z_join(tuple(CATEGORY_BASE_PREFIX.get(category, ())) + tuple(core))
suffix = z_join(tuple(details) + tuple(CATEGORY_BASE_SUFFIX.get(category, ())))
tags2 = [tag.strip() for tag in tags if (tag or "").strip()]
if not tags2:
tags2 = ["user"]
if flux_suffix is None:
parts = [f"Style: {final_name}"]
core_hint = z_join(core[:12]) if core else ""
detail_hint = z_join(details[:12]) if details else ""
if core_hint:
parts.append(f"Core cues: {core_hint}")
if detail_hint:
parts.append(f"Details: {detail_hint}")
parts.append("Lighting: coherent and intentional")
parts.append("Mood: consistent with the user prompt")
flux_suffix2 = flux_join_sentences(parts)
else:
flux_suffix2 = str(flux_suffix or "").strip()
if flux_suffix2 and not re.search(r"[.!?]$", flux_suffix2):
flux_suffix2 += "."
return {
"id": final_id,
"name": final_name,
"category": category,
"default": {"prefix": prefix, "suffix": suffix},
"models": {"flux_2_klein": {"prefix": "", "suffix": flux_suffix2}},
"tags": tags2,
}
def _read_bulk_csv(path: str) -> List[Dict[str, str]]:
rows: List[Dict[str, str]] = []
with open(path, "r", encoding="utf-8-sig", newline="") as handle:
reader = csv.DictReader(handle)
for row in reader:
if not row:
continue
rows.append({key.strip(): (value or "").strip() for key, value in row.items() if key})
return rows
def _read_bulk_json(path: str) -> List[Dict[str, Any]]:
data = _read_json(path)
if isinstance(data, dict) and "styles" in data:
data = data["styles"]
if not isinstance(data, list):
raise ValueError("JSON bulk file must be a list (or an object with a 'styles' list).")
return [item for item in data if isinstance(item, dict)]
def cmd_categories(_args) -> int:
library = _load_library()
counts = Counter(style.category or "Uncategorized" for style in library.styles)
for category in available_categories(library.styles):
print(f"{category} ({counts.get(category, 0)})")
return 0
def cmd_stats(args) -> int:
styles = _load_all_styles()
counts = Counter(s.get("category", "Uncategorized") for s in styles)
def cmd_stats(_args) -> int:
library = _load_library()
counts = Counter(style.category or "Uncategorized" for style in library.styles)
total = sum(counts.values())
print(f"styles {total}")
for c, n in counts.most_common():
print(f"{n:4d} {c}")
for category, count in counts.most_common():
print(f"{count:4d} {category}")
return 0
def cmd_add(args) -> int:
gen_mod = _load_generator_module()
pack_path = os.path.abspath(args.pack)
pack = _load_or_init_pack(pack_path)
all_styles = _load_all_styles()
existing_ids = {str(s.get("id")) for s in all_styles if s.get("id") is not None}
existing_names = {str(s.get("name")) for s in all_styles if s.get("name") is not None}
existing_ids, existing_names = _existing_identity_sets()
core = _split_csv_list(args.core) + _split_csv_list(args.prefix_extra)
details = _split_csv_list(args.details) + _split_csv_list(args.suffix_extra)
tags = _split_csv_list(args.tags)
entry = _make_style_entry(
gen_mod,
name=args.name,
category=args.category,
core=core,
@@ -273,7 +230,11 @@ def cmd_add(args) -> int:
pack["styles"].append(entry)
pack["styles"] = sorted(
pack["styles"],
key=lambda s: (str(s.get("category", "")).casefold(), str(s.get("name", "")).casefold(), str(s.get("id", ""))),
key=lambda style: (
str(style.get("category", "")).casefold(),
str(style.get("name", "")).casefold(),
str(style.get("id", "")),
),
)
_write_json(pack_path, pack)
@@ -282,39 +243,10 @@ def cmd_add(args) -> int:
return 0
def _read_bulk_csv(path: str) -> List[Dict[str, str]]:
rows: List[Dict[str, str]] = []
with open(path, "r", encoding="utf-8-sig", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
if not row:
continue
rows.append({k.strip(): (v or "").strip() for k, v in row.items() if k})
return rows
def _read_bulk_json(path: str) -> List[Dict[str, Any]]:
data = _read_json(path)
if isinstance(data, dict) and "styles" in data:
data = data["styles"]
if not isinstance(data, list):
raise ValueError("JSON bulk file must be a list (or an object with a 'styles' list).")
out: List[Dict[str, Any]] = []
for item in data:
if not isinstance(item, dict):
continue
out.append(item)
return out
def cmd_bulk(args) -> int:
gen_mod = _load_generator_module()
pack_path = os.path.abspath(args.pack)
pack = _load_or_init_pack(pack_path)
all_styles = _load_all_styles()
existing_ids = {str(s.get("id")) for s in all_styles if s.get("id") is not None}
existing_names = {str(s.get("name")) for s in all_styles if s.get("name") is not None}
existing_ids, existing_names = _existing_identity_sets()
if args.csv:
items = _read_bulk_csv(os.path.abspath(args.csv))
@@ -330,22 +262,15 @@ def cmd_bulk(args) -> int:
if not name or not category:
continue
core = _split_csv_list(item.get("core")) + _split_csv_list(item.get("prefix_extra"))
details = _split_csv_list(item.get("details")) + _split_csv_list(item.get("suffix_extra"))
tags = _split_csv_list(item.get("tags"))
style_id = (item.get("id") or "").strip() or None
flux_suffix = (item.get("flux") or item.get("flux_suffix") or "").strip() or None
entry = _make_style_entry(
gen_mod,
name=name,
category=category,
core=core,
details=details,
tags=tags,
style_id=style_id,
core=_split_csv_list(item.get("core")) + _split_csv_list(item.get("prefix_extra")),
details=_split_csv_list(item.get("details")) + _split_csv_list(item.get("suffix_extra")),
tags=_split_csv_list(item.get("tags")),
style_id=(item.get("id") or "").strip() or None,
id_prefix=args.id_prefix,
flux_suffix=flux_suffix,
flux_suffix=(item.get("flux") or item.get("flux_suffix") or "").strip() or None,
existing_ids=existing_ids,
existing_names=existing_names,
)
@@ -354,7 +279,11 @@ def cmd_bulk(args) -> int:
pack["styles"] = sorted(
pack["styles"],
key=lambda s: (str(s.get("category", "")).casefold(), str(s.get("name", "")).casefold(), str(s.get("id", ""))),
key=lambda style: (
str(style.get("category", "")).casefold(),
str(style.get("name", "")).casefold(),
str(style.get("id", "")),
),
)
_write_json(pack_path, pack)
print(f"Added {added} styles to {pack_path}")
@@ -364,55 +293,35 @@ def cmd_bulk(args) -> int:
def _prompt_line(label: str, *, default: Optional[str] = None) -> str:
if default is None:
return input(f"{label}: ").strip()
v = input(f"{label} [{default}]: ").strip()
return v if v else default
value = input(f"{label} [{default}]: ").strip()
return value if value else default
def cmd_wizard(args) -> int:
"""
Interactive style creation helper.
Writes (or updates) a JSON pack under styles/packs/, defaulting to:
styles/packs/99_user_custom.json
User styles are always categorized under: User/<subcategory>
"""
gen_mod = _load_generator_module()
pack_path = os.path.abspath(args.pack)
pack = _load_or_init_pack(pack_path)
all_styles = _load_all_styles()
existing_ids = {str(s.get("id")) for s in all_styles if s.get("id") is not None}
existing_names = {str(s.get("name")) for s in all_styles if s.get("name") is not None}
existing_ids, existing_names = _existing_identity_sets()
print("PromptStyler: User Style Wizard")
print("Tip: use comma-separated phrases (node splits default templates on ', ').")
print("")
subcat_raw = _prompt_line("User subcategory (creates category User/<subcategory>)", default="Custom")
subcat = _normalize_user_subcategory(subcat_raw)
subcat = _normalize_user_subcategory(
_prompt_line("User subcategory (creates category User/<subcategory>)", default="Custom")
)
category = f"User/{subcat}"
name = _prompt_line("Style name")
if not name.strip():
print("ERROR: style name is required")
return 2
core_raw = _prompt_line("Core descriptors (comma-separated)", default="")
details_raw = _prompt_line("Detail descriptors (comma-separated)", default="")
tags_raw = _prompt_line("Tags (comma-separated, optional)", default="")
core = _split_csv_list(core_raw)
details = _split_csv_list(details_raw)
tags = _split_csv_list(tags_raw)
# Always tag user styles for easier filtering/searching.
core = _split_csv_list(_prompt_line("Core descriptors (comma-separated)", default=""))
details = _split_csv_list(_prompt_line("Detail descriptors (comma-separated)", default=""))
tags = _split_csv_list(_prompt_line("Tags (comma-separated, optional)", default=""))
tags.extend(["user", _slugify(subcat)])
flux = _prompt_line("Flux suffix (optional; blank to auto-generate)", default="").strip() or None
flux_suffix = _prompt_line("Flux suffix (optional; blank to auto-generate)", default="").strip() or None
entry = _make_style_entry(
gen_mod,
name=name,
category=category,
core=core,
@@ -420,7 +329,7 @@ def cmd_wizard(args) -> int:
tags=tags,
style_id=None,
id_prefix=args.id_prefix,
flux_suffix=flux,
flux_suffix=flux_suffix,
existing_ids=existing_ids,
existing_names=existing_names,
)
@@ -444,7 +353,11 @@ def cmd_wizard(args) -> int:
pack["styles"].append(entry)
pack["styles"] = sorted(
pack["styles"],
key=lambda s: (str(s.get("category", "")).casefold(), str(s.get("name", "")).casefold(), str(s.get("id", ""))),
key=lambda style: (
str(style.get("category", "")).casefold(),
str(style.get("name", "")).casefold(),
str(style.get("id", "")),
),
)
_write_json(pack_path, pack)
@@ -471,16 +384,8 @@ def main(argv: Optional[List[str]] = None) -> int:
p_add.add_argument("--id", default=None, help="Optional explicit id (must be unique)")
p_add.add_argument("--name", required=True, help="Style display name")
p_add.add_argument("--category", required=True, help="Style category (as shown in the dropdown)")
p_add.add_argument(
"--core",
default="",
help="Comma-separated core descriptors (added to prefix after category base)",
)
p_add.add_argument(
"--details",
default="",
help="Comma-separated detail descriptors (added to suffix before category base)",
)
p_add.add_argument("--core", default="", help="Comma-separated core descriptors")
p_add.add_argument("--details", default="", help="Comma-separated detail descriptors")
p_add.add_argument("--prefix-extra", default="", help="Alias for --core")
p_add.add_argument("--suffix-extra", default="", help="Alias for --details")
p_add.add_argument("--tags", default="", help="Comma-separated tags")
@@ -499,7 +404,11 @@ def main(argv: Optional[List[str]] = None) -> int:
p_wizard.set_defaults(func=cmd_wizard)
args = parser.parse_args(argv)
return int(args.func(args))
try:
return int(args.func(args))
except StyleLibraryError as exc:
print(f"ERROR: {exc}")
return 2
if __name__ == "__main__":
+59 -98
View File
@@ -1,127 +1,91 @@
import json
import os
import re
import sys
from collections import Counter
from typing import Any, Dict, Iterable, List, Tuple
from typing import List, Tuple
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
PACKS_DIR = os.path.join(ROOT, "styles", "packs")
if ROOT not in sys.path:
sys.path.insert(0, ROOT)
from style_library import LOAD_POLICY_STRICT, StyleLibraryError, iter_pack_paths, load_style_library
def _iter_pack_paths() -> Iterable[str]:
if not os.path.isdir(PACKS_DIR):
return []
for name in sorted(os.listdir(PACKS_DIR)):
if name.lower().endswith(".json"):
yield os.path.join(PACKS_DIR, name)
def _load_all_styles() -> Tuple[List[Dict[str, Any]], List[str]]:
styles: List[Dict[str, Any]] = []
bad_packs: List[str] = []
for path in _iter_pack_paths():
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f) or {}
except Exception:
bad_packs.append(path)
continue
styles.extend(data.get("styles", []) or [])
return styles, bad_packs
ID_RE = re.compile(r"^[a-z0-9_]+$")
COMMA_RE = re.compile(r",(?!\s)")
BANNED_GEAR_TERMS = (
"softbox",
"soft box",
"strobe",
"speedlight",
"beauty dish",
"octabox",
"umbrella",
"ring light",
"ringlight",
"on-camera",
"on camera",
"tripod shot",
)
BANNED_GEAR_RES = [(term, re.compile(re.escape(term), re.IGNORECASE)) for term in BANNED_GEAR_TERMS]
def main() -> int:
styles, bad_packs = _load_all_styles()
try:
library = load_style_library(load_policy=LOAD_POLICY_STRICT)
except StyleLibraryError as exc:
print(f"ERROR: {exc}")
return 2
if bad_packs:
print("WARN: unable to read some packs:")
for p in bad_packs:
print(f" - {p}")
ids = [str(s.get("id", "")) for s in styles]
names = [str(s.get("name", "")) for s in styles]
cats = [str(s.get("category", "Uncategorized")) for s in styles]
styles = list(library.styles)
ids = [style.id for style in styles]
names = [style.name for style in styles]
categories = [style.category or "Uncategorized" for style in styles]
print(f"styles: {len(styles)}")
print(f"packs: {len(list(_iter_pack_paths()))}")
print(f"packs: {len(iter_pack_paths())}")
print("categories:")
for c, n in Counter(cats).most_common():
print(f" {n:4d} {c}")
for category, count in Counter(categories).most_common():
print(f" {count:4d} {category}")
warnings: List[str] = []
id_re = re.compile(r"^[a-z0-9_]+$")
bad_ids = [sid for sid in ids if sid and not id_re.match(sid)]
bad_ids = [style_id for style_id in ids if style_id and not ID_RE.match(style_id)]
if bad_ids:
warnings.append(f"bad_ids: {len(bad_ids)} (expected snake_case [a-z0-9_])")
empty_prefix = 0
empty_suffix = 0
missing_default = 0
missing_tags = 0
missing_flux_2_klein = 0
missing_variant = 0
comma_without_space: List[Tuple[str, str]] = []
comma_re = re.compile(r",(?!\s)")
banned_gear_hits: List[Tuple[str, str, str]] = []
banned_gear_terms = (
"softbox",
"soft box",
"strobe",
"speedlight",
"beauty dish",
"octabox",
"umbrella",
"ring light",
"ringlight",
"on-camera",
"on camera",
"tripod shot",
)
banned_gear_hits: List[Tuple[str, str, str]] = [] # (id, field, term)
banned_gear_res = [(t, re.compile(re.escape(t), re.IGNORECASE)) for t in banned_gear_terms]
for s in styles:
sid = str(s.get("id", ""))
default = s.get("default", None)
if not isinstance(default, dict):
missing_default += 1
continue
prefix = str(default.get("prefix", "") or "")
suffix = str(default.get("suffix", "") or "")
for style in styles:
prefix = style.prefix or ""
suffix = style.suffix or ""
if not prefix.strip():
empty_prefix += 1
if not suffix.strip():
empty_suffix += 1
for field, text in (("prefix", prefix), ("suffix", suffix)):
if comma_re.search(text):
comma_without_space.append((sid, field))
if COMMA_RE.search(text):
comma_without_space.append((style.id, field))
models = s.get("models", {})
flux_suffix = ""
if isinstance(models, dict):
flux = models.get("flux_2_klein", {})
if isinstance(flux, dict):
flux_suffix = str(flux.get("suffix", "") or "")
variant_prefix, variant_suffix = style.variants.get("flux_2_klein", ("", ""))
if "flux_2_klein" not in style.variants:
missing_variant += 1
for field, text in (("prefix", prefix), ("suffix", suffix), ("flux_2_klein.suffix", flux_suffix)):
for field, text in (("prefix", prefix), ("suffix", suffix), ("flux_2_klein.prefix", variant_prefix), ("flux_2_klein.suffix", variant_suffix)):
if not text:
continue
for term, rx in banned_gear_res:
if rx.search(text):
banned_gear_hits.append((sid, field, term))
for term, pattern in BANNED_GEAR_RES:
if pattern.search(text):
banned_gear_hits.append((style.id, field, term))
tags = s.get("tags", None)
if not isinstance(tags, list) or not any(str(t).strip() for t in tags):
if not style.tags:
missing_tags += 1
if not (isinstance(models, dict) and "flux_2_klein" in models):
missing_flux_2_klein += 1
if missing_default:
warnings.append(f"missing_or_bad_default: {missing_default}")
if empty_prefix:
warnings.append(f"empty_prefix: {empty_prefix}")
if empty_suffix:
@@ -130,25 +94,23 @@ def main() -> int:
warnings.append(f"comma_without_space: {len(comma_without_space)} (node splits on ', ')")
if missing_tags:
warnings.append(f"missing_tags: {missing_tags}")
if missing_flux_2_klein:
warnings.append(f"missing_models.flux_2_klein: {missing_flux_2_klein}")
if banned_gear_hits:
warnings.append(f"banned_gear_terms: {len({sid for sid, _field, _term in banned_gear_hits})} styles")
# Quick uniqueness sanity (validate_styles.py is the source of truth).
if missing_variant:
warnings.append(f"missing_models.flux_2_klein: {missing_variant}")
if len(set(ids)) != len(ids):
warnings.append("duplicate_ids: detected")
if len(set(names)) != len(names):
warnings.append("duplicate_names: detected")
if banned_gear_hits:
warnings.append(f"banned_gear_terms: {len({style_id for style_id, _field, _term in banned_gear_hits})} styles")
if warnings:
print("warnings:")
for w in warnings:
print(f" - {w}")
for warning in warnings:
print(f" - {warning}")
if banned_gear_hits:
print("banned gear term examples:")
for sid, field, term in banned_gear_hits[:20]:
print(f" - {sid} ({field}): {term}")
for style_id, field, term in banned_gear_hits[:20]:
print(f" - {style_id} ({field}): {term}")
else:
print("warnings: none")
@@ -157,4 +119,3 @@ def main() -> int:
if __name__ == "__main__":
raise SystemExit(main())
+11
View File
@@ -9,11 +9,17 @@ from __future__ import annotations
import json
import os
import sys
from dataclasses import dataclass
from typing import Dict, Iterable, List, Optional, Sequence
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
if ROOT not in sys.path:
sys.path.insert(0, ROOT)
from style_library import write_legacy_snapshot
PACKS_DIR = os.path.join(ROOT, "styles", "packs")
@@ -1626,8 +1632,13 @@ def build() -> None:
grades,
)
)
snapshot_total = write_legacy_snapshot()
print(f"Wrote style packs: {total} styles -> {PACKS_DIR}")
print(f"Updated legacy snapshot: {snapshot_total} styles -> {os.path.join(ROOT, 'styles', 'styles_v1.json')}")
if __name__ == "__main__":
build()
+23
View File
@@ -0,0 +1,23 @@
import os
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
if ROOT not in sys.path:
sys.path.insert(0, ROOT)
from style_library import LOAD_POLICY_STRICT, StyleLibraryError, write_legacy_snapshot
def main() -> int:
try:
total = write_legacy_snapshot(load_policy=LOAD_POLICY_STRICT)
except StyleLibraryError as exc:
print(f"ERROR: {exc}")
return 2
print(f"Updated styles/styles_v1.json with {total} merged styles")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+18 -45
View File
@@ -1,64 +1,37 @@
import json
import os
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
if ROOT not in sys.path:
sys.path.insert(0, ROOT)
from style_library import LOAD_POLICY_STRICT, StyleLibraryError, load_style_library
def main() -> int:
root = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
packs_dir = os.path.join(root, "styles", "packs")
data = {"styles": []}
if os.path.isdir(packs_dir):
for name in sorted(os.listdir(packs_dir)):
if not name.lower().endswith(".json"):
continue
path = os.path.join(packs_dir, name)
with open(path, "r", encoding="utf-8") as f:
part = json.load(f) or {}
data["styles"].extend(part.get("styles", []) or [])
else:
path = os.path.join(root, "styles", "styles_v1.json")
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
styles = data.get("styles", [])
if not isinstance(styles, list):
print("ERROR: styles must be a list")
try:
library = load_style_library(load_policy=LOAD_POLICY_STRICT)
except StyleLibraryError as exc:
print(f"ERROR: {exc}")
return 2
ids = set()
names = set()
ok = True
for i, s in enumerate(styles):
sid = s.get("id")
name = s.get("name")
if not sid or not name:
print(f"ERROR: styles[{i}] missing id or name")
for style in library.styles:
if style.id in ids:
print(f"ERROR: duplicate id: {style.id}")
ok = False
continue
if sid in ids:
print(f"ERROR: duplicate id: {sid}")
if style.name in names:
print(f"ERROR: duplicate name: {style.name}")
ok = False
if name in names:
print(f"ERROR: duplicate name: {name}")
ok = False
ids.add(sid)
names.add(name)
default = s.get("default", {})
if not isinstance(default, dict):
print(f"ERROR: styles[{i}].default must be an object")
ok = False
continue
for k in ("prefix", "suffix"):
if k not in default:
print(f"ERROR: styles[{i}].default missing {k}")
ok = False
ids.add(style.id)
names.add(style.name)
if not ok:
return 2
print(f"OK: {len(styles)} styles; {len(ids)} unique ids; {len(names)} unique names")
print(f"OK: {len(library.styles)} styles; {len(ids)} unique ids; {len(names)} unique names")
return 0
+77
View File
@@ -0,0 +1,77 @@
import argparse
import json
import os
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
VERSION_PATH = os.path.join(ROOT, "VERSION")
INIT_PATH = os.path.join(ROOT, "__init__.py")
MANIFEST_PATH = os.path.join(ROOT, ".release-please-manifest.json")
def _read_version() -> str:
with open(VERSION_PATH, "r", encoding="utf-8") as handle:
return handle.read().strip()
def _read_manifest_version() -> str:
with open(MANIFEST_PATH, "r", encoding="utf-8") as handle:
data = json.load(handle) or {}
return str(data.get(".", "")).strip()
def _write_manifest_version(version: str) -> None:
with open(MANIFEST_PATH, "w", encoding="utf-8") as handle:
json.dump({".": version}, handle, indent=2, ensure_ascii=True)
handle.write("\n")
def _init_reads_version_file() -> bool:
with open(INIT_PATH, "r", encoding="utf-8") as handle:
text = handle.read()
return "_read_version()" in text and 'os.path.join(here, "VERSION")' in text
def cmd_check(_args) -> int:
version = _read_version()
manifest_version = _read_manifest_version()
ok = True
if not _init_reads_version_file():
print("ERROR: __init__.py is no longer reading VERSION directly")
ok = False
if manifest_version != version:
print(f"ERROR: .release-please-manifest.json ({manifest_version}) does not match VERSION ({version})")
ok = False
if not ok:
return 2
print(f"OK: VERSION, __init__.py, and .release-please-manifest.json are aligned at {version}")
return 0
def cmd_write(_args) -> int:
version = _read_version()
_write_manifest_version(version)
print(f"Updated .release-please-manifest.json -> {version}")
return 0
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(prog="version_sync.py", description="Check or sync version metadata")
sub = parser.add_subparsers(dest="cmd", required=True)
p_check = sub.add_parser("check", help="Validate VERSION, __init__.py, and release manifest alignment")
p_check.set_defaults(func=cmd_check)
p_write = sub.add_parser("write", help="Write manifest version from VERSION")
p_write.set_defaults(func=cmd_write)
args = parser.parse_args(argv)
return int(args.func(args))
if __name__ == "__main__":
raise SystemExit(main())