Pick lines on Voice-over (click; Ctrl / Cmd adds, Shift a run; Esc lets
go) and click a character in the list on the left to have it read them;
with none picked, the click changes who reads by default. On Voice-over
the list is the cast: narrower, characters with their colours, and a
click never leaves the page. Cards show who reads each line in its
colour, with that character's emotions; the bundle carries [name] where
the speaker changes, so generating, the cards' listen button, pushing,
dragging and copying follow it, and a file voiced again from the audio
gallery keeps its speakers. The character cards' Voice-over button is
gone. The workflow actions move to ui_script_workflow.js and the cast
helpers to ui_script_cast.js, keeping the director under 600 lines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For Anomalous_TTS interface 16: the default preset (Steady / Standard /
Lively), a preset for each pair of languages the characters can meet and the
presets' numbers, saved at once to the node's presets file; a Generation
settings button on character cards for the character's own preset, speed and
numbers, as a whole and per language. Older nodes get a pointer to the update
line instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Character cards show the version Anomalous_TTS reports (interface 15). The
v3/v4/v5 base models and vocoders are listed, counted and reminded of only
when a character or a dropped SoVITS file needs them; the import tells an
unreadable SoVITS file from a version an older node cannot use.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings -> Appearance -> Theme (and the ComfyUI setting
Anomalous.ModelBrowser.Theme, which replaces the Abyssal Scarlet switch)
picks Follow ComfyUI (default), Dark, Light or Abyssal Scarlet. Following
ComfyUI watches the dark-theme class ComfyUI sets on <html> for a dark
palette and repaints at once; an older Abyssal switch reads as Abyssal.
The light theme is html.amb-theme-light with its own --amb-* values. Hard
coded colors were turned into tokens whose dark values are the colors they
replace (checked: every stylesheet resolves to the same dark values):
rgba(var(--amb-ink-rgb), a) for tints, status shades, tooltip, float,
dialog, sunken, scrim and note tokens. Older rules and inline styles that
wrote a dark color now write var(--amb-lt-<role>, <that color>); the
--amb-lt-* tokens exist only in the light theme. Inside the plugin's
windows ComfyUI's palette variables (--bg-color, --fg-color ...) follow the
plugin's theme when the two differ. Surfaces over images stay dark, and the
image viewer gets the dark tokens back. The ready banner on a recipe reads
green instead of pink. Stylesheet cache versions are bumped.
Checked on a test ComfyUI in light and dark: home, models, gallery and its
viewer, workflows and a recipe's tabs, combos and a combo, prompts and the
docked drawer, voices, audio library, settings, scan, Model Check, current
node and its model picker, a model's detail with notes, share, save
recipe, update guide, versions, GPT-SoVITS settings and the prompt dialog;
light with a dark ComfyUI, and Abyssal Scarlet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Voices has two views switched at the top, Characters and Voice-over, sharing
the character list (clicking a character on Voice-over makes it speak); the
rail's Voices entry reopens the view used last, and the separate Voice-over
rail entry is gone. Generation settings: language as choices and speed as a
slider in view, the sampling numbers folded under fine-tuning with plain
names, no sideways scroll. A finished take says how long it took and links
to the audio gallery. Line cards show editing buttons on hover, the emotion's
reference clip sits beside its chips, and a character with only its main
voice shows no chips. The audio gallery shows each file's lines once and
can open them on Voice-over for another take; the list no longer repeats
the gallery. Also fixes the audio list staying empty after a reload.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Script Director was a drawer behind one of four equal buttons on
Voices, over the character cards, and its name did not say what it does.
It is now Voice-over (配音), a rail page between Voices and Audio
(ui_script_page.js mounts the director); each character card has a
Voice-over button that opens it with that character chosen, and Home's
"Give a character a voice" goes there. Generate is the main button;
pushing, dragging and copying sit under "Into your own workflow", and a
push or drop is one Ctrl+Z step. Plainer words throughout; the Voices
page keeps import, refresh and search.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- window.anomalous_open_voice(character) opens the browser on that
character's voice card (browser.openVoice); the Anomalous_TTS node's
character menu calls it.
- voiceGroupKey() is the one place that builds a GPT-SoVITS group key.
- The install card always says how to install (ComfyUI Manager or the
project page) and links the Anomalous_TTS repository, which is going
public; the "not published yet" text is gone.
- The voice card drag hint no longer promises a tag button on every row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The models page starts with a chip per models folder (Checkpoint, LoRA, ...)
and its count. A chip lists the whole folder with its subfolders
(GET /anomalous/type_models); a folder picked in the list narrows the grid
to that folder, shown as a crumb whose x goes back to the whole type. The
chosen type is remembered.
owner.modelScope now says what the grid lists; currentType/PathIdx/Subfolder
stay the folder of the model at hand, set from each card (focusModel), so
the editor, the scanner and "add to canvas" get the right subfolder.
Voice clips: the copy button reads "Copy {tag}" with a tooltip on what
pasting it does, and is gone for characters with only a main voice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The "!" guide on the audio page hid its tour banner with the hidden attribute,
but the banner's display:flex overrode it, so the banner showed and started the
image-side tour, whose buttons are not on the audio page. The audio guide now
has its own spotlight steps, and tour steps whose target is not on screen are
skipped. The batch import screen shows a folded example of a folder layout that
sorts cleanly: one folder per character, named after it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The F5-TTS voice library (input/F5-TTS scanning, voice upload with ffmpeg
conversion, romanization, Load Audio clip drags) and the engine switch are
removed. The audio page now works with one engine, Anomalous_TTS
(GPT-SoVITS): characters, drags onto its node, the Script Director with
direct generation, pronunciation and settings. The audio gallery and its
stream route stay (output and temp folders only). The root PATH hack that
existed for F5-TTS's ffmpeg lookup is gone. Recorded as AD-016.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Pronunciation" on a character card edits the node's `replace` setting
(script text -> what the character reads). Each row can be heard as the
character reads it now (the saved table applies) and as replaced, through
a Preview Audio run that stays out of the audio gallery; one preview at a
time, cancelled when the dialog closes. The file is re-read before editing
and every other settings field is kept. buildTtsPrompt gains `preview`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Script Director gets a Generate section for GPT-SoVITS characters
(ui_script_run.js): it queues the script as an API prompt with this
page's client id (audio_tts_run.js), shows queued/running/progress,
plays the result and saves it to output/audio/<character>/. The canvas
is never touched. A line card's retake button adds [take:N] to that
line so only it is regenerated; "New take of all" changes the seed.
Cancel deletes a queued job or interrupts it by prompt id when running
(checked against /queue, since the start event can arrive first).
Events that arrive before /prompt answers are buffered and replayed.
Language/speed start from the character's `defaults` and can be saved
back (read-modify-write, unknown fields kept). Needs Anomalous_TTS
interface 11; older versions get a hint and keep push-to-node.
The Arona multi-voice template read a file only one machine has; the
button, route, helper (alignedVoiceValue) and strings are removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The import form becomes a two-column workbench for one or many
characters. Files and whole folders can be dropped, or a folder on this
PC scanned (Anomalous_TTS browse?recursive=1, interface 6). Weights pair
up by name with the latest epoch kept; clips and line files follow their
folder or file-name prefix; anything unclear waits in an unassigned
tray. The left column lists the characters with a status dot, the right
shows the selected one with one compact line per clip and a picker (or
drag handle) to move files between characters. Ready characters are
imported one after another, each on its own, so a failure leaves the
others alone.
Grouping rules live in tts_import_groups.js, the draft pane in
ui_tts_import_draft.js and OS drop reading in ui_tts_file_drop.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sidebar groups characters by language (GPT-SoVITS) or folder
(F5-TTS) in foldable groups; a character unfolds into its clips and a
click plays one through the studio row. Characters drag onto nodes from
the sidebar, a red dot marks characters that need a look, a search box
shows from six characters on, and files dropped on a GPT-SoVITS
character open "Add files" for it.
GPT-SoVITS settings move to a dialog opened from the sidebar footer,
which carries the reminder dot; "Import character" moves into the
studio toolbar. Canvas drag helpers move to audio_voice_drag.js so the
sidebar and the cards share them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The setup card is one folded line; missing pretrained files (fetched on
first use anyway) only get a small dismissable dot once the studio's
characters need them, and the import form notes what the chosen
language will download. An empty GPT-SoVITS studio is a drop area with
the import button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fixed sections for GPT weight, SoVITS weight, reference clips and line
files; files dropped anywhere on the form go to their section. One GPT
and one SoVITS: a new one replaces the old, and from a batch the latest
epoch is kept (the others are named in a note).
- The save location is one line ("Saved to …/gpt_sovits/<name>") with
"Change location"; the library list only shows when asked for.
- Name clashes are shown while typing; an exact match offers "add to that
character instead", which switches the form to add mode in place.
- Unsupported SoVITS versions (v3/v4) are marked and block the import;
clips outside 3-10 s cannot be the main voice.
- After an import the studio scrolls to the new card and highlights it.
Tested: node tests/tts_setup_api.mjs (epoch pick, name clash, version
block, clip range added). Isolated ComfyUI in the browser: name clash and
switch to add mode, three GPT epochs -> e15 kept, SoVITS replaced by a v4
file -> blocked, short clips not selectable as main, add-mode import of a
clip as {平静} with the card highlighted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Uses Anomalous_TTS interface v3 so a new user can get going without
editing extra_model_paths.yaml or sorting model folders by hand.
- Setup card on the GPT-SoVITS studio: character libraries (add/remove),
pretrained files (per-item or all download with progress, or use an
existing GPT-SoVITS package) and missing Python packages with the
install command. Collapses to one line when ready.
- Import form: drop files, pick them in the browser, or pick them on the
ComfyUI machine (server-side path picker, no upload). Name, SoVITS
version, clip length, lines, main voice and language are filled in from
the node's inspect; emotions per clip. Uploads go in 8 MB chunks.
- "Add files" on a card, or a drop onto it, adds clips and emotions to
that character without replacing its main voice.
- Hidden for nodes without v3; disabled when the page is not opened on
the ComfyUI machine.
- New modules: tts_setup_api.js (calls + pure form rules),
ui_tts_setup.js, ui_tts_import.js, ui_tts_path_picker.js and
styles/12-tts-setup.css. ui_audio_studio.js only wires them in.
Tested: node tests/tts_setup_api.mjs (new, local) plus the audio engine,
node target and script rule tests. Isolated ComfyUI with both plugins in
the browser: setup card for a fresh user, package as pretrained source,
library add, import from local paths (lines found in all.txt), file drop
onto a card (chunked upload, add mode), cancel discards the upload.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Cache engine presence and voice groups (60 s) so domain switches, sidebar
clicks and the studio + sidebar pair cost no requests; saves and added
voices invalidate, Refresh also asks Anomalous_TTS to rescan (?refresh=1).
- Follow Anomalous_TTS interface v2: the list is a summary; the emotion
editor opens at once and loads the character's audio list in the
background (save waits for it, keeps unknown settings fields).
- Cards: show "角色/版本" as name + quiet suffix, one-line drag hint,
header wraps instead of squeezing.
- Docs: ARCHITECTURE audio_engines paragraph, audio-studio guide.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q6FStZ5hiKELvNH3WKgBnx
- audio_engines.js: F5-TTS and GPT-SoVITS (the separate Anomalous_TTS node
pack), detected through /object_info/<node class>. Both engines' voices are
normalised into one voice-group shape so cards, sidebar and Script Director
share one implementation.
- The audio page shows an engine switch; a missing engine stays listed as
"not installed" with install steps (no popups). The F5-TTS library still
works without the F5-TTS node; GPT-SoVITS is never scanned when missing.
- GPT-SoVITS cards come only from the node's HTTP contract: preview reference
clips, drag a character onto AnomalousTTS_CharacterSpeech, and edit the
emotion references ("Edit emotions"), saved through /anomalous_tts/settings
with unknown fields preserved and the node's validation messages shown.
- Drop targets and the Script Director are per engine: voices of one engine
are refused on the other engine's node with the reason.
- Audio gallery reads the character of GPT-SoVITS generations.
- Usage guide (in-app and docs/guides/audio-studio.md) covers both engines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W411Z69FC3em9CzpeQEub5
- audio_node_targets.js is the only list of nodes the audio studio writes
into: F5-TTS (Audio / Advanced / FromModel) takes a whole character, so the
card header sets its main voice; ComfyUI's Load Audio takes one exact clip,
so a row sets that file. Unlisted nodes are refused instead of matched by
widget name, and a single emotion clip is never written into F5-TTS's
sample (it would break {emotion} tags).
- While dragging, the hint says what releasing will do, or why a hovered node
is refused and what to do instead (shared bindMaterialDrag targetHint /
rejectHint).
- Script Director uses the same table and refuses nodes whose sample_audio is
linked.
- In the audio domain the header "!" opens a five-step usage guide;
docs/guides/audio-studio.md is the full guide (docs/guides is now tracked).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W411Z69FC3em9CzpeQEub5
Assigning a voice used to mean selecting a line card and then clicking a voice
row in the studio behind the drawer, which was not discoverable.
- pick one character at the top (F5-TTS needs one main voice per script); the
hint lists the emotions that character can voice
- after parsing, each line is a card with editable text, a preview button,
delete, and a row of emotion chips (main / happy / ...); a choice the current
character cannot voice stays visible and blocks the bundle instead of being
silently reset
- the footer shows the bundled speech and offers copy, push to the selected or
only F5TTSAudio node, and a handle to drag the bundle onto a node
- voice rows and the bundle both use the shared bindMaterialDrag (the browser
hides while dragging, the target node is highlighted); the studio's own
window drop listener and the row-click assignment path are removed
- bundling is pure (buildScriptPackage/usableEmotions in audio_script.js)
Verified: tests/audio_script_rules.mjs, jsdom walkthrough of parse -> chips ->
push -> character switch -> drag onto a node, domain navigation walkthrough;
other JS tests unchanged. Not yet checked in a live ComfyUI session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W411Z69FC3em9CzpeqEub5
ComfyUI-F5-TTS resolves {happy} to <sample stem>.happy.wav beside the selected
sample, so `Arona_happy.wav` + `{Arona_happy}` could never run. Voices now use
Character.wav (main, {main}) and Character.<emotion>.wav ({emotion}); the
catalog groups a main voice with its variants and marks tags that F5-TTS
cannot resolve (missing main, missing .txt, different format).
- script director: one character per script, untagged lines pinned to {main},
pushes sample (main voice, node's own combo spelling) and speech together,
confirms before overwriting and re-checks the node after the dialog
- pure rules (splitting, composing, planning, combo spelling) in audio_script.js
- one module instance per file: drop ?v= imports that duplicated
ui_audio_gallery/ui_audio_studio state, so studio playback stops the gallery
- all user/file-derived text goes through textContent (no innerHTML)
- studio/sidebar/gallery renders ignore stale responses; errors are shown
- gallery: count badge no longer overwrites the title, paging via "load more",
deletion errors surfaced, timeupdate instead of a 100ms timer
- sidebar: filter selection updates in place instead of refetching; the header
tabs, sidebar and domain switch share browser.switchAudioTab
- uploader: createViewScope lifecycle (Esc/overlay/close release listeners and
object URLs, Esc no longer reaches the parent), 409 -> confirm -> overwrite,
sends the pre-romanization text as original_text, no hard-coded character
- workflow template: confirm before replacing the canvas, clear message when
the template file is missing, align sample paths to the node's combo values
- static styles moved from inline JS into 11-audio-studio.css; strings localized
Verified: tests/audio_script_rules.mjs, tests/test_audio_catalog.py (15), a
jsdom walkthrough of studio/gallery/director/uploader with a stubbed app
(outside the repo), JS syntax and import/export link check. Not yet checked in
a live ComfyUI session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W411Z69FC3em9CzpeqEub5