# Character Studio — clothes, Hair, and visual briefs

Interface release **v1.0** · 2026-10-05. [Release notes](RELEASE_NOTES.md) ·
[v1+ roadmap](ROADMAP.md) · [current handoff](_activity.md).

A static, local workspace for **one Character, a wardrobe, and an editable visual
reference brief**. Open `index.html` through an HTTP host. No build, API key,
image provider, or paid service is required.

## Try it

1. Select Everyday, Formal, or Active from the example wardrobe. Only the selected
   outfit is used in the assembled prompt. The Character stays unchanged.
2. Choose an occasion and seed. Generate repeats that seed; Reroll advances a
   separate counter. Make three adds three selectable alternatives.
3. Hold a garment, hairstyle, or palette. Editing a garment description holds it
   automatically. A held incompatible garment produces an error with no partial
   wardrobe update. One-piece top/bottom references are held together.
4. Import an Oracle JSON record or batch and choose a Character. Review the visual
   projection, mapping notes, and marking regions. Full source Physicality is
   retained; only uncovered regional details enter the prompt.
5. Edit the prompt if desired. Subsequent changes update the assembled text but
   preserve your edit until you click **Use assembled prompt**.
6. Download the Studio JSON to resume later, the selected outfit JSON to reuse it,
   or the visible prompt text. Downloads/copy export exactly the shown text.

Studio imports replace the complete workspace. Oracle imports replace the Character
and retain the wardrobe. Outfit imports replace the wardrobe and retain the Character.
Importing a new document resets the prompt edit; export before replacement. Files
stay in browser memory, with no persistence across reload. Maximum import: 2 MB,
100 Characters in an Oracle batch, or 100 outfits in a Studio document.

**Outfit Generator:** `../outfitgen/index.html` uses the same engine and interface
without requiring a Character. Its text output is the readable outfit, not an image
prompt. Export Studio JSON there and import it here to continue. Runtime code and
catalogue are shared, not duplicated.

## Protecting unexported work

Both entry points show an unexported-work indicator and request a browser
leave-page warning after document changes. Generation, edits, holds, outfit
selection, markings/accessories, and manual prompt text are tracked. A fresh
example or full Studio JSON import establishes a clean baseline; reverting an
edit exactly also clears the guard. Browse/seed controls and derived prompt text
are transient and do not activate it by themselves.

Only **Export Studio JSON** clears the guard after its complete download starts.
Keep that file: the tool cannot detect a cancelled/incomplete disk download.
Outfit JSON, Blocks, prompt exports, and failed imports/downloads leave protection
active. Oracle/outfit imports modify part of a workspace and require a new full
Studio export. Restore example is an explicit reset, with its existing replace note.

Browser dialog text is generic and browser-controlled; prior user interaction is
usually required, and some mobile/browser exits suppress it. This is not autosave.
JSON remains the portable save format, with no persistence across reload.

## Removing outfits

**Remove selected outfit** removes the whole entry and selects its next neighbour
(or the previous one at the end). **Undo last removal** restores its original
position, edits, and holds, and selects it again. Undo is one level, held in this
tab only; another removal replaces it, and a successful import or example reset
clears it. Removed outfits are absent from JSON exports. Undo is disabled if the
wardrobe has reached its 100-outfit limit.

Keep at least one outfit: generate or import a replacement before removing the
last one. Character data and manually edited prompt text are preserved; the
assembled prompt updates to the new selection. Review retained manual text if it
still describes an outfit you removed.

## Outfit colours and Clothes Blocks

Each palette exposes **Primary, Secondary, Highlight, and Accessory** with a
labelled swatch and editable six-digit hex code. Primary colours main garments;
Secondary colours lower garments and underlayers; Accessory colours footwear and
outfit accessories. Highlight applies to the catalogue's named stitching/binding
details. Not every role needs to be used, and roles may share a colour.

Changing a swatch or hex value holds the palette and updates unheld garments
assigned to that role. Held garments, including description-edited garments,
retain their concrete base and trim colours. They may differ from the palette.
Signature Character accessories are unaffected. Invalid hex input leaves the
outfit unchanged and reports the error. When a hue changes, its editable record
uses the hex as its colour label rather than claiming the old descriptive name.

