吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3328|回复: 24
收起左侧

[Windows] 高级定制提词器_v13_ai_Turbo

[复制链接]
just8 发表于 2026-6-18 06:40
ScreenShot_2026-06-17_155244_785.png


高级定制提词器_v13_ai_tur....txt (83 Bytes, 下载次数: 233)

高级定制提词器_v13_ai_turbo

解决直播忘词的尴尬,需要麦克风权限。

免费评分

参与人数 2吾爱币 +1 热心值 +2 收起 理由
6636 + 1 鼓励转贴优秀软件安全工具和文档!
Snapdeal + 1 + 1 必须给点个赞,如果有扩展模式就更完美了。

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

nursoft 发表于 2026-6-20 03:48
提示词源码:
[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>专业高级提词器 (Turbo 零延迟算法版)</title>
    <style>
        :root {
            --bg-color: #111111;        
            --text-color: #555555;      
            --highlight-color: #00ff66; 
            --panel-bg: rgba(30, 30, 30, 0.85);
            --cue-top: 35%; 
        }

        * { box-sizing: border-box; margin: 0; padding: 0; }

        body, html {
            width: 100%; height: 100%; overflow: hidden;
            background-color: var(--bg-color);
            font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
        }

        #container { display: flex; width: 100%; height: 100%; position: relative; }

        /* 左侧设置面板 */
        #control-panel {
            width: 320px; height: 100%; background: var(--panel-bg);
            backdrop-filter: blur(10px); border-right: 1px solid #333;
            padding: 20px; color: #fff; display: flex; flex-direction: column;
            gap: 14px; z-index: 10; transition: transform 0.3s ease; overflow-y: auto;
        }

        #control-panel.hidden { transform: translateX(-320px); position: absolute; }

        /* 右侧提词显示区域 */
        #display-area { flex: 1; height: 100%; position: relative; overflow: hidden; display: flex; flex-direction: column; }

        /* 视线对齐辅助线区域 */
        #cue-zone {
            position: absolute; top: var(--cue-top); left: 0; width: 100%; height: 80px; margin-top: -40px; 
            border-top: 1px dashed rgba(255,255,255,0.15); border-bottom: 1px dashed rgba(255,255,255,0.15);
            background: rgba(255, 255, 255, 0.01); z-index: 5; pointer-events: none; display: flex; align-items: center;
        }
        
        #cue-line { width: 100%; height: 2px; background: linear-gradient(90deg, transparent, var(--highlight-color), transparent); position: relative; }
        #cue-line::before, #cue-line::after { content: '&#9658;'; position: absolute; color: var(--highlight-color); font-size: 20px; top: -12px; }
        #cue-line::before { left: 10px; }
        #cue-line::after { right: 10px; transform: rotate(180deg); }

        /* 滚动容器 */
        #scroll-wrapper {
            flex: 1; overflow-y: scroll; padding: 35vh 8% 65vh 8%; 
            /* 缩短过渡时间至 80ms,实现近乎瞬移的干脆感,消除粘滞感 */
            scroll-behavior: smooth;
        }
        #scroll-wrapper.instant-mode { scroll-behavior: auto; }
        #scroll-wrapper::-webkit-scrollbar { display: none; }
        #scroll-wrapper { -ms-overflow-style: none; scrollbar-width: none; }

        #text-content { display: flex; flex-direction: column; gap: 20px; }

        /* 单行文本样式 */
        .script-line {
            font-size: 55px; line-height: 1.4; color: var(--text-color); font-weight: bold;
            white-space: pre-wrap; word-break: break-all; transition: color 0.1s ease, transform 0.1s ease, opacity 0.1s ease;
            opacity: 0.2; transform: scale(0.95); transform-origin: left center;
        }

        /* 文字过线高亮样式 */
        .script-line.active { color: var(--highlight-color) !important; opacity: 1; transform: scale(1.02); }

        /* 镜像翻转 */
        .mirror-x { transform: scaleX(-1); }
        .mirror-y { transform: scaleY(-1); }
        .mirror-both { transform: scale(-1, -1); }
        .mirror-x .script-line, .mirror-both .script-line { transform-origin: right center; }

        /* UI 布局 */
        .form-group { display: flex; flex-direction: column; gap: 6px; }
        label { font-size: 12px; color: #aaa; }
        textarea { width: 100%; height: 90px; background: #222; border: 1px solid #444; color: #fff; padding: 10px; border-radius: 4px; resize: none; font-size: 13px; }
        .btn-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
        button { background: #333; color: #fff; border: 1px solid #555; padding: 8px; border-radius: 4px; cursor: pointer; font-weight: bold; transition: all 0.2s; font-size: 13px; }
        button:hover { background: #444; border-color: var(--highlight-color); }
        button.primary { background: var(--highlight-color); color: #000; border: none; }
        input[type="range"] { width: 100%; accent-color: var(--highlight-color); }
        select { background: #222; color: #fff; padding: 7px; border-radius: 4px; border: 1px solid #444; font-size: 13px; }
        .value-display { float: right; color: var(--highlight-color); }

        /* AI跟随高级面板 */
        .ai-panel { background: rgba(0, 255, 102, 0.06); border: 1px solid rgba(0, 255, 102, 0.2); padding: 12px; border-radius: 6px; }
        .ai-toggle-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
        .ai-status-dot { font-size: 12px; display: flex; align-items: center; gap: 4px; color: #aaa; }
        .ai-status-dot::before { content: ''; display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #666; }
        .ai-status-dot.active::before { background: #00ff66; box-shadow: 0 0 8px #00ff66; }
        .ai-speech-preview { font-size: 11px; color: #888; background: #222; padding: 6px; border-radius: 4px; min-height: 28px; word-break: break-all; margin-top: 4px; max-height: 50px; overflow-y: auto; }

        .color-picker-group { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
        .color-dot { width: 24px; height: 24px; border-radius: 50%; cursor: pointer; border: 2px solid transparent; }
        .color-dot.selected { border-color: #fff; transform: scale(1.1); }
        .more-color-wrapper { position: relative; width: 24px; height: 24px; border-radius: 50%; background: linear-gradient(45deg, red, purple); overflow: hidden; cursor: pointer;}
        .more-color-wrapper input[type="color"] { position: absolute; top: -5px; left: -5px; width: 34px; height: 34px; opacity: 0; cursor: pointer; }

        #toggle-panel-btn { position: absolute; left: 10px; top: 10px; z-index: 11; background: rgba(0,0,0,0.6); border: 1px solid #444; padding: 5px 10px; font-size: 12px; color: #fff; cursor: pointer; }
        #status-bar { position: absolute; bottom: 10px; right: 20px; color: #666; font-size: 13px; z-index: 5; pointer-events: none; }
    </style>
</head>
<body>

    <div id="container">
        <div id="control-panel">
            <h4>高级定制提词器Turbo零延迟算法版<br />AI检测每句前几个字</h4>
            <hr style="border-color: #333;">
            
            <div class="form-group">
                <label>提词文本内容</label>
                <textarea id="text-input" placeholder="请输入或粘贴演讲稿...">欢迎体验 Turbo 零延迟算法版!

当前版本专门解决网络传输造成的滞后。

我们启用了激进的前端单字预判机制。

只要你读出某行开头的第一个字。

系统就会强行中断一切无用匹配。

直接把这一行推向屏幕中央。

不需要等整句话吐完。

快开口试一下它的敏捷度吧!</textarea>
            </div>

            <div class="form-group ai-panel">
                <div class="ai-toggle-row">
                    <label style="color: #00ff66; font-weight: bold;">Turbo 语音跟随</label>
                    <button id="ai-mode-btn" style="padding: 4px 10px; background: #333; font-size: 12px; border-color: #444;">已关闭</button>
                </div>
                <div style="display: flex; justify-content: space-between;">
                    <span id="ai-dot" class="ai-status-dot">未启用监听</span>
                </div>
                <div id="speech-preview" class="ai-speech-preview">暂无语音输入...</div>
            </div>

            <div class="form-group">
                <label>常规控制 (匀速模式)</label>
                <div class="btn-row">
                    <button id="start-btn" class="primary">播放 (Space)</button>
                    <button id="reset-btn">重置 (Esc)</button>
                </div>
            </div>

            <div class="form-group">
                <label>每行强制字数 (个) <span id="max-chars-val" class="value-display">20</span></label>
                <input type="range" id="max-chars-slider" min="5" max="40" value="20">
            </div>

            <div class="form-group" id="speed-group-box">
                <label>常规滚动速度 (像素/秒) <span id="speed-val" class="value-display">45</span></label>
                <input type="range" id="speed-slider" min="10" max="250" value="45">
            </div>

            <div class="form-group">
                <label>文字大小 (px) <span id="font-val" class="value-display">55</span></label>
                <input type="range" id="font-slider" min="24" max="100" value="55">
            </div>

            <div class="form-group">
                <label>引导线高度位置 (%) <span id="line-pos-val" class="value-display">35</span></label>
                <input type="range" id="line-pos-slider" min="15" max="80" value="35">
            </div>
            <div class="form-group">
                <label>提词背景颜色</label>
                <div class="color-picker-group" id="bg-color-group">
                    <div class="color-dot selected" style="background: #111111;" data-color="#111111"></div>
                    <div class="color-dot" style="background: #22252a;" data-color="#22252a"></div>
                    <div class="color-dot" style="background: #091124;" data-color="#091124"></div>
                    <div class="color-dot" style="background: #091a0f;" data-color="#091a0f"></div>
                    <div class="more-color-wrapper">
                        <input type="color" id="bg-color-more" value="#111111">
                    </div>
                </div>
            </div>

            <div class="form-group">
                <label>普通文字颜色</label>
                <div class="color-picker-group" id="base-color-group">
                    <div class="color-dot selected" style="background: #555555;" data-color="#555555"></div>
                    <div class="color-dot" style="background: #888888;" data-color="#888888"></div>
                    <div class="color-dot" style="background: #ffffff;" data-color="#ffffff"></div>
                    <div class="color-dot" style="background: #224263;" data-color="#224263"></div>
                    <div class="more-color-wrapper">
                        <input type="color" id="base-color-more" value="#555555">
                    </div>
                </div>
            </div>

            <div class="form-group">
                <label>过线高亮颜色</label>
                <div class="color-picker-group" id="highlight-color-group">
                    <div class="color-dot selected" style="background: #00ff66;" data-color="#00ff66"></div>
                    <div class="color-dot" style="background: #ff007f;" data-color="#ff007f"></div>
                    <div class="color-dot" style="background: #00e5ff;" data-color="#00e5ff"></div>
                    <div class="color-dot" style="background: #ffcc00;" data-color="#ffcc00"></div>
                    <div class="more-color-wrapper">
                        <input type="color" id="highlight-color-more" value="#00ff66">
                    </div>
                </div>
            </div>
            <div class="form-group">
                <label>文本镜像 (分光镜适配)</label>
                <select id="mirror-select">
                    <option value="none">正常模式 (常规直视)</option>
                    <option value="x">水平镜像 (左右反转)</option>
                    <option value="y">垂直镜像 (上下反转)</option>
                    <option value="both">全镜像 (中心对称)</option>
                </select>
            </div>

            <div class="form-group" style="margin-top: 5px;">
                <button id="fullscreen-btn">进入全屏演讲模式</button>
            </div>
        </div>

        <!-- 右侧提词器主体区域 -->
        <div id="display-area">
            <button id="toggle-panel-btn">隐藏菜单</button>
            <div id="cue-zone"><div id="cue-line"></div></div>
            <div id="scroll-wrapper"><div id="text-content"></div></div>
            <div id="status-bar">模式: <span id="status-mode-text">常规滚动</span> | 状态: <span id="status-run">准备</span></div>
        </div>
    </div>

    <script>
        const textInput = document.getElementById('text-input');
        const textContent = document.getElementById('text-content');
        const scrollWrapper = document.getElementById('scroll-wrapper');
        const startBtn = document.getElementById('start-btn');
        const resetBtn = document.getElementById('reset-btn');
        const speedSlider = document.getElementById('speed-slider');
        const speedVal = document.getElementById('speed-val');
        const fontSlider = document.getElementById('font-slider');
        const fontVal = document.getElementById('font-val');
        const maxCharsSlider = document.getElementById('max-chars-slider');
        const maxCharsVal = document.getElementById('max-chars-val');
        const mirrorSelect = document.getElementById('mirror-select');
        const linePosSlider = document.getElementById('line-pos-slider');
        const linePosVal = document.getElementById('line-pos-val');
        const fullscreenBtn = document.getElementById('fullscreen-btn');
        const controlPanel = document.getElementById('control-panel');
        const togglePanelBtn = document.getElementById('toggle-panel-btn');
        const statusRun = document.getElementById('status-run');
        const statusModeText = document.getElementById('status-mode-text');

        const aiModeBtn = document.getElementById('ai-mode-btn');
        const aiDot = document.getElementById('ai-dot');
        const speechPreview = document.getElementById('speech-preview');
        const speedGroupBox = document.getElementById('speed-group-box');

        let isPlaying = false;
        let isHovered = false; 
        let speed = parseInt(speedSlider.value);
        let lastTimestamp = 0;
        let currentScrollY = 0;
        let animationFrameId = null;
        let observer = null;

        let isAiMode = false;
        let recognition = null;
        let isUserStoppingVoice = false; 
        let parsedLinesArray = [];
        
        // 【Turbo 核心变量】:引入历史锁步单向指针
        let currentLinePointer = 0; 

        function autoSplitText(rawText) {
            const paragraphs = rawText.split('\n');
            const processedLines = [];
            const maxCharsPerLine = parseInt(maxCharsSlider.value);

            paragraphs.forEach(para => {
                if (para.trim() === "") return; 
                let currentLine = "";
                for (let i = 0; i < para.length; i++) {
                    const char = para[i];
                    currentLine += char;
                    const isPunctuation = ['。', '!', '?', ';', ',', '.', '!', '?', ';', ','].includes(char);
                    if ((isPunctuation && currentLine.length >= Math.max(5, maxCharsPerLine * 0.6)) || currentLine.length >= maxCharsPerLine) {
                        if (i + 1 < para.length && ['。', '!', '?', ';', ',', '、', '.', '!', '?', ';', ','].includes(para[i+1])) {
                            continue;
                        }
                        processedLines.push(currentLine.trim());
                        currentLine = "";
                    }
                }
                if (currentLine.trim() !== "") processedLines.push(currentLine.trim());
            });
            return processedLines;
        }

        function syncTextPositionAndObserver() {
            const linePercent = parseInt(linePosSlider.value);
            const fontSize = parseInt(fontSlider.value);
            
            scrollWrapper.style.paddingTop = `calc(${linePercent}vh - ${fontSize * 0.6}px)`;
            scrollWrapper.style.paddingBottom = `calc(${100 - linePercent}vh + 150px)`;

            if (observer) observer.disconnect();
            const topMargin = linePercent - 1; 
            const bottomMargin = 100 - linePercent - 3; 

            observer = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting && entry.intersectionRatio > 0.15) {
                        if(!isAiMode) {
                            document.querySelectorAll('.script-line').forEach(el => el.classList.remove('active'));
                            entry.target.classList.add('active');
                        }
                    }
                });
            }, {
                root: scrollWrapper,
                rootMargin: `-${topMargin}% 0% -${bottomMargin}% 0%`, 
                threshold: [0.01, 0.15, 0.5, 0.8, 1.0]
            });

            document.querySelectorAll('.script-line').forEach(line => observer.observe(line));
        }

        linePosSlider.addEventListener('input', (e) => {
            const val = e.target.value;
            linePosVal.innerText = val;
            document.documentElement.style.setProperty('--cue-top', val + '%');
            syncTextPositionAndObserver();
            if (!isPlaying && !isAiMode) {
                scrollWrapper.scrollTop = 0;
                currentScrollY = 0;
            }
        });

        function renderText() {
            textContent.innerHTML = '';
            parsedLinesArray = autoSplitText(textInput.value);
            parsedLinesArray.forEach((lineText) => {
                const lineDiv = document.createElement('div');
                lineDiv.className = 'script-line';
                lineDiv.innerText = lineText;
                lineDiv.style.fontSize = fontSlider.value + 'px';
                textContent.appendChild(lineDiv);
            });
            if(textContent.children.length > 0) textContent.children[0].classList.add('active');
            syncTextPositionAndObserver();
        }

        maxCharsSlider.addEventListener('input', (e) => {
            maxCharsVal.innerText = e.target.value;
            renderText(); 
        });

        // 调色板
        function setupColorPicker(groupId, moreInputId, cssVariableName, onUpdateCallback) {
            const group = document.getElementById(groupId);
            const dots = group.querySelectorAll('.color-dot');
            const moreInput = document.getElementById(moreInputId);

            dots.forEach(dot => {
                dot.addEventListener('click', () => {
                    group.querySelector('.color-dot.selected')?.classList.remove('selected');
                    dot.classList.add('selected');
                    const color = dot.getAttribute('data-color');
                    document.documentElement.style.setProperty(cssVariableName, color);
                    if(onUpdateCallback) onUpdateCallback(color);
                });
            });

            moreInput.addEventListener('input', (e) => {
                group.querySelector('.color-dot.selected')?.classList.remove('selected');
                const color = e.target.value;
                document.documentElement.style.setProperty(cssVariableName, color);
                if(onUpdateCallback) onUpdateCallback(color);
            });
        }

        setupColorPicker('bg-color-group', 'bg-color-more', '--bg-color');
        setupColorPicker('base-color-group', 'base-color-more', '--text-color');
        setupColorPicker('highlight-color-group', 'highlight-color-more', '--highlight-color', (color) => {
            if (!isPlaying) startBtn.style.background = color;
        });

        function scrollEngine(timestamp) {
            if (!lastTimestamp) lastTimestamp = timestamp;
            const elapsed = timestamp - lastTimestamp;
            lastTimestamp = timestamp;

            if (isPlaying && !isHovered && !isAiMode) {
                currentScrollY += (speed / 1000) * elapsed;
                scrollWrapper.scrollTop = currentScrollY;

                const lines = document.querySelectorAll('.script-line');
                if (lines.length > 0) {
                    const lastLine = lines[lines.length - 1];
                    const lastLineRect = lastLine.getBoundingClientRect();
                    const cueZoneRect = document.getElementById('cue-zone').getBoundingClientRect();
                    if (lastLineRect.bottom < cueZoneRect.top) stop();
                }
            }
            if(!isAiMode) animationFrameId = requestAnimationFrame(scrollEngine);
        }

        function start() {
            if (isAiMode) return;
            if (!isPlaying) {
                isPlaying = true;
                startBtn.innerText = "暂停 (Space)";
                startBtn.style.background = "#ff3333";
                statusRun.innerText = "播放中";
                lastTimestamp = 0;
                currentScrollY = scrollWrapper.scrollTop;
                animationFrameId = requestAnimationFrame(scrollEngine);
            }
        }

        function stop() {
            if (isPlaying) {
                isPlaying = false;
                startBtn.innerText = "播放 (Space)";
                startBtn.style.background = "var(--highlight-color)";
                statusRun.innerText = "已暂停";
                if (animationFrameId) cancelAnimationFrame(animationFrameId);
            }
        }

        function togglePlay() { isPlaying ? stop() : start(); }

        function resetScroll() {
            stop();
            scrollWrapper.scrollTop = 0;
            currentScrollY = 0;
            currentLinePointer = 0;
            statusRun.innerText = "准备";
            document.querySelectorAll('.script-line').forEach((el, idx) => {
                if(idx === 0) el.classList.add('active');
                else el.classList.remove('active');
            });
            speechPreview.innerText = "暂无语音输入...";
        }

        // ================= 【深度整合:Turbo 级零延迟匹配引擎】 =================
        function initVoiceRecognition() {
            const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
            if (!SpeechRecognition) {
                alert("当前浏览器不支持原生语音识别。请使用 Chrome 或 Edge 浏览器。");
                disableAiMode();
                return;
            }

            recognition = new SpeechRecognition();
            recognition.continuous = true;       
            recognition.interimResults = true; // 抓取毫秒级未定型音频流
            recognition.lang = 'zh-CN';           

            recognition.onstart = () => {
                aiDot.innerText = "Turbo 级强行卡位聆听中...";
                aiDot.classList.add('active');
                statusRun.innerText = "语音跟随中";
            };

            recognition.onerror = (event) => {
                console.warn("语音通道异常: ", event.error);
                if (event.error === 'not-allowed') {
                    alert("麦克风授信被拒!");
                    disableAiMode();
                }
            };

            recognition.onend = () => {
                if (isAiMode && !isUserStoppingVoice) {
                    try { recognition.start(); } catch(e) {}
                } else {
                    aiDot.innerText = "监听已结束";
                    aiDot.classList.remove('active');
                }
            };

            recognition.onresult = (event) => {
                let textBuffer = '';
                for (let i = event.resultIndex; i < event.results.length; ++i) {
                    textBuffer += event.results[i][0].transcript;
                }
                
                const cleanSpeechText = textBuffer.replace(/[,。?!、.,?!]/g, "").trim();
                if(!cleanSpeechText) return;

                speechPreview.innerText = cleanSpeechText;
                
                // 触发激进的前端单字预判与历史锁步算法
                matchTextTurbo(cleanSpeechText);
            };
        }

        function matchTextTurbo(speechText) {
            const linesDOM = document.querySelectorAll('.script-line');
            if(linesDOM.length === 0) return;

            // 雷达扫描范围:当前指针位置往后看 4 行(丢弃久远的历史文本,减少 90% 无用计算)
            const lookAheadLimit = Math.min(currentLinePointer + 4, linesDOM.length);

            for (let i = currentLinePointer; i < lookAheadLimit; i++) {
                const cleanLineText = parsedLinesArray[i].replace(/[,。?!、.,?!]/g, "");
                
                // 加速策略 1:提取待命行的前 2 个字
                const lineStartTokens = cleanLineText.slice(0, 2);
                
                // 加速策略 2:提取实时语音最后吐出的 2~4 个字
                const realTimeVoiceTail = speechText.slice(-4);

                // 【首字强制唤醒条件】:新词尾部包含了下一行开头,或全串包含,或最后两字刚好咬合
                if (
                    realTimeVoiceTail.includes(lineStartTokens) || 
                    speechText.includes(cleanLineText) ||
                    (lineStartTokens && speechText.slice(-2) === lineStartTokens)
                ) {
                    if (i !== currentLinePointer) {
                        // 锁步向前,彻底封闭过去的行
                        currentLinePointer = i;

                        // 更新高亮样式
                        linesDOM.forEach(el => el.classList.remove('active'));
                        linesDOM[i].classList.add('active');

                        // 毫秒级像素咬合滚动
                        const wrapperHeight = scrollWrapper.clientHeight;
                        const linePercent = parseInt(linePosSlider.value);
                        const cueZonePixelTop = (linePercent / 100) * wrapperHeight;
                        const lineOffsetTop = linesDOM[i].offsetTop;

                        const targetScrollTop = lineOffsetTop - cueZonePixelTop + (linesDOM[i].clientHeight / 2);
                        scrollWrapper.scrollTop = Math.max(0, targetScrollTop);
                        break; 
                    }
                }
            }
        }

        function enableAiMode() {
            isAiMode = true;
            isUserStoppingVoice = false; 
            stop(); 
            scrollWrapper.classList.remove('instant-mode'); 
            aiModeBtn.innerText = "已开启";
            aiModeBtn.style.background = "var(--highlight-color)";
            aiModeBtn.style.color = "#000";
            statusModeText.innerText = "Turbo语音跟随";
            speedGroupBox.style.opacity = "0.3"; 
            speedGroupBox.style.pointerEvents = "none";
            startBtn.style.pointerEvents = "none"; 
            startBtn.style.opacity = "0.4";

            if (!recognition) initVoiceRecognition();
            currentLinePointer = 0; // 开启时重置历史锁步指针
            try { recognition.start(); } catch(e) {}
        }

        function disableAiMode() {
            isAiMode = false;
            isUserStoppingVoice = true; 
            scrollWrapper.classList.add('instant-mode'); 
            aiModeBtn.innerText = "已关闭";
            aiModeBtn.style.background = "#333";
            aiModeBtn.style.color = "#fff";
            aiDot.innerText = "未启用监听";
            aiDot.classList.remove('active');
            statusModeText.innerText = "常规滚动";
            speedGroupBox.style.opacity = "1";
            speedGroupBox.style.pointerEvents = "auto";
            startBtn.style.pointerEvents = "auto";
            startBtn.style.opacity = "1";
            speechPreview.innerText = "暂无语音输入...";

            if (recognition) { try { recognition.stop(); } catch(e) {} }
            resetScroll();
        }

        aiModeBtn.addEventListener('click', () => {
            if (!isAiMode) enableAiMode();
            else disableAiMode();
        });

        // 基础控制绑定
        textInput.addEventListener('input', renderText);
        textInput.addEventListener('paste', () => setTimeout(renderText, 50));
        startBtn.addEventListener('click', togglePlay);
        resetBtn.addEventListener('click', resetScroll);

        speedSlider.addEventListener('input', (e) => {
            speed = parseInt(e.target.value);
            speedVal.innerText = speed;
        });

        fontSlider.addEventListener('input', (e) => {
            fontVal.innerText = e.target.value;
            document.querySelectorAll('.script-line').forEach(el => el.style.fontSize = e.target.value + 'px');
            setTimeout(syncTextPositionAndObserver, 150);
        });

        mirrorSelect.addEventListener('change', (e) => {
            textContent.className = '';
            if (e.target.value !== 'none') textContent.classList.add('mirror-' + e.target.value);
        });

        togglePanelBtn.addEventListener('click', () => {
            controlPanel.classList.toggle('hidden');
            togglePanelBtn.innerText = controlPanel.classList.contains('hidden') ? "显示菜单" : "隐藏菜单";
        });

        fullscreenBtn.addEventListener('click', () => {
            if (!document.fullscreenElement) {
                document.documentElement.requestFullscreen().catch(err => alert(`全屏失败: ${err.message}`));
            } else {
                document.exitFullscreen();
            }
        });

        scrollWrapper.addEventListener('mouseenter', () => { isHovered = true; if(isPlaying && !isAiMode) statusRun.innerText = "悬停暂停"; });
        scrollWrapper.addEventListener('mouseleave', () => { isHovered = false; if(isPlaying && !isAiMode) statusRun.innerText = "播放中"; });

        window.addEventListener('keydown', (e) => {
            if (document.activeElement === textInput) return;
            if (e.code === 'Space') { e.preventDefault(); if(!isAiMode) togglePlay(); }
            if (e.code === 'Escape') resetScroll();
        });

        // 初始化
        renderText();
        resetScroll();
    </script>
</body>
</html>
乱码时代 发表于 2026-6-19 16:55
你手动滚屏功能要一下一下敲的吗?文本调用也好像没有
Snapdeal 发表于 2026-6-19 02:21
xiehaipeng 发表于 2026-6-19 08:06
win10  64位 运行失败  一堆报错
 楼主| just8 发表于 2026-6-19 14:18
Snapdeal 发表于 2026-6-19 02:21
请问如何去掉引导线和增加扩展模式?

怎么扩展模式?这是自己写的自己用的。感觉使用还可以了。
 楼主| just8 发表于 2026-6-19 14:19
xiehaipeng 发表于 2026-6-19 08:06
win10  64位 运行失败  一堆报错

我是win11用的,你上边那位应该也是能使用。只是有点不符合他的要求。
xiehaipeng 发表于 2026-6-19 14:56
just8 发表于 2026-6-19 14:19
我是win11用的,你上边那位应该也是能使用。只是有点不符合他的要求。

好的,感谢分享。
 楼主| just8 发表于 2026-6-19 17:25
乱码时代 发表于 2026-6-19 16:55
你手动滚屏功能要一下一下敲的吗?文本调用也好像没有

鼠标不要放在字幕区,鼠标放上不滚动。
如果不用AI识别滚动不灵活,就开一下AI,然后再关掉。再点开始,自动按行滚动。
Snapdeal 发表于 2026-6-19 21:14
just8 发表于 2026-6-19 14:18
怎么扩展模式?这是自己写的自己用的。感觉使用还可以了。

就是像PPT似的可以映射到副屏,不影响主屏控制操作这个软件。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-9-20 15:45

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表