0.13 now co-exist with "vanilla" V2 HQ coming

This commit is contained in:
CGlide
2026-06-23 03:17:58 +02:00
committed by GitHub
parent 6542586cdc
commit fd164b480c
21 changed files with 237 additions and 281 deletions
+32 -50
View File
@@ -1,50 +1,32 @@
from .ltx_keyframer import LTXKeyframer
from .multi_image_loader import MultiImageLoader
from .ltx_sequencer import LTXSequencer
from .speech_length_calculator import SpeechLengthCalculator
from .load_audio_ui import LoadAudioUI
from .load_video_ui import LoadVideoUI
from .ltx_director import LTXDirector
from .ltx_director_guide import LTXDirectorGuide
from comfy_api.latest import ComfyExtension, io
from typing_extensions import override
from .latent_slice import CleanLatentSlice
class PromptRelay(ComfyExtension):
@override
async def get_node_list(self) -> list[type[io.ComfyNode]]:
return [
LTXDirector,
LTXDirectorGuide
]
async def comfy_entrypoint() -> PromptRelay:
return PromptRelay()
NODE_CLASS_MAPPINGS = {
"LTXKeyframer": LTXKeyframer,
"MultiImageLoader": MultiImageLoader,
"LTXSequencer": LTXSequencer,
"SpeechLengthCalculator": SpeechLengthCalculator,
"LoadAudioUI": LoadAudioUI,
"LoadVideoUI": LoadVideoUI,
"LTXDirector": LTXDirector,
"LTXDirectorGuide": LTXDirectorGuide,
"CleanLatentSlice": CleanLatentSlice,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LTXKeyframer": "LTX Keyframer",
"MultiImageLoader": "Multi Image Loader",
"LTXSequencer": "LTX Sequencer",
"SpeechLengthCalculator": "Speech Length Calculator",
"LoadAudioUI": "Load Audio UI",
"LoadVideoUI": "Load Video UI",
"LTXDirector": "LTX Director",
"LTXDirectorGuide": "LTX Director Guide",
"CleanLatentSlice": "Clean Latent Slice",
}
WEB_DIRECTORY = "./js"
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY']
from .ltx_director import LTXDirector
from .ltx_director_guide import LTXDirectorGuide
from comfy_api.latest import ComfyExtension, io
from typing_extensions import override
from .latent_slice import CleanLatentSlice
class PromptRelay(ComfyExtension):
@override
async def get_node_list(self) -> list[type[io.ComfyNode]]:
return [
LTXDirector,
LTXDirectorGuide
]
async def comfy_entrypoint() -> PromptRelay:
return PromptRelay()
NODE_CLASS_MAPPINGS = {
"LTXDirectorCS": LTXDirector,
"LTXDirectorGuideCS": LTXDirectorGuide,
"CleanLatentSliceCS": CleanLatentSlice,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LTXDirectorCS": "LTX Director CS",
"LTXDirectorGuideCS": "LTX Director Guide CS",
"CleanLatentSliceCS": "Clean Latent Slice CS",
}
WEB_DIRECTORY = "./js"
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY']
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
+168 -168
View File
@@ -43,98 +43,98 @@ function clamp(v, min, max) { return Math.max(min, Math.min(max, v)); }
// --- Modern Dark/Grey UI CSS (ComfyUI Match) ---
const STYLES = `
.pr-wrapper { font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; display: flex; flex-direction: column; gap: 8px; width: 100%; height: 100%; box-sizing: border-box; padding-bottom: 4px; }
.pr-wrapper.drag-active { outline: 2px dashed #888; background: rgba(255, 255, 255, 0.05); border-radius: 6px; }
.pr-toolbar { display: flex; justify-content: space-between; align-items: center; padding: 2px 0px; flex-wrap: wrap; gap: 6px; flex-shrink: 0; }
.pr-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.pr-btn { background: #222; color: #e0e0e0; border: 1px solid #111; border-radius: 4px; padding: 6px 12px; font-size: 11px; font-weight: 500; cursor: pointer; display: flex; align-items: center; gap: 6px; transition: all 0.2s ease; }
.pr-btn:hover { background: #333; border-color: #555; }
.pr-btn-danger:hover { background: #4a1515; border-color: #cc4444; color: #ffaaaa; }
.pr-canvas { border-radius: 6px; border: 1px solid #111; background: #2a2a2a; cursor: pointer; width: 100%; outline: none; display: block; }
.pr-prop-container { display: flex; flex-direction: column; width: 100%; flex-grow: 1; min-height: 40px; }
.pr-prompt-area { width: 100%; height: 100%; background: #222; color: #e0e0e0; border: 1px solid #111; border-radius: 6px; padding: 8px; resize: none; font-size: 12px; line-height: 1.4; box-sizing: border-box; outline: none; transition: border-color 0.2s ease; }
.pr-prompt-area:focus { border-color: #888; }
.pr-audio-info { width: 100%; height: 100%; background: #181818; color: #aaa; border: 1px solid #111; border-radius: 6px; padding: 10px; font-size: 12px; line-height: 1.6; box-sizing: border-box; display: none; }
.pr-audio-info span { color: #fff; font-weight: 500; }
.pr-controls-group { background: #1e1e1e; border: 1px solid #333; border-radius: 6px; padding: 6px 10px; display: flex; flex-direction: column; gap: 4px; margin-bottom: 4px; box-sizing: border-box; width: 100%; flex-shrink: 0; }
.pr-strength-row { display: flex; align-items: center; gap: 12px; width: 100%; box-sizing: border-box; }
.pr-height-resizer { height: 6px; background: #2a2a2a; cursor: ns-resize; border-radius: 3px; margin: 2px 0; transition: background 0.15s; border: 1px solid #1e1e1e; }
.pr-height-resizer:hover { background: #444; border-color: #555; }
.pr-strength-label { font-size: 11px; font-weight: 600; color: #fff; white-space: nowrap; margin-left: auto; }
.pr-strength-slider { -webkit-appearance: none; appearance: none; width: 80px; height: 4px; background: #444; border-radius: 2px; outline: none; cursor: pointer; border: 1px solid #222; }
.pr-strength-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 12px; border-radius: 50%; background: #aaa; cursor: pointer; }
.pr-strength-slider:disabled { opacity: 0.3; cursor: not-allowed; }
.pr-strength-input { font-size: 12px; color: #fff; background: #222; border: 1px solid #444; border-radius: 4px; width: 52px; text-align: center; padding: 3px; }
.pr-strength-input::-webkit-outer-spin-button, .pr-strength-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pr-strength-input[type=number] { -moz-appearance: textfield; }
.pr-strength-input:disabled { opacity: 0.35; cursor: not-allowed; }
.pr-gap-menu { position: fixed; background: #1e1e1e; border: 1px solid #444; border-radius: 6px; padding: 4px; display: flex; flex-direction: column; gap: 4px; z-index: 9999; box-shadow: 0 4px 16px rgba(0,0,0,0.6); }
.pr-gap-menu-btn { background: #2a2a2a; color: #e0e0e0; border: 1px solid #333; border-radius: 4px; padding: 6px 14px; font-size: 11px; font-family: inherit; cursor: pointer; text-align: left; white-space: nowrap; display: flex; align-items: center; gap: 6px; transition: background 0.15s ease; }
.pr-gap-menu-btn:hover { background: #3a3a3a; border-color: #666; }
.pr-player-controls { display: flex; justify-content: center; align-items: center; gap: 12px; padding: 2px 0; flex-wrap: wrap; width: 100%; }
.pr-icon-btn { background: #2a2a2a; border: 1px solid #444; color: #eee; cursor: pointer; padding: 6px 12px; border-radius: 4px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; }
.pr-icon-btn * { pointer-events: none; }
.pr-icon-btn:hover { color: #fff; background: #3a3a3a; border-color: #666; }
.pr-icon-btn.active { color: #4fff8f; border-color: #4fff8f; background: #1a3a2a; }
.pr-seek-bar { -webkit-appearance: none; appearance: none; height: 6px; background: #444; border-radius: 3px; outline: none; cursor: pointer; border: 1px solid #222; }
.pr-seek-bar::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 14px; height: 14px; border-radius: 50%; background: #ff4444; cursor: pointer; border: 2px solid #222; }
.pr-timeline-viewport { width: 100%; overflow-x: auto; overflow-y: hidden; flex-shrink: 0; }
.pr-timeline-viewport::-webkit-scrollbar { height: 10px; }
.pr-timeline-viewport::-webkit-scrollbar-track { background: #151515; border-radius: 5px; }
.pr-timeline-viewport::-webkit-scrollbar-thumb { background: #444; border-radius: 5px; border: 1px solid #000; }
.pr-timeline-viewport::-webkit-scrollbar-thumb:hover { background: #666; border-color: #000; }
.pr-zoom-controls { display: flex; align-items: center; gap: 4px; margin-left: 12px; }
.pr-zoom-slider { width: 80px; -webkit-appearance: none; appearance: none; height: 4px; background: #444; border-radius: 2px; outline: none; cursor: pointer; }
.pr-zoom-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 12px; border-radius: 50%; background: #aaa; cursor: pointer; }
.pr-right-group { display: flex; align-items: center; gap: 12px; }
.pr-segment-bounds { font-size: 12px; color: #aaa; font-family: monospace; }
.pr-timecode { font-size: 14px; font-weight: bold; color: #e0e0e0; font-family: monospace; }
.pr-settings-menu { position: fixed; background: #1e1e1e; border: 1px solid #444; border-radius: 6px; padding: 10px; display: flex; flex-direction: column; gap: 8px; z-index: 9999; box-shadow: 0 4px 20px rgba(0,0,0,0.7); min-width: 220px; }
.pr-settings-title { font-size: 11px; font-weight: 600; color: #888; text-transform: uppercase; letter-spacing: 0.06em; padding-bottom: 4px; border-bottom: 1px solid #333; margin-bottom: 2px; }
.pr-settings-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.pr-settings-label { font-size: 12px; color: #bbb; flex: 1; white-space: nowrap; }
.pr-number-control { display: flex; align-items: center; border: 1px solid #444; border-radius: 4px; background: #2a2a2a; overflow: hidden; }
.pr-number-btn { background: #333; color: #aaa; border: none; width: 20px; height: 22px; cursor: pointer; font-size: 12px; display: flex; align-items: center; justify-content: center; transition: background 0.15s; user-select: none; }
.pr-number-btn:hover { background: #444; color: #fff; }
.pr-settings-input { background: transparent; color: #e0e0e0; border: none; padding: 0 4px; font-size: 12px; width: 50px; height: 22px; text-align: center; font-family: monospace; outline: none; -moz-appearance: textfield; }
.pr-settings-input::-webkit-outer-spin-button, .pr-settings-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pr-settings-select { background: #2a2a2a; color: #e0e0e0; border: 1px solid #444; border-radius: 4px; padding: 3px 4px; font-size: 12px; width: 98px; cursor: pointer; }
.pr-settings-divider { border: none; border-top: 1px solid #2a2a2a; margin: 2px 0; }
.pr-settings-toggle-btn { width: 100%; background: #252525; color: #aaa; border: 1px solid #333; border-radius: 4px; padding: 5px 8px; font-size: 11px; cursor: pointer; text-align: center; transition: all 0.15s; }
.pr-settings-toggle-btn:hover { background: #2e2e2e; color: #ccc; border-color: #555; }
.pr-settings-close-btn { background: transparent; color: #888; border: none; cursor: pointer; padding: 2px; display: flex; align-items: center; justify-content: center; border-radius: 4px; transition: all 0.15s; }
.pr-settings-close-btn:hover { color: #fff; background: rgba(255,255,255,0.1); }
.pr-segmented-control { display: flex; background: #1e1e1e; border: 1px solid #333; border-radius: 6px; padding: 2px; width: 110px; height: 22px; align-items: center; box-sizing: border-box; }
.pr-segment { flex: 1; text-align: center; font-size: 10px; font-weight: 500; line-height: 18px; cursor: pointer; border-radius: 4px; color: #888; transition: all 0.15s ease; }
.pr-segment.active { background: #333; color: #fff; }
.pr-segment:hover:not(.active) { color: #ccc; }
.pr-characters-container { display: flex; justify-content: space-between; gap: 12px; margin-bottom: 8px; box-sizing: border-box; width: 100%; flex-shrink: 0; }
.pr-character-slot { flex: 1; background: #1e1e1e; border: 1.5px dashed #444; border-radius: 8px; height: 120px; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; padding: 4px; position: relative; cursor: pointer; overflow: hidden; transition: all 0.2s ease; box-sizing: border-box; }
.pr-character-slot:hover { border-color: #666; background: #252525; }
.pr-character-slot.drag-over { border-color: #4fff8f; background: rgba(79, 255, 143, 0.05); }
.pr-character-label { font-size: 10px; font-weight: bold; color: #888; margin-bottom: 2px; pointer-events: none; }
.pr-character-placeholder { font-size: 9px; color: #666; text-align: center; pointer-events: none; margin-top: 10px; }
.pr-character-previews-row { display: flex; width: 100%; height: 52px; gap: 4px; position: relative; }
.pr-character-preview-wrapper { flex: 1; height: 100%; position: relative; overflow: hidden; border-radius: 3px; background: #111; }
.pr-character-preview { width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.pr-character-delete { position: absolute; top: 2px; right: 2px; background: rgba(0, 0, 0, 0.85); color: #ff4444; border: none; border-radius: 50%; width: 14px; height: 14px; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 9px; transition: background 0.15s; z-index: 10; padding: 0; }
.pr-character-delete:hover { background: #ff4444; color: #fff; }
.pr-character-validate-btn { position: absolute; bottom: -8px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.85); color: #e0e0e0; border: 1px solid #444; border-radius: 3px; padding: 2px 8px; font-size: 9px; font-weight: bold; cursor: pointer; transition: all 0.15s; z-index: 20; }
.pr-character-validate-btn:hover { background: #4fff8f; color: #000; border-color: #4fff8f; }
.pr-character-validate-btn.loading { background: #333; color: #888; cursor: wait; pointer-events: none; }
.pr-character-desc { width: 100%; height: 38px; background: #111; color: #e0e0e0; border: 1px solid #333; border-radius: 4px; font-size: 9px; resize: none; box-sizing: border-box; padding: 2px 4px; margin-top: 10px; outline: none; font-family: inherit; z-index: 10; }
.pr-character-desc:focus { border-color: #4fff8f; }
.pr-autocomplete-menu { position: fixed; background: #181818; border: 1px solid #444; border-radius: 6px; padding: 4px; display: flex; flex-direction: column; gap: 2px; z-index: 100000; box-shadow: 0 4px 16px rgba(0,0,0,0.6); min-width: 180px; max-height: 200px; overflow-y: auto; }
.pr-autocomplete-item { background: #252525; color: #aaa; border: 1px solid #333; border-radius: 4px; padding: 6px 12px; font-size: 11px; font-family: monospace; cursor: pointer; text-align: left; display: flex; align-items: center; justify-content: space-between; transition: all 0.15s ease; }
.pr-autocomplete-item:hover, .pr-autocomplete-item.active { background: #1c222d; color: #4fff8f; border-color: #4fff8f; }
.pr-autocomplete-item span { font-weight: bold; font-size: 12px; }
.pr-autocomplete-item small { color: #777; font-size: 10px; }
.pr-autocomplete-item.active small { color: #4fff8f; opacity: 0.8; }
.prcs-wrapper { font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; display: flex; flex-direction: column; gap: 8px; width: 100%; height: 100%; box-sizing: border-box; padding-bottom: 4px; }
.prcs-wrapper.drag-active { outline: 2px dashed #888; background: rgba(255, 255, 255, 0.05); border-radius: 6px; }
.prcs-toolbar { display: flex; justify-content: space-between; align-items: center; padding: 2px 0px; flex-wrap: wrap; gap: 6px; flex-shrink: 0; }
.prcs-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.prcs-btn { background: #222; color: #e0e0e0; border: 1px solid #111; border-radius: 4px; padding: 6px 12px; font-size: 11px; font-weight: 500; cursor: pointer; display: flex; align-items: center; gap: 6px; transition: all 0.2s ease; }
.prcs-btn:hover { background: #333; border-color: #555; }
.prcs-btn-danger:hover { background: #4a1515; border-color: #cc4444; color: #ffaaaa; }
.prcs-canvas { border-radius: 6px; border: 1px solid #111; background: #2a2a2a; cursor: pointer; width: 100%; outline: none; display: block; }
.prcs-prop-container { display: flex; flex-direction: column; width: 100%; flex-grow: 1; min-height: 40px; }
.prcs-prompt-area { width: 100%; height: 100%; background: #222; color: #e0e0e0; border: 1px solid #111; border-radius: 6px; padding: 8px; resize: none; font-size: 12px; line-height: 1.4; box-sizing: border-box; outline: none; transition: border-color 0.2s ease; }
.prcs-prompt-area:focus { border-color: #888; }
.prcs-audio-info { width: 100%; height: 100%; background: #181818; color: #aaa; border: 1px solid #111; border-radius: 6px; padding: 10px; font-size: 12px; line-height: 1.6; box-sizing: border-box; display: none; }
.prcs-audio-info span { color: #fff; font-weight: 500; }
.prcs-controls-group { background: #1e1e1e; border: 1px solid #333; border-radius: 6px; padding: 6px 10px; display: flex; flex-direction: column; gap: 4px; margin-bottom: 4px; box-sizing: border-box; width: 100%; flex-shrink: 0; }
.prcs-strength-row { display: flex; align-items: center; gap: 12px; width: 100%; box-sizing: border-box; }
.prcs-height-resizer { height: 6px; background: #2a2a2a; cursor: ns-resize; border-radius: 3px; margin: 2px 0; transition: background 0.15s; border: 1px solid #1e1e1e; }
.prcs-height-resizer:hover { background: #444; border-color: #555; }
.prcs-strength-label { font-size: 11px; font-weight: 600; color: #fff; white-space: nowrap; margin-left: auto; }
.prcs-strength-slider { -webkit-appearance: none; appearance: none; width: 80px; height: 4px; background: #444; border-radius: 2px; outline: none; cursor: pointer; border: 1px solid #222; }
.prcs-strength-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 12px; border-radius: 50%; background: #aaa; cursor: pointer; }
.prcs-strength-slider:disabled { opacity: 0.3; cursor: not-allowed; }
.prcs-strength-input { font-size: 12px; color: #fff; background: #222; border: 1px solid #444; border-radius: 4px; width: 52px; text-align: center; padding: 3px; }
.prcs-strength-input::-webkit-outer-spin-button, .prcs-strength-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.prcs-strength-input[type=number] { -moz-appearance: textfield; }
.prcs-strength-input:disabled { opacity: 0.35; cursor: not-allowed; }
.prcs-gap-menu { position: fixed; background: #1e1e1e; border: 1px solid #444; border-radius: 6px; padding: 4px; display: flex; flex-direction: column; gap: 4px; z-index: 9999; box-shadow: 0 4px 16px rgba(0,0,0,0.6); }
.prcs-gap-menu-btn { background: #2a2a2a; color: #e0e0e0; border: 1px solid #333; border-radius: 4px; padding: 6px 14px; font-size: 11px; font-family: inherit; cursor: pointer; text-align: left; white-space: nowrap; display: flex; align-items: center; gap: 6px; transition: background 0.15s ease; }
.prcs-gap-menu-btn:hover { background: #3a3a3a; border-color: #666; }
.prcs-player-controls { display: flex; justify-content: center; align-items: center; gap: 12px; padding: 2px 0; flex-wrap: wrap; width: 100%; }
.prcs-icon-btn { background: #2a2a2a; border: 1px solid #444; color: #eee; cursor: pointer; padding: 6px 12px; border-radius: 4px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; }
.prcs-icon-btn * { pointer-events: none; }
.prcs-icon-btn:hover { color: #fff; background: #3a3a3a; border-color: #666; }
.prcs-icon-btn.active { color: #4fff8f; border-color: #4fff8f; background: #1a3a2a; }
.prcs-seek-bar { -webkit-appearance: none; appearance: none; height: 6px; background: #444; border-radius: 3px; outline: none; cursor: pointer; border: 1px solid #222; }
.prcs-seek-bar::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 14px; height: 14px; border-radius: 50%; background: #ff4444; cursor: pointer; border: 2px solid #222; }
.prcs-timeline-viewport { width: 100%; overflow-x: auto; overflow-y: hidden; flex-shrink: 0; }
.prcs-timeline-viewport::-webkit-scrollbar { height: 10px; }
.prcs-timeline-viewport::-webkit-scrollbar-track { background: #151515; border-radius: 5px; }
.prcs-timeline-viewport::-webkit-scrollbar-thumb { background: #444; border-radius: 5px; border: 1px solid #000; }
.prcs-timeline-viewport::-webkit-scrollbar-thumb:hover { background: #666; border-color: #000; }
.prcs-zoom-controls { display: flex; align-items: center; gap: 4px; margin-left: 12px; }
.prcs-zoom-slider { width: 80px; -webkit-appearance: none; appearance: none; height: 4px; background: #444; border-radius: 2px; outline: none; cursor: pointer; }
.prcs-zoom-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 12px; border-radius: 50%; background: #aaa; cursor: pointer; }
.prcs-right-group { display: flex; align-items: center; gap: 12px; }
.prcs-segment-bounds { font-size: 12px; color: #aaa; font-family: monospace; }
.prcs-timecode { font-size: 14px; font-weight: bold; color: #e0e0e0; font-family: monospace; }
.prcs-settings-menu { position: fixed; background: #1e1e1e; border: 1px solid #444; border-radius: 6px; padding: 10px; display: flex; flex-direction: column; gap: 8px; z-index: 9999; box-shadow: 0 4px 20px rgba(0,0,0,0.7); min-width: 220px; }
.prcs-settings-title { font-size: 11px; font-weight: 600; color: #888; text-transform: uppercase; letter-spacing: 0.06em; padding-bottom: 4px; border-bottom: 1px solid #333; margin-bottom: 2px; }
.prcs-settings-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.prcs-settings-label { font-size: 12px; color: #bbb; flex: 1; white-space: nowrap; }
.prcs-number-control { display: flex; align-items: center; border: 1px solid #444; border-radius: 4px; background: #2a2a2a; overflow: hidden; }
.prcs-number-btn { background: #333; color: #aaa; border: none; width: 20px; height: 22px; cursor: pointer; font-size: 12px; display: flex; align-items: center; justify-content: center; transition: background 0.15s; user-select: none; }
.prcs-number-btn:hover { background: #444; color: #fff; }
.prcs-settings-input { background: transparent; color: #e0e0e0; border: none; padding: 0 4px; font-size: 12px; width: 50px; height: 22px; text-align: center; font-family: monospace; outline: none; -moz-appearance: textfield; }
.prcs-settings-input::-webkit-outer-spin-button, .prcs-settings-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.prcs-settings-select { background: #2a2a2a; color: #e0e0e0; border: 1px solid #444; border-radius: 4px; padding: 3px 4px; font-size: 12px; width: 98px; cursor: pointer; }
.prcs-settings-divider { border: none; border-top: 1px solid #2a2a2a; margin: 2px 0; }
.prcs-settings-toggle-btn { width: 100%; background: #252525; color: #aaa; border: 1px solid #333; border-radius: 4px; padding: 5px 8px; font-size: 11px; cursor: pointer; text-align: center; transition: all 0.15s; }
.prcs-settings-toggle-btn:hover { background: #2e2e2e; color: #ccc; border-color: #555; }
.prcs-settings-close-btn { background: transparent; color: #888; border: none; cursor: pointer; padding: 2px; display: flex; align-items: center; justify-content: center; border-radius: 4px; transition: all 0.15s; }
.prcs-settings-close-btn:hover { color: #fff; background: rgba(255,255,255,0.1); }
.prcs-segmented-control { display: flex; background: #1e1e1e; border: 1px solid #333; border-radius: 6px; padding: 2px; width: 110px; height: 22px; align-items: center; box-sizing: border-box; }
.prcs-segment { flex: 1; text-align: center; font-size: 10px; font-weight: 500; line-height: 18px; cursor: pointer; border-radius: 4px; color: #888; transition: all 0.15s ease; }
.prcs-segment.active { background: #333; color: #fff; }
.prcs-segment:hover:not(.active) { color: #ccc; }
.prcs-characters-container { display: flex; justify-content: space-between; gap: 12px; margin-bottom: 8px; box-sizing: border-box; width: 100%; flex-shrink: 0; }
.prcs-character-slot { flex: 1; background: #1e1e1e; border: 1.5px dashed #444; border-radius: 8px; height: 120px; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; padding: 4px; position: relative; cursor: pointer; overflow: hidden; transition: all 0.2s ease; box-sizing: border-box; }
.prcs-character-slot:hover { border-color: #666; background: #252525; }
.prcs-character-slot.drag-over { border-color: #4fff8f; background: rgba(79, 255, 143, 0.05); }
.prcs-character-label { font-size: 10px; font-weight: bold; color: #888; margin-bottom: 2px; pointer-events: none; }
.prcs-character-placeholder { font-size: 9px; color: #666; text-align: center; pointer-events: none; margin-top: 10px; }
.prcs-character-previews-row { display: flex; width: 100%; height: 52px; gap: 4px; position: relative; }
.prcs-character-preview-wrapper { flex: 1; height: 100%; position: relative; overflow: hidden; border-radius: 3px; background: #111; }
.prcs-character-preview { width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.prcs-character-delete { position: absolute; top: 2px; right: 2px; background: rgba(0, 0, 0, 0.85); color: #ff4444; border: none; border-radius: 50%; width: 14px; height: 14px; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 9px; transition: background 0.15s; z-index: 10; padding: 0; }
.prcs-character-delete:hover { background: #ff4444; color: #fff; }
.prcs-character-validate-btn { position: absolute; bottom: -8px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.85); color: #e0e0e0; border: 1px solid #444; border-radius: 3px; padding: 2px 8px; font-size: 9px; font-weight: bold; cursor: pointer; transition: all 0.15s; z-index: 20; }
.prcs-character-validate-btn:hover { background: #4fff8f; color: #000; border-color: #4fff8f; }
.prcs-character-validate-btn.loading { background: #333; color: #888; cursor: wait; pointer-events: none; }
.prcs-character-desc { width: 100%; height: 38px; background: #111; color: #e0e0e0; border: 1px solid #333; border-radius: 4px; font-size: 9px; resize: none; box-sizing: border-box; padding: 2px 4px; margin-top: 10px; outline: none; font-family: inherit; z-index: 10; }
.prcs-character-desc:focus { border-color: #4fff8f; }
.prcs-autocomplete-menu { position: fixed; background: #181818; border: 1px solid #444; border-radius: 6px; padding: 4px; display: flex; flex-direction: column; gap: 2px; z-index: 100000; box-shadow: 0 4px 16px rgba(0,0,0,0.6); min-width: 180px; max-height: 200px; overflow-y: auto; }
.prcs-autocomplete-item { background: #252525; color: #aaa; border: 1px solid #333; border-radius: 4px; padding: 6px 12px; font-size: 11px; font-family: monospace; cursor: pointer; text-align: left; display: flex; align-items: center; justify-content: space-between; transition: all 0.15s ease; }
.prcs-autocomplete-item:hover, .prcs-autocomplete-item.active { background: #1c222d; color: #4fff8f; border-color: #4fff8f; }
.prcs-autocomplete-item span { font-weight: bold; font-size: 12px; }
.prcs-autocomplete-item small { color: #777; font-size: 10px; }
.prcs-autocomplete-item.active small { color: #4fff8f; opacity: 0.8; }
`;
if (!document.getElementById("prompt-relay-styles")) {
if (!document.getElementById("prompt-relay-styles-cs")) {
const styleEl = document.createElement("style");
styleEl.id = "prompt-relay-styles";
styleEl.id = "prompt-relay-styles-cs";
styleEl.textContent = STYLES;
document.head.appendChild(styleEl);
}
@@ -443,7 +443,7 @@ class TimelineEditor {
createDOM() {
this.wrapper = document.createElement("div");
this.wrapper.className = "pr-wrapper";
this.wrapper.className = "prcs-wrapper";
this.wrapper.addEventListener("mouseenter", () => { this._isHovering = true; });
this.wrapper.addEventListener("mouseleave", () => { this._isHovering = false; });
@@ -485,10 +485,10 @@ class TimelineEditor {
// --- Toolbar ---
const toolbar = document.createElement("div");
toolbar.className = "pr-toolbar";
toolbar.className = "prcs-toolbar";
const actionGroup = document.createElement("div");
actionGroup.className = "pr-actions";
actionGroup.className = "prcs-actions";
this.fileInput = document.createElement("input");
this.fileInput.type = "file";
@@ -505,22 +505,22 @@ class TimelineEditor {
this.audioFileInput.addEventListener("change", (e) => this.handleAudioUpload(e.target.files));
const uploadBtn = document.createElement("button");
uploadBtn.className = "pr-btn";
uploadBtn.className = "prcs-btn";
uploadBtn.innerHTML = `${ICONS.upload} Add Image`;
uploadBtn.addEventListener("click", () => this.fileInput.click());
const uploadAudioBtn = document.createElement("button");
uploadAudioBtn.className = "pr-btn";
uploadAudioBtn.className = "prcs-btn";
uploadAudioBtn.innerHTML = `${ICONS.audio} Add Audio`;
uploadAudioBtn.addEventListener("click", () => this.audioFileInput.click());
const addTextBtn = document.createElement("button");
addTextBtn.className = "pr-btn";
addTextBtn.className = "prcs-btn";
addTextBtn.innerHTML = `${ICONS.text} Add Text`;
addTextBtn.addEventListener("click", () => this.addTextSegmentFreeSpace());
const deleteBtn = document.createElement("button");
deleteBtn.className = "pr-btn pr-btn-danger";
deleteBtn.className = "prcs-btn prcs-btn-danger";
deleteBtn.innerHTML = `${ICONS.trash} Delete`;
deleteBtn.addEventListener("click", () => this.deleteSelectedSegment());
@@ -533,18 +533,18 @@ class TimelineEditor {
toolbar.appendChild(actionGroup);
const rightGroup = document.createElement("div");
rightGroup.className = "pr-right-group";
rightGroup.className = "prcs-right-group";
this.segmentBoundsDisplay = document.createElement("div");
this.segmentBoundsDisplay.className = "pr-segment-bounds";
this.segmentBoundsDisplay.className = "prcs-segment-bounds";
this.segmentBoundsDisplay.textContent = "Start: - | End: -";
this.timeCodeDisplay = document.createElement("div");
this.timeCodeDisplay.className = "pr-timecode";
this.timeCodeDisplay.className = "prcs-timecode";
this.timeCodeDisplay.textContent = this.formatTime(0);
const settingsBtn = document.createElement("button");
settingsBtn.className = "pr-btn";
settingsBtn.className = "prcs-btn";
settingsBtn.style.padding = "6px";
settingsBtn.style.justifyContent = "center";
settingsBtn.style.width = "28px";
@@ -562,7 +562,7 @@ class TimelineEditor {
});
const toggleBtn = document.createElement("button");
toggleBtn.className = "pr-btn";
toggleBtn.className = "prcs-btn";
toggleBtn.style.padding = "6px 8px";
toggleBtn.style.fontSize = "11px";
toggleBtn.style.marginRight = "0px";
@@ -601,7 +601,7 @@ class TimelineEditor {
}, 100);
const helpBtn = document.createElement("button");
helpBtn.className = "pr-btn";
helpBtn.className = "prcs-btn";
helpBtn.style.padding = "6px";
helpBtn.style.justifyContent = "center";
helpBtn.style.width = "28px";
@@ -627,7 +627,7 @@ class TimelineEditor {
// --- Canvas & Viewport ---
this.viewport = document.createElement("div");
this.viewport.className = "pr-timeline-viewport";
this.viewport.className = "prcs-timeline-viewport";
this.viewport.addEventListener("wheel", (e) => {
if (e.ctrlKey || e.metaKey) {
@@ -649,7 +649,7 @@ class TimelineEditor {
}, { passive: false, capture: true });
this.canvas = document.createElement("canvas");
this.canvas.className = "pr-canvas";
this.canvas.className = "prcs-canvas";
this.ctx = this.canvas.getContext("2d");
this.canvas.style.width = "100%";
@@ -661,14 +661,14 @@ class TimelineEditor {
// --- Content Area Container ---
const propContainer = document.createElement("div");
propContainer.className = "pr-prop-container";
propContainer.className = "prcs-prop-container";
// Visual character references setup
this.createCharacterSlots(propContainer);
// --- Text Area (Image/Text) ---
this.promptInput = document.createElement("textarea");
this.promptInput.className = "pr-prompt-area";
this.promptInput.className = "prcs-prompt-area";
this.promptInput.placeholder = "Enter prompt for selected segment... Type '@' for character shortcuts.";
this.promptInput.addEventListener("input", () => {
if (this.selectionType === "image" && this.timeline.segments[this.selectedIndex]) {
@@ -679,7 +679,7 @@ class TimelineEditor {
// --- Audio Info Area ---
this.audioInfoArea = document.createElement("div");
this.audioInfoArea.className = "pr-audio-info";
this.audioInfoArea.className = "prcs-audio-info";
propContainer.appendChild(this.promptInput);
propContainer.appendChild(this.audioInfoArea);
@@ -792,15 +792,15 @@ class TimelineEditor {
this.wrapper.appendChild(this.viewport);
this.strengthRow = document.createElement("div");
this.strengthRow.className = "pr-strength-row";
this.strengthRow.className = "prcs-strength-row";
const strengthLabel = document.createElement("span");
strengthLabel.className = "pr-strength-label";
strengthLabel.className = "prcs-strength-label";
strengthLabel.textContent = "Guide Strength:";
this.strengthValue = document.createElement("input");
this.strengthValue.type = "text";
this.strengthValue.className = "pr-strength-input";
this.strengthValue.className = "prcs-strength-input";
this.strengthValue.value = "1.00";
this.strengthValue.disabled = true;
this.strengthValue.style.cursor = "ew-resize";
@@ -874,14 +874,14 @@ class TimelineEditor {
this.strengthRow.appendChild(this.strengthValue);
this.playBtn = document.createElement("button");
this.playBtn.className = "pr-icon-btn";
this.playBtn.className = "prcs-icon-btn";
this.playBtn.style.padding = "4px";
this.playBtn.innerHTML = ICONS.play;
this.playBtn.title = "Play/Pause Audio";
this.playBtn.addEventListener("click", () => this.togglePlay());
this.loopBtn = document.createElement("button");
this.loopBtn.className = "pr-icon-btn";
this.loopBtn.className = "prcs-icon-btn";
this.loopBtn.style.padding = "4px";
this.loopBtn.innerHTML = ICONS.loop;
this.loopBtn.title = "Toggle Loop";
@@ -889,7 +889,7 @@ class TimelineEditor {
this.seekBar = document.createElement("input");
this.seekBar.type = "range";
this.seekBar.className = "pr-seek-bar";
this.seekBar.className = "prcs-seek-bar";
this.seekBar.min = "0";
this.seekBar.value = "0";
this.seekBar.style.flex = "1";
@@ -902,10 +902,10 @@ class TimelineEditor {
});
const zoomControls = document.createElement("div");
zoomControls.className = "pr-zoom-controls";
zoomControls.className = "prcs-zoom-controls";
const zoomOutBtn = document.createElement("button");
zoomOutBtn.className = "pr-icon-btn";
zoomOutBtn.className = "prcs-icon-btn";
zoomOutBtn.style.padding = "4px";
zoomOutBtn.innerHTML = ICONS.minus;
zoomOutBtn.title = "Zoom Out";
@@ -917,7 +917,7 @@ class TimelineEditor {
this.zoomSlider = document.createElement("input");
this.zoomSlider.type = "range";
this.zoomSlider.className = "pr-zoom-slider";
this.zoomSlider.className = "prcs-zoom-slider";
this.zoomSlider.min = "1";
this.zoomSlider.max = "1";
this.zoomSlider.step = "0.1";
@@ -941,7 +941,7 @@ class TimelineEditor {
});
const zoomInBtn = document.createElement("button");
zoomInBtn.className = "pr-icon-btn";
zoomInBtn.className = "prcs-icon-btn";
zoomInBtn.style.padding = "4px";
zoomInBtn.innerHTML = ICONS.plus;
zoomInBtn.title = "Zoom In";
@@ -952,7 +952,7 @@ class TimelineEditor {
});
const zoomFitBtn = document.createElement("button");
zoomFitBtn.className = "pr-icon-btn";
zoomFitBtn.className = "prcs-icon-btn";
zoomFitBtn.style.padding = "4px";
zoomFitBtn.style.marginLeft = "4px";
zoomFitBtn.innerHTML = ICONS.fit;
@@ -974,14 +974,14 @@ class TimelineEditor {
zoomControls.appendChild(zoomFitBtn);
const playerControls = document.createElement("div");
playerControls.className = "pr-player-controls";
playerControls.className = "prcs-player-controls";
playerControls.appendChild(this.playBtn);
playerControls.appendChild(this.loopBtn);
playerControls.appendChild(this.seekBar);
playerControls.appendChild(zoomControls);
const controlsGroup = document.createElement("div");
controlsGroup.className = "pr-controls-group";
controlsGroup.className = "prcs-controls-group";
controlsGroup.appendChild(this.strengthRow);
controlsGroup.appendChild(playerControls);
@@ -996,7 +996,7 @@ class TimelineEditor {
// --- Visual Character Reference Slots ---
createCharacterSlots(parent) {
const container = document.createElement("div");
container.className = "pr-characters-container";
container.className = "prcs-characters-container";
if (!this.timeline.characters) {
this.timeline.characters = [
@@ -1010,7 +1010,7 @@ class TimelineEditor {
for (let i = 0; i < 3; i++) {
const slot = document.createElement("div");
slot.className = "pr-character-slot";
slot.className = "prcs-character-slot";
slot.dataset.index = i;
slot.addEventListener("dragover", (e) => {
@@ -1034,9 +1034,9 @@ class TimelineEditor {
});
slot.addEventListener("click", (e) => {
if (e.target.closest(".pr-character-delete") ||
e.target.closest(".pr-character-validate-btn") ||
e.target.closest(".pr-character-desc")) return;
if (e.target.closest(".prcs-character-delete") ||
e.target.closest(".prcs-character-validate-btn") ||
e.target.closest(".prcs-character-desc")) return;
const fi = document.createElement("input");
fi.type = "file";
@@ -1120,19 +1120,19 @@ class TimelineEditor {
if (data.images && data.images.length > 0) {
const previewsRow = document.createElement("div");
previewsRow.className = "pr-character-previews-row";
previewsRow.className = "prcs-character-previews-row";
data.images.forEach((imgData, imgIdx) => {
const imgWrapper = document.createElement("div");
imgWrapper.className = "pr-character-preview-wrapper";
imgWrapper.className = "prcs-character-preview-wrapper";
const img = document.createElement("img");
img.className = "pr-character-preview";
img.className = "prcs-character-preview";
img.src = imgData.b64;
imgWrapper.appendChild(img);
const delBtn = document.createElement("button");
delBtn.className = "pr-character-delete";
delBtn.className = "prcs-character-delete";
delBtn.innerHTML = ICONS.close;
delBtn.title = "Delete Image";
delBtn.addEventListener("click", (e) => {
@@ -1148,7 +1148,7 @@ class TimelineEditor {
});
const valBtn = document.createElement("button");
valBtn.className = "pr-character-validate-btn";
valBtn.className = "prcs-character-validate-btn";
valBtn.textContent = data.description ? "Re-Analyze" : "Analyze";
valBtn.title = "Run Gemma 4 Multimodal Analysis";
valBtn.style.left = "50%";
@@ -1163,7 +1163,7 @@ class TimelineEditor {
slot.appendChild(previewsRow);
const descInput = document.createElement("textarea");
descInput.className = "pr-character-desc";
descInput.className = "prcs-character-desc";
descInput.value = data.description || "";
descInput.placeholder = "manual description...";
descInput.addEventListener("input", () => {
@@ -1176,11 +1176,11 @@ class TimelineEditor {
slot.appendChild(descInput);
} else {
const label = document.createElement("div");
label.className = "pr-character-label";
label.className = "prcs-character-label";
label.textContent = `@char${i+1}`;
const placeholder = document.createElement("div");
placeholder.className = "pr-character-placeholder";
placeholder.className = "prcs-character-placeholder";
placeholder.innerHTML = `${ICONS.upload}<br>Drop Sheet`;
slot.appendChild(label);
@@ -1257,7 +1257,7 @@ class TimelineEditor {
if (!input) return;
const menu = document.createElement("div");
menu.className = "pr-autocomplete-menu";
menu.className = "prcs-autocomplete-menu";
menu.style.display = "none";
document.body.appendChild(menu);
this._autocompleteMenu = menu;
@@ -1306,7 +1306,7 @@ class TimelineEditor {
filtered.forEach((s, idx) => {
const item = document.createElement("div");
item.className = "pr-autocomplete-item" + (idx === activeIndex ? " active" : "");
item.className = "prcs-autocomplete-item" + (idx === activeIndex ? " active" : "");
item.innerHTML = `<span>${s.tag}</span><small>${s.label}</small>`;
item.addEventListener("mousedown", (e) => {
@@ -1339,7 +1339,7 @@ class TimelineEditor {
input.addEventListener("keydown", (e) => {
if (showMenu) {
const items = menu.querySelectorAll(".pr-autocomplete-item");
const items = menu.querySelectorAll(".prcs-autocomplete-item");
if (items.length === 0) return;
if (e.key === "ArrowDown") {
@@ -2931,7 +2931,7 @@ onMouseUp(e) {
showContextMenu(clientX, clientY, seg, trackType) {
this.dismissContextMenu();
const menu = document.createElement("div");
menu.className = "pr-gap-menu";
menu.className = "prcs-gap-menu";
menu.style.left = `${clientX + 6}px`;
menu.style.top = `${clientY - 10}px`;
@@ -2939,7 +2939,7 @@ onMouseUp(e) {
if (isImage) {
const copyBtn = document.createElement("button");
copyBtn.className = "pr-gap-menu-btn";
copyBtn.className = "prcs-gap-menu-btn";
copyBtn.innerHTML = `Copy Image`;
copyBtn.onclick = async () => {
try {
@@ -2954,7 +2954,7 @@ onMouseUp(e) {
menu.appendChild(copyBtn);
const saveBtn = document.createElement("button");
saveBtn.className = "pr-gap-menu-btn";
saveBtn.className = "prcs-gap-menu-btn";
saveBtn.innerHTML = `Save Image`;
saveBtn.onclick = () => {
const a = document.createElement("a");
@@ -2966,7 +2966,7 @@ onMouseUp(e) {
menu.appendChild(saveBtn);
const openBtn = document.createElement("button");
openBtn.className = "pr-gap-menu-btn";
openBtn.className = "prcs-gap-menu-btn";
openBtn.innerHTML = `Open Image in New Tab`;
openBtn.onclick = () => {
const win = window.open();
@@ -2981,7 +2981,7 @@ onMouseUp(e) {
if (trackType !== "audio") {
const copyPromptBtn = document.createElement("button");
copyPromptBtn.className = "pr-gap-menu-btn";
copyPromptBtn.className = "prcs-gap-menu-btn";
copyPromptBtn.innerHTML = `Copy Prompt`;
copyPromptBtn.onclick = async () => {
try {
@@ -2995,7 +2995,7 @@ onMouseUp(e) {
}
const copySegBtn = document.createElement("button");
copySegBtn.className = "pr-gap-menu-btn";
copySegBtn.className = "prcs-gap-menu-btn";
copySegBtn.innerHTML = `Copy Segment`;
copySegBtn.onclick = () => {
this._copiedSegment = { ...seg, id: Date.now().toString() + Math.random().toString(36).substr(2, 5) };
@@ -3007,7 +3007,7 @@ onMouseUp(e) {
const currentTrack = trackType === "audio" ? "audio" : "image";
if (this._copiedSegment && this._copiedSegmentTrack === currentTrack) {
const pasteReplaceBtn = document.createElement("button");
pasteReplaceBtn.className = "pr-gap-menu-btn";
pasteReplaceBtn.className = "prcs-gap-menu-btn";
pasteReplaceBtn.innerHTML = `Paste & Replace`;
pasteReplaceBtn.onclick = () => {
const newSeg = {
@@ -3026,7 +3026,7 @@ onMouseUp(e) {
}
const delBtn = document.createElement("button");
delBtn.className = "pr-gap-menu-btn";
delBtn.className = "prcs-gap-menu-btn";
delBtn.innerHTML = `Delete`;
delBtn.style.color = "#ff4444";
delBtn.onclick = () => {
@@ -3050,7 +3050,7 @@ onMouseUp(e) {
showGapContextMenu(clientX, clientY, gap) {
this.dismissContextMenu();
const menu = document.createElement("div");
menu.className = "pr-gap-menu";
menu.className = "prcs-gap-menu";
menu.style.left = `${clientX + 6}px`;
menu.style.top = `${clientY - 10}px`;
@@ -3058,7 +3058,7 @@ onMouseUp(e) {
if (this._copiedSegment && this._copiedSegmentTrack === currentTrack) {
const pasteBtn = document.createElement("button");
pasteBtn.className = "pr-gap-menu-btn";
pasteBtn.className = "prcs-gap-menu-btn";
pasteBtn.innerHTML = `Paste Segment`;
pasteBtn.onclick = () => {
const startFrame = Math.round(gap.clickedFrame !== undefined ? gap.clickedFrame : gap.frameStart);
@@ -3081,7 +3081,7 @@ onMouseUp(e) {
if (currentTrack === "image") {
const textBtn = document.createElement("button");
textBtn.className = "pr-gap-menu-btn";
textBtn.className = "prcs-gap-menu-btn";
textBtn.innerHTML = `${ICONS.text} Text Segment`;
textBtn.onclick = () => {
this.addSegmentInGap(gap.frameStart, gap.frameEnd, "text");
@@ -3090,7 +3090,7 @@ onMouseUp(e) {
menu.appendChild(textBtn);
const imgBtn = document.createElement("button");
imgBtn.className = "pr-gap-menu-btn";
imgBtn.className = "prcs-gap-menu-btn";
imgBtn.innerHTML = `${ICONS.upload} Image Segment`;
imgBtn.onclick = () => {
this.dismissContextMenu();
@@ -3124,12 +3124,12 @@ onMouseUp(e) {
showGapMenu(clientX, clientY, gap) {
this.dismissGapMenu();
const menu = document.createElement("div");
menu.className = "pr-gap-menu";
menu.className = "prcs-gap-menu";
menu.style.left = `${clientX + 6}px`;
menu.style.top = `${clientY - 10}px`;
const textBtn = document.createElement("button");
textBtn.className = "pr-gap-menu-btn";
textBtn.className = "prcs-gap-menu-btn";
textBtn.innerHTML = `${ICONS.text} Text Segment`;
textBtn.addEventListener("click", () => {
this.addSegmentInGap(gap.frameStart, gap.frameEnd, "text");
@@ -3137,7 +3137,7 @@ onMouseUp(e) {
});
const imgBtn = document.createElement("button");
imgBtn.className = "pr-gap-menu-btn";
imgBtn.className = "prcs-gap-menu-btn";
imgBtn.innerHTML = `${ICONS.upload} Image Segment`;
imgBtn.addEventListener("click", () => {
this.dismissGapMenu();
@@ -3157,7 +3157,7 @@ onMouseUp(e) {
const currentTrack = gap.track === "audio" ? "audio" : "image";
if (this._copiedSegment && this._copiedSegmentTrack === currentTrack) {
const pasteBtn = document.createElement("button");
pasteBtn.className = "pr-gap-menu-btn";
pasteBtn.className = "prcs-gap-menu-btn";
pasteBtn.innerHTML = `Paste Segment`;
pasteBtn.onclick = () => {
const gapLength = gap.frameEnd - gap.frameStart;
@@ -3260,9 +3260,9 @@ onMouseUp(e) {
_makeSettingRow(label, inputEl) {
const row = document.createElement("div");
row.className = "pr-settings-row";
row.className = "prcs-settings-row";
const lbl = document.createElement("span");
lbl.className = "pr-settings-label";
lbl.className = "prcs-settings-label";
lbl.textContent = label;
row.appendChild(lbl);
row.appendChild(inputEl);
@@ -3272,10 +3272,10 @@ onMouseUp(e) {
showSettingsMenu(anchorEl) {
this.dismissSettingsMenu();
const menu = document.createElement("div");
menu.className = "pr-settings-menu";
menu.className = "prcs-settings-menu";
const titleContainer = document.createElement("div");
titleContainer.className = "pr-settings-title";
titleContainer.className = "prcs-settings-title";
titleContainer.style.display = "flex";
titleContainer.style.justifyContent = "space-between";
titleContainer.style.alignItems = "center";
@@ -3285,7 +3285,7 @@ onMouseUp(e) {
titleContainer.appendChild(titleText);
const closeBtn = document.createElement("button");
closeBtn.className = "pr-settings-close-btn";
closeBtn.className = "prcs-settings-close-btn";
closeBtn.innerHTML = ICONS.close;
closeBtn.title = "Close Settings";
closeBtn.addEventListener("click", () => this.dismissSettingsMenu());
@@ -3304,14 +3304,14 @@ onMouseUp(e) {
const dmWidget = this.node.widgets?.find(w => w.name === "display_mode");
if (dmWidget) {
const ctrl = document.createElement("div");
ctrl.className = "pr-segmented-control";
ctrl.className = "prcs-segmented-control";
const framesSeg = document.createElement("div");
framesSeg.className = "pr-segment";
framesSeg.className = "prcs-segment";
framesSeg.textContent = "Frames";
const secondsSeg = document.createElement("div");
secondsSeg.className = "pr-segment";
secondsSeg.className = "prcs-segment";
secondsSeg.textContent = "Seconds";
const updateActive = (val) => {
@@ -3344,27 +3344,27 @@ onMouseUp(e) {
}
const divider1 = document.createElement("hr");
divider1.className = "pr-settings-divider";
divider1.className = "prcs-settings-divider";
menu.appendChild(divider1);
const createScrubbableNumberControl = (w, step, min, max, isFloat = false) => {
const container = document.createElement("div");
container.className = "pr-number-control";
container.className = "prcs-number-control";
const decBtn = document.createElement("button");
decBtn.className = "pr-number-btn";
decBtn.className = "prcs-number-btn";
decBtn.textContent = "-";
const inp = document.createElement("input");
inp.type = "number";
inp.className = "pr-settings-input";
inp.className = "prcs-settings-input";
inp.value = w.value;
inp.step = step.toString();
inp.min = min.toString();
inp.max = max.toString();
const incBtn = document.createElement("button");
incBtn.className = "pr-number-btn";
incBtn.className = "prcs-number-btn";
incBtn.textContent = "+";
decBtn.addEventListener("click", () => {
@@ -3489,7 +3489,7 @@ onMouseUp(e) {
}
const toggleBtn = document.createElement("button");
toggleBtn.className = "pr-settings-toggle-btn";
toggleBtn.className = "prcs-settings-toggle-btn";
const widgetsVisible = !!(this.node.widgets?.find(w => w.name === "display_mode" && !(w.options && w.options.hidden)));
toggleBtn.textContent = widgetsVisible ? "Hide Widgets on Node" : "Show Widgets on Node";
toggleBtn.addEventListener("click", () => {
@@ -3735,9 +3735,9 @@ onMouseUp(e) {
// --- Node Registration Hooks ---
app.registerExtension({
name: "LTXDirector",
name: "LTXDirectorCS",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "LTXDirector") {
if (nodeData.name === "LTXDirectorCS") {
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
+12 -17
View File
@@ -1,17 +1,12 @@
import { app } from "../../scripts/app.js";
// LTX Director Guide is a pure pass-through processor node.
// All configuration (images, insert frames, strengths) comes from
// the guide_data output of Prompt Relay Encode (Timeline).
// No dynamic widgets or sync logic needed.
app.registerExtension({
name: "Comfy.LTXDirectorGuide",
async nodeCreated(node) {
if (node.comfyClass !== "LTXDirectorGuide") return;
// Nothing to initialize — the node has no configurable widgets.
},
});
// Nothing to initialize — the node has no configurable widgets.
},
});
import { app } from "../../scripts/app.js";
// LTX Director Guide CS is a pure pass-through processor node.
// All configuration (images, insert frames, strengths) comes from
// the guide_data output of the LTX Director CS (Timeline) node.
app.registerExtension({
name: "Comfy.LTXDirectorGuideCS",
async nodeCreated(node) {
if (node.comfyClass !== "LTXDirectorGuideCS") return;
// Nothing to initialize — the node has no configurable widgets.
},
});
+3 -3
View File
@@ -16,7 +16,7 @@ class CleanLatentSlice:
RETURN_TYPES = ("LATENT",)
RETURN_NAMES = ("latent",)
FUNCTION = "slice_latent"
CATEGORY = "WhatDreamsCost"
CATEGORY = "WhatDreamsCost CS"
DESCRIPTION = "Safely slices a video latent starting from an offset index for a specific length. Uses torch.narrow to bypass PyTorch NestedTensor slicing bugs."
def slice_latent(self, latent, start, length):
@@ -65,11 +65,11 @@ class CleanLatentSlice:
# Register the node with ComfyUI
NODE_CLASS_MAPPINGS = {
"CleanLatentSlice": CleanLatentSlice
"CleanLatentSliceCS": CleanLatentSlice
}
NODE_DISPLAY_NAME_MAPPINGS = {
"CleanLatentSlice": "Clean Latent Slice"
"CleanLatentSliceCS": "Clean Latent Slice CS"
}
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
+1 -1
View File
@@ -71,7 +71,7 @@ class LoadAudioUI:
}
}
CATEGORY = "WhatDreamsCost"
CATEGORY = "WhatDreamsCost CS"
RETURN_TYPES = ("AUDIO", "FLOAT")
RETURN_NAMES = ("audio", "duration")
FUNCTION = "load_audio"
+3 -3
View File
@@ -697,9 +697,9 @@ class LTXDirector(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="LTXDirector",
display_name="LTX Director",
category="WhatDreamsCost",
node_id="LTXDirectorCS",
display_name="LTX Director CS",
category="WhatDreamsCost CS",
description=(
"Same as Prompt Relay Encode, but local prompts and segment lengths are edited "
"visually as draggable blocks on a timeline. The duration_frames input only sets the "
+3 -3
View File
@@ -9,9 +9,9 @@ class LTXDirectorGuide(LTXVAddGuide):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="LTXDirectorGuide",
display_name="LTX Director Guide",
category="WhatDreamsCost",
node_id="LTXDirectorGuideCS",
display_name="LTX Director Guide CS",
category="WhatDreamsCost CS",
description=(
"Applies guide images from a Prompt Relay Timeline node at the frame positions "
"and strengths defined on the timeline. Connect guide_data from the timeline node."
+14 -36
View File
@@ -1,36 +1,14 @@
[project]
name = "WhatDreamsCost-ComfyUI"
description = "A variety of custom ComfyUI nodes and workflows for creatives."
version = "1.3.9"
license = {file = "LICENSE"}
# classifiers = [
# # For OS-independent nodes (works on all operating systems)
# "Operating System :: OS Independent",
#
# # OR for OS-specific nodes, specify the supported systems:
# "Operating System :: Microsoft :: Windows", # Windows specific
# "Operating System :: POSIX :: Linux", # Linux specific
# "Operating System :: MacOS", # macOS specific
#
# # GPU Accelerator support. Pick the ones that are supported by your extension.
# "Environment :: GPU :: NVIDIA CUDA", # NVIDIA CUDA support
# "Environment :: GPU :: AMD ROCm", # AMD ROCm support
# "Environment :: GPU :: Intel Arc", # Intel Arc support
# "Environment :: NPU :: Huawei Ascend", # Huawei Ascend support
# "Environment :: GPU :: Apple Metal", # Apple Metal support
# ]
[project.urls]
Repository = "https://github.com/WhatDreamsCost/WhatDreamsCost-ComfyUI"
# Used by Comfy Registry https://registry.comfy.org
Documentation = "https://github.com/WhatDreamsCost/WhatDreamsCost-ComfyUI/wiki"
"Bug Tracker" = "https://github.com/WhatDreamsCost/WhatDreamsCost-ComfyUI/issues"
[tool.comfy]
PublisherId = "whatdreamscost"
DisplayName = "WhatDreamsCost-ComfyUI"
Icon = "https://raw.githubusercontent.com/WhatDreamsCost/MediaSyncer/refs/heads/main/Icon.png"
includes = []
# "requires-comfyui" = ">=1.0.0" # ComfyUI version compatibility
[project]
name = "whatdreamscost-csglide"
description = "LTX Director (CS) — modded LTX Director node with reference-sheet / @char / MSR support. Coexists with the original WhatDreamsCost-ComfyUI without collisions."
version = "1.0.0"
license = {file = "LICENSE"}
[project.urls]
Repository = "https://github.com/CGlide/WhatDreamsCost-CSGlide"
[tool.comfy]
PublisherId = "REPLACE_WITH_YOUR_REGISTRY_PUBLISHER_ID"
DisplayName = "WhatDreamsCost-CSGlide"
Icon = ""
includes = []