Open **Selected Clothes Block** in the wardrobe to preview, copy, or download the
selected `<clothes>` fragment. It includes four palette fields, a short third-person
NarrativeStatement, and all seven clothing slots with concrete hex colours where
known. Underlayers are included here even though they remain concealed in image
prompts. None, Unspecified, and one-piece garment references remain distinct.
In Studio, the statement uses the editable Character name. Standalone mode names
the outfit without inventing a wearer. Output escapes XML-significant characters
and normalizes multiline item descriptions into portable single-line fields.

The Block is **JMC's draft XML-container + variable notation**, downloaded as
`clothes-block.txt`. It is not a strict XML schema, full Rolesheet/Character Card,
or supported import format. Its palette fields are Webtools proposals, not edits
to Ballad. JSON remains the complete, reimportable handoff, including hairstyle,
coverage, sources, edits, and holds. Block export never replaces a manual prompt.

Garment text and assembled prompts prefer actual hex colours while retaining
materials and garment descriptions. Descriptive palette names stay in the UI.
Arbitrary user prose is preserved; words embedded in that prose are not rewritten
or automatically interpreted. Exact colour rendering remains model-dependent.

Older 0.1 Studio/outfit JSON remains importable. Three-swatch legacy palettes map
their accent to both Highlight and Accessory; missing swatches and ambiguous or
custom garment colours remain unresolved. Import/export does not invent colours
or mutate the original Character source. Starter fixtures intentionally exercise
these legacy records. New generation uses engine/catalogue 0.2. A 0.1 recipe
cannot replay with 0.2 data; use its full JSON snapshot, or the old pinned runtime,
to recover it. Imported old recipes stay recorded and are never silently upgraded.

## Hair catalogue and Hair Blocks

Confirm **Available length** and **Existing texture** in the Hair section. These
workspace inputs leave the imported Appearance intact; arbitrary prose is not
parsed into a guessed fit. Unknown inputs allow deliberate selection with review
notes, but random generation waits for both confirmations. No scalp hair and
currently unsupported cropped hair have no catalogue matches.

Filter by **Presentation, Occasion, Attitude, and Construction**. Length and
texture provide the other two sorts through compatibility. **Any** is unrestricted;
**Unspecified** denotes styles without a masc/fem presentation tag. These are
editorial browse labels, independent of gender, and excluded from generated
prompts and Hair Blocks. Your own imported/edited prose is used as written.

Browse the nine arrangements and their practical notes, then **Use selected style**,
or generate with the independent Hair seed. Generate repeats; Reroll advances a
Hair-only counter. Counter progress and browse controls are session controls;
the selected snapshot retains the exact resolved filters and seed in its recipe.
**Match outfit occasion** explicitly maps the selected outfit's Everyday to Casual,
and Formal/Active directly. Changing a filter never changes an applied style.
No matches means no matches: the tool doesn't relax filters behind your back.

Edit the main description and coordinated front/top/sides/back views. A catalogue
selection or any edit holds Hair; a held arrangement follows new outfits, while
release lets the next outfit use its normal ensemble text. **Restore Character
baseline** clears the snapshot and override, then holds that baseline choice.
Confirmed No scalp hair supplies an explicit baseline description; review any
contradictory hair prose in Visual identity. Missing baseline remains unspecified.
Changing confirmed inputs preserves existing styles and shows incompatibility
notes so you can resolve them. Imported Character sources never change.

**Selected Hair Block** has an escaped `<hair.style>` preview, copy fallback, and
`.txt` download with regional descriptions and requirements. Full Studio/outfit
JSON retains the selected record, source, recipe, edits, and hold. Old JSON and
the free-text hairstyle field remain supported. The image brief and readable
outfit use the same regional arrangement; manually edited prompts stay intact.

Styles do not create cuts, dye, extensions, length, or texture changes. Read the
reach, required-fastener, and headwear notes: they describe physical limits without
claiming an automatic collision detector. Fasteners are requirements rather than
automatically owned inventory; signature linking and new accessory colours remain
future work. See [the Hair source guide](../outfitgen/data/hair/README.md).

## Data and source boundaries

The catalogue contains six authored ensemble structures, sixteen reusable garments,
and four palettes. It is a **temperate-weather humanoid draft**, not Setting canon.
Active currently means travel. No population weights or gender/lineage clothing
rules are inferred. Individual `fixtures/outfits/*.json` examples were authored before
random selection; `fixtures/oracle.json` is a real Oracle v0.1 output from seed
`sapphire-studio`. `fixtures/character.json` adds an explicitly authored signature
clasp and shoulder tattoo to test deduplication and coverage, retaining the original
Oracle source unchanged. Oracle Physicality retains its Celine attribution.

