From 6a2c0ad51f39ba1e60cab02b2980be46420c455a Mon Sep 17 00:00:00 2001 From: CGlide Date: Tue, 23 Jun 2026 03:12:54 +0200 Subject: [PATCH] Delete js/speech_length_calculator.js --- js/speech_length_calculator.js | 336 --------------------------------- 1 file changed, 336 deletions(-) delete mode 100644 js/speech_length_calculator.js diff --git a/js/speech_length_calculator.js b/js/speech_length_calculator.js deleted file mode 100644 index dfcf565..0000000 --- a/js/speech_length_calculator.js +++ /dev/null @@ -1,336 +0,0 @@ -import { app } from "../../scripts/app.js"; -import { ComfyWidgets } from "../../scripts/widgets.js"; - -// 1. DEFINE CSS STYLES GLOBALLY ONCE -// We use DOM elements instead of Canvas drawing so it works flawlessly in ComfyUI V3 -const cssStyles = ` - .slc-ui-container { - width: 100%; - height: 100%; - min-height: 260px; /* Increased from 220px to prevent V1 cropping */ - background-color: rgba(15, 15, 19, 0.7); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 8px; - padding: 12px; - box-sizing: border-box; - display: flex; - flex-direction: column; - font-family: sans-serif; - color: #ffffff; - overflow: hidden; - pointer-events: auto; /* allows text selection */ - } - .slc-empty { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - } - .slc-empty-title { color: #aaaaaa; font-size: 14px; font-weight: 500; margin-bottom: 4px; } - .slc-empty-sub { color: #777777; font-size: 11px; } - .slc-headers { - display: flex; - gap: 8px; - margin-bottom: 12px; - } - .slc-header-block { - flex: 1; - background-color: rgba(0, 0, 0, 0.4); - border-radius: 6px; - padding: 8px 0; - display: flex; - flex-direction: column; - align-items: center; - } - .slc-header-title { color: #888888; font-size: 9px; font-weight: bold; margin-bottom: 4px; } - .slc-header-value { color: #ffffff; font-size: 16px; font-weight: bold; } - .slc-cards { - display: flex; - flex-direction: column; - gap: 8px; - } - .slc-card { - display: flex; - justify-content: space-between; - align-items: center; - background-color: rgba(0, 0, 0, 0.25); - border: 1px solid rgba(255, 255, 255, 0.05); - border-radius: 4px; - padding: 8px 12px 8px 16px; - position: relative; - overflow: hidden; - } - .slc-card::before { - content: ''; - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 4px; - } - .slc-card.slow::before { background-color: #93c5fd; } - .slc-card.avg::before { background-color: #86efac; } - .slc-card.fast::before { background-color: #fca5a5; } - - .slc-card-left { display: flex; flex-direction: column; } - .slc-card-speed { font-size: 12px; font-weight: bold; margin-bottom: 2px; } - .slc-card.slow .slc-card-speed { color: #93c5fd; } - .slc-card.avg .slc-card-speed { color: #86efac; } - .slc-card.fast .slc-card-speed { color: #fca5a5; } - .slc-card-wpm { font-size: 10px; color: #aaaaaa; } - - .slc-card-right { display: flex; flex-direction: column; align-items: flex-end; } - .slc-card-time { font-size: 13px; font-weight: bold; color: #ffffff; margin-bottom: 2px; } - .slc-card-frames { font-size: 11px; font-family: monospace; color: #888888; } - - .slc-legend { - margin-top: auto; - padding-top: 8px; - color: #999999; - font-size: 10px; - font-weight: 500; - } -`; - -// Inject styles to document head safely -if (!document.getElementById("speech-length-calculator-styles")) { - const styleEl = document.createElement("style"); - styleEl.id = "speech-length-calculator-styles"; - styleEl.textContent = cssStyles; - document.head.appendChild(styleEl); -} - -// 2. HELPER TO GENERATE THE HTML CONTENT -function buildUIHTML(statsData) { - if (!statsData || statsData.empty) { - return ` -
-
Awaiting Script
-
Wrap spoken text inside "quotes"
-
- `; - } - - return ` -
-
-
SPOKEN WORDS
-
${statsData.wordCount}
-
-
-
ADDED TIME
-
${statsData.additionalTime}s
-
-
-
-
-
-
SLOW
-
100 WPM
-
-
-
${statsData.slow.time}
-
${statsData.slow.frames} frames
-
-
-
-
-
AVG
-
130 WPM
-
-
-
${statsData.avg.time}
-
${statsData.avg.frames} frames
-
-
-
-
-
FAST
-
160 WPM
-
-
-
${statsData.fast.time}
-
${statsData.fast.frames} frames
-
-
-
-
WPM = Words Per Minute
- `; -} - -app.registerExtension({ - name: "Comfy.SpeechLengthCalculator", - async beforeRegisterNodeDef(nodeType, nodeData, app) { - if (nodeData.name === "SpeechLengthCalculator") { - - const onNodeCreated = nodeType.prototype.onNodeCreated; - nodeType.prototype.onNodeCreated = function () { - const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined; - - // 3. CREATE THE DOM WIDGET - const uiContainer = document.createElement("div"); - uiContainer.className = "slc-ui-container"; - uiContainer.innerHTML = buildUIHTML({ empty: true }); - - // Attach to node: standard addDOMWidget works natively in both V1 and V3 Frontends - let statsWidget; - if (typeof this.addDOMWidget === "function") { - statsWidget = this.addDOMWidget("Stats", "HTML", uiContainer, { - serialize: false, - hideOnZoom: false - }); - } else { - // Fallback for very old unpatched V1 installations - statsWidget = ComfyWidgets["STRING"](this, "Stats", ["STRING", { multiline: true }], app).widget; - if (statsWidget.inputEl) { - statsWidget.inputEl.style.display = "none"; - if (statsWidget.inputEl.parentNode) { - statsWidget.inputEl.parentNode.appendChild(uiContainer); - } - } - } - - // Tell LiteGraph exactly how much vertical space to hold for our UI - statsWidget.computeSize = function() { - return [0, 280]; // Increased from 240 to prevent bottom cropping in V1 - }; - - // Ensure the node is wide enough on initial creation - requestAnimationFrame(() => { - const minWidth = 340; - if (this.size[0] < minWidth) this.size[0] = minWidth; - - // Fine tune initial height to account for the larger computeSize and make text box taller - if (this.size[1] < 500) this.size[1] = 500; - - this.setDirtyCanvas(true, true); - }); - - // Fetch text from input link OR widget - this._getCurrentText = () => { - const inputSlot = this.inputs && this.inputs.find(i => i.name === "text_input" || (i.name === "text" && i.link)); - if (inputSlot && inputSlot.link) { - const link = app.graph.links[inputSlot.link]; - if (link) { - const sourceNode = app.graph.getNodeById(link.origin_id); - if (sourceNode && sourceNode.widgets) { - const w = sourceNode.widgets.find(w => w.name === "value" || w.name === "text" || w.name === "Text" || w.type === "customtext" || w.type === "STRING"); - if (w && typeof w.value === "string") return w.value; - } - } - } - const textWidget = this.widgets && this.widgets.find(w => w.name === "text"); - return textWidget ? (textWidget.value || "") : ""; - }; - - this._lastState = { text: null, fps: null, addTime: null }; - - const updateStats = () => { - const fpsWidget = this.widgets && this.widgets.find(w => w.name === "fps"); - const additionalTimeWidget = this.widgets && this.widgets.find(w => w.name === "additional_time"); - - if (!fpsWidget) return; - - const text = this._getCurrentText(); - const fps = fpsWidget.value || 24; - const additionalTime = additionalTimeWidget ? parseFloat(additionalTimeWidget.value) || 0 : 0; - - // Skip expensive calculations if nothing changed - if (this._lastState.text === text && - this._lastState.fps === fps && - this._lastState.addTime === additionalTime) { - return; - } - - this._lastState.text = text; - this._lastState.fps = fps; - this._lastState.addTime = additionalTime; - - const regex = /"([^"]*)"|'([^']*)'|“([^”]*)”|‘([^’]*)’/g; - let match; - let quotedText = ""; - while ((match = regex.exec(text)) !== null) { - quotedText += (match[1] || match[2] || match[3] || match[4] || "") + " "; - } - - const words = quotedText.trim().split(/\s+/).filter(w => w.length > 0); - const wordCount = words.length; - - const formatTime = (wpm) => { - const baseMins = wordCount / wpm; - const totalSecs = (baseMins * 60) + additionalTime; - - const mins = Math.floor(totalSecs / 60); - let secs = totalSecs % 60; - secs = Math.ceil(secs * 10) / 10; - const frames = Math.ceil(totalSecs * fps); - - const secsStr = secs.toFixed(1); - const timeStr = mins > 0 ? `${mins}m ${secsStr}s` : `${secsStr}s`; - - return { - time: timeStr, - frames: frames.toString() - }; - }; - - const statsData = { - empty: (wordCount === 0 && additionalTime === 0), - wordCount: wordCount, - additionalTime: additionalTime, - slow: formatTime(100), - avg: formatTime(130), - fast: formatTime(160) - }; - - // 4. INJECT HTML TO THE DOM - // Replaces the heavy canvas redrawing! - uiContainer.innerHTML = buildUIHTML(statsData); - - this.setDirtyCanvas(true, false); - }; - - // We still use onDrawBackground as an efficient silent trigger - // to auto-update in case upstream nodes silently change their text - const onDrawBackground = this.onDrawBackground; - this.onDrawBackground = function(ctx) { - if (onDrawBackground) onDrawBackground.apply(this, arguments); - updateStats(); - }; - - // Bind events to update in real time based on node interactions - setTimeout(() => { - const textWidget = this.widgets && this.widgets.find(w => w.name === "text"); - const fpsWidget = this.widgets && this.widgets.find(w => w.name === "fps"); - const additionalTimeWidget = this.widgets && this.widgets.find(w => w.name === "additional_time"); - - if (textWidget) { - const origCallback = textWidget.callback; - textWidget.callback = function() { - if (origCallback) origCallback.apply(this, arguments); - updateStats(); - } - } - if (fpsWidget) { - const origFpsCallback = fpsWidget.callback; - fpsWidget.callback = function() { - if (origFpsCallback) origFpsCallback.apply(this, arguments); - updateStats(); - } - } - if (additionalTimeWidget) { - const origAddCallback = additionalTimeWidget.callback; - additionalTimeWidget.callback = function() { - if (origAddCallback) origAddCallback.apply(this, arguments); - updateStats(); - } - } - updateStats(); - }, 100); - - return r; - }; - } - } -}); \ No newline at end of file