The generator loads a small manifest at `../outfitgen/data/catalogue.json`:
palettes are shared, garments are grouped by slot, and ensembles are grouped by
Everyday/Formal/Active. See [the catalogue guide](../outfitgen/data/README.md).
The demo loads `fixtures/studio.json` as an internal manifest and assembles its
Character plus ordered outfit examples; see [the fixture guide](fixtures/README.md).
Repository manifests are not new user import formats. Exported JSON still contains
complete documents/records with no external file references.

Ballad `actorRolesheet.xml` Attire / Clothing.outfit and CharacterCard §7 inform
structure. This is not a Ballad parser or full CharacterCard exporter. Source
revisions and mappings live in `../../Draft/character-studio/SOURCES.md`.

The prompt excludes source NarrativeStatement, voice, scent, aura, and magic/Æran
prose. This avoids old clothes and nonvisual traits entering the visual projection.
Manual visual text is used as written: review it for old clothes or hidden markings.
Marking visibility is conservative and region-based, not a fabric simulation.
Garment-description edits preserve existing material/coverage metadata. Editing a
sleeve length in prose alone does not change what the composer thinks is covered.
Signature items deduplicate by explicit stable ID, not language similarity.

Replay reproduces generated content with the recorded engine/catalogue and held
snapshots. Editable names and post-generation changes are preserved by full-document
export; they are not retroactively changes to the original recipe. Workspace IDs
may receive a suffix to keep repeated results separately selectable.

## Files

- `app.js`, `styles.css`, `index.html`: Studio UI; Outfit Generator shares app/CSS.
- `compose.js`: versioned document validation, visibility, and prompt assembly.
- `adapters/oracle.js`: explicit Oracle v0.1 mapping and batch recognition.
- `schema/README.md`, `fixtures/`: contract and fixed examples.
- `../outfitgen/engine.js`, `../outfitgen/data/catalogue.json`: reusable generation.
- `../outfitgen/catalogue.js`, `examples.js`: ordered static-source loading and integrity checks.
- `../outfitgen/colours.js`, `blocks.js`: palette handling and Clothes serialization.
- `../outfitgen/hair.js`, `hair-contract.js`, `data/hair/`: shared Hair loading,
  compatibility, separate generation, snapshots, and Hair Block serialization.
- `workspace-guard.js`: full-document baseline and dirty-only unload listener.
- `ROADMAP.md`, `RELEASE_NOTES.md`: v1+ priorities and supported release scope.
- `tools/check.mjs`, `workspace-check.mjs`: actual app flows and unload tracking.

## Verification

Use Node 22+; install test-only dependencies in `tools` with `npm install`, then
run `npm test` there. Runtime itself has no external dependencies.

Guard checks cover clean startup, nested changes, exact reversion, listener
removal, complete versus partial exports, failed downloads/imports, full-file
restoration, and the Oracle-to-Studio generation/export/import flow in both modes.
These assert the handler and state; native browser dialogs remain a separate check.

Checks cover 240 category/seed cases; deterministic replay including held/manual
items; one-piece references; incompatible holds; source preservation; visible and
covered markings; accessory deduplication; malformed/unsupported imports; Oracle
batches; safe text rendering; manual prompt preservation; and JSON export. Both
Studio and Outfit Generator modes exercise the actual app in jsdom.

Checks also cover four-role hex edits, held base/trim preservation, legacy/unknown
colours, invalid input, Block selection/escaping/absence, clipboard fallback,
download content, and full JSON restoration in both modes.

The organisation checks compare assembled catalogue and demo records against
pre-split digests, plus 720 byte-identical generated/held/edited outputs per mode.
They exercise out-of-order file completion, duplicate IDs, broken references,
malformed JSON, missing files, and failed startup with controls kept disabled.

Hair checks cover the nine records, independent seeds/replay, explicit unknown
states, empty/incompatible matches, edited regions/holds, baseline restoration,
legacy text, safe Hair Blocks, JSON restoration, and failed Hair startup in both modes.

DOM tests do not establish responsive visual quality, operating-system clipboard
behaviour, real file downloads, or fidelity of image generation. JMC reported
successful hands-on use and image generations on 2026-09-27 (PR #12), then tested
and merged removal/Undo (PR #13), then palette/Blocks (PR #14). JMC accepted Hair PR #17 and reports it works as anticipated. The new unload
handler has DOM evidence; native-dialog/browser review remains a separate check.
