@wendongfly/myhi 1.3.59 → 1.3.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat.html +186 -0
- package/dist/index.js +1 -1
- package/dist/index.min.js +117 -104
- package/package.json +1 -1
package/dist/chat.html
CHANGED
|
@@ -350,6 +350,7 @@
|
|
|
350
350
|
<button class="sk sk-claude" onclick="doSlashCmd('/rename')">命名</button>
|
|
351
351
|
<button class="sk sk-claude" onclick="openGitSheet()" style="color:#3fb950">提交</button>
|
|
352
352
|
<button class="sk sk-claude" onclick="showMemory()">记忆</button>
|
|
353
|
+
<button class="sk sk-claude" onclick="openVaSheet()" style="color:#1f6feb">语音</button>
|
|
353
354
|
<button class="sk sk-claude" onclick="openSkillSheet()" style="color:#9d5cf5">技能</button>
|
|
354
355
|
<span id="sk-builtin-skills"></span>
|
|
355
356
|
<span id="sk-custom-skills"></span>
|
|
@@ -569,6 +570,36 @@
|
|
|
569
570
|
</div>
|
|
570
571
|
</div>
|
|
571
572
|
|
|
573
|
+
<!-- 语音需求助手面板 -->
|
|
574
|
+
<div id="va-sheet" class="action-sheet">
|
|
575
|
+
<div class="action-sheet-backdrop" onclick="closeVaSheet()"></div>
|
|
576
|
+
<div class="action-sheet-box" style="max-height:90vh;display:flex;flex-direction:column">
|
|
577
|
+
<div class="action-sheet-title" style="display:flex;align-items:center;justify-content:space-between">
|
|
578
|
+
<span>语音需求助手</span>
|
|
579
|
+
<button onclick="vaClear()" style="background:none;border:none;color:#8b949e;font-size:0.78rem;cursor:pointer;padding:0.2rem 0.4rem">清除</button>
|
|
580
|
+
</div>
|
|
581
|
+
<div id="va-transcript" style="flex:1;overflow-y:auto;min-height:80px;max-height:28vh;padding:0.6rem 0.8rem;background:#0d1117;border:1px solid #30363d;border-radius:8px;margin:0 0.2rem 0.4rem;font-size:0.85rem;color:#c9d1d9;line-height:1.65;scrollbar-width:thin;white-space:pre-wrap">
|
|
582
|
+
<span style="color:#8b949e">按住大按钮说话,松手自动识别,多轮累积后整理成需求...</span>
|
|
583
|
+
</div>
|
|
584
|
+
<div id="va-result-wrap" style="display:none;padding:0 0.2rem 0.3rem">
|
|
585
|
+
<div style="font-size:0.72rem;color:#8b949e;margin-bottom:0.2rem">整理后的需求</div>
|
|
586
|
+
<textarea id="va-result" class="slash-inp" rows="6" style="resize:vertical;font-size:0.82rem;line-height:1.5;font-family:monospace"></textarea>
|
|
587
|
+
</div>
|
|
588
|
+
<button id="va-record-btn"
|
|
589
|
+
style="background:#1f6feb;color:#fff;font-size:1.1rem;font-weight:700;padding:1.1rem;margin:0 0.2rem 0.3rem;border:none;border-radius:12px;cursor:pointer;touch-action:none;user-select:none;-webkit-user-select:none">
|
|
590
|
+
按住说话
|
|
591
|
+
</button>
|
|
592
|
+
<div id="va-status" style="text-align:center;font-size:0.78rem;color:#8b949e;min-height:1.3em;margin-bottom:0.2rem"></div>
|
|
593
|
+
<div style="display:flex;gap:0.4rem;padding:0 0.2rem">
|
|
594
|
+
<button id="va-organize-btn" class="action-sheet-cancel" onclick="vaOrganize()"
|
|
595
|
+
style="flex:1;background:#7c3aed;color:#fff;font-weight:600;margin-bottom:0">整理成需求</button>
|
|
596
|
+
<button id="va-send-btn" class="action-sheet-cancel" onclick="vaSend()"
|
|
597
|
+
style="flex:1;background:#1f6feb;color:#fff;font-weight:600;margin-bottom:0;display:none">发送到对话</button>
|
|
598
|
+
</div>
|
|
599
|
+
<button class="action-sheet-cancel" onclick="closeVaSheet()" style="margin-top:0.4rem">关闭</button>
|
|
600
|
+
</div>
|
|
601
|
+
</div>
|
|
602
|
+
|
|
572
603
|
<div id="status-overlay">连接中...</div>
|
|
573
604
|
|
|
574
605
|
<script type="module">
|
|
@@ -3060,6 +3091,161 @@
|
|
|
3060
3091
|
|
|
3061
3092
|
window.toggleVoice = _startVoice; // 保留 onclick 兼容
|
|
3062
3093
|
|
|
3094
|
+
// ── 语音需求助手 ──────────────────────────────────────────
|
|
3095
|
+
let _vaLines = [];
|
|
3096
|
+
let _vaMR = null;
|
|
3097
|
+
let _vaActive = false;
|
|
3098
|
+
let _vaStartTime2 = 0;
|
|
3099
|
+
|
|
3100
|
+
const _vaBtn = document.getElementById('va-record-btn');
|
|
3101
|
+
const _vaStatusEl = document.getElementById('va-status');
|
|
3102
|
+
|
|
3103
|
+
function _vaSetStatus(t) { _vaStatusEl.textContent = t || ''; }
|
|
3104
|
+
|
|
3105
|
+
function _vaRender() {
|
|
3106
|
+
const el = document.getElementById('va-transcript');
|
|
3107
|
+
if (_vaLines.length === 0) {
|
|
3108
|
+
el.innerHTML = '<span style="color:#8b949e">按住大按钮说话,松手自动识别,多轮累积后整理成需求...</span>';
|
|
3109
|
+
} else {
|
|
3110
|
+
el.textContent = _vaLines.map((t, i) => `[${i + 1}] ${t}`).join('\n\n');
|
|
3111
|
+
el.scrollTop = el.scrollHeight;
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
|
|
3115
|
+
async function _vaStart() {
|
|
3116
|
+
if (_vaActive) return;
|
|
3117
|
+
if (!navigator.mediaDevices?.getUserMedia) {
|
|
3118
|
+
_vaSetStatus('浏览器不支持麦克风(需要 HTTPS)'); return;
|
|
3119
|
+
}
|
|
3120
|
+
let stream;
|
|
3121
|
+
try {
|
|
3122
|
+
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
3123
|
+
} catch (e) {
|
|
3124
|
+
_vaSetStatus(e.name === 'NotAllowedError' ? '麦克风权限被拒绝,请在地址栏允许' : '麦克风失败: ' + e.message);
|
|
3125
|
+
return;
|
|
3126
|
+
}
|
|
3127
|
+
const chunks = [];
|
|
3128
|
+
_vaMR = new MediaRecorder(stream);
|
|
3129
|
+
_vaMR.ondataavailable = (e) => { if (e.data.size > 0) chunks.push(e.data); };
|
|
3130
|
+
_vaMR.onstop = async () => {
|
|
3131
|
+
stream.getTracks().forEach(t => t.stop());
|
|
3132
|
+
await _vaTranscribe(new Blob(chunks, { type: _vaMR.mimeType || 'audio/webm' }));
|
|
3133
|
+
};
|
|
3134
|
+
_vaMR.start();
|
|
3135
|
+
_vaActive = true;
|
|
3136
|
+
_vaBtn.textContent = '松手停止';
|
|
3137
|
+
_vaBtn.style.background = '#da3633';
|
|
3138
|
+
_vaSetStatus('录音中...');
|
|
3139
|
+
}
|
|
3140
|
+
|
|
3141
|
+
function _vaStop() {
|
|
3142
|
+
if (!_vaActive || !_vaMR) return;
|
|
3143
|
+
_vaActive = false;
|
|
3144
|
+
_vaBtn.textContent = '按住说话';
|
|
3145
|
+
_vaBtn.style.background = '#1f6feb';
|
|
3146
|
+
_vaSetStatus('识别中...');
|
|
3147
|
+
_vaMR.stop();
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
async function _vaTranscribe(blob) {
|
|
3151
|
+
try {
|
|
3152
|
+
const form = new FormData();
|
|
3153
|
+
form.append('audio', blob, 'audio.webm');
|
|
3154
|
+
const resp = await fetch(`/api/voice-transcribe?sessionId=${SESSION_ID}`, { method: 'POST', body: form });
|
|
3155
|
+
const data = await resp.json();
|
|
3156
|
+
if (resp.status === 503) {
|
|
3157
|
+
_vaSetStatus('ASR 服务未配置');
|
|
3158
|
+
} else if (data.text) {
|
|
3159
|
+
_vaLines.push(data.text);
|
|
3160
|
+
_vaRender();
|
|
3161
|
+
_vaSetStatus(`第 ${_vaLines.length} 段已录入,可继续录或点"整理成需求"`);
|
|
3162
|
+
} else {
|
|
3163
|
+
_vaSetStatus('未识别到内容,请重试');
|
|
3164
|
+
}
|
|
3165
|
+
} catch (e) {
|
|
3166
|
+
_vaSetStatus('上传失败: ' + e.message);
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
|
|
3170
|
+
window.openVaSheet = function() {
|
|
3171
|
+
document.getElementById('va-sheet').classList.add('active');
|
|
3172
|
+
_vaRender();
|
|
3173
|
+
};
|
|
3174
|
+
window.closeVaSheet = function() {
|
|
3175
|
+
document.getElementById('va-sheet').classList.remove('active');
|
|
3176
|
+
if (_vaActive) _vaStop();
|
|
3177
|
+
};
|
|
3178
|
+
window.vaClear = function() {
|
|
3179
|
+
_vaLines = [];
|
|
3180
|
+
document.getElementById('va-result-wrap').style.display = 'none';
|
|
3181
|
+
document.getElementById('va-send-btn').style.display = 'none';
|
|
3182
|
+
document.getElementById('va-organize-btn').style.display = '';
|
|
3183
|
+
_vaRender();
|
|
3184
|
+
_vaSetStatus('');
|
|
3185
|
+
};
|
|
3186
|
+
window.vaOrganize = async function() {
|
|
3187
|
+
if (_vaLines.length === 0) { _vaSetStatus('请先录入语音内容'); return; }
|
|
3188
|
+
const btn = document.getElementById('va-organize-btn');
|
|
3189
|
+
btn.textContent = '整理中...';
|
|
3190
|
+
btn.disabled = true;
|
|
3191
|
+
try {
|
|
3192
|
+
const resp = await fetch('/api/voice-assist/organize', {
|
|
3193
|
+
method: 'POST',
|
|
3194
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3195
|
+
body: JSON.stringify({ transcript: _vaLines.join('\n'), sessionId: SESSION_ID }),
|
|
3196
|
+
});
|
|
3197
|
+
const data = await resp.json();
|
|
3198
|
+
if (data.requirement) {
|
|
3199
|
+
document.getElementById('va-result').value = data.requirement;
|
|
3200
|
+
document.getElementById('va-result-wrap').style.display = '';
|
|
3201
|
+
document.getElementById('va-send-btn').style.display = '';
|
|
3202
|
+
_vaSetStatus('整理完成,可编辑后点"发送到对话"');
|
|
3203
|
+
} else {
|
|
3204
|
+
_vaSetStatus('整理失败: ' + (data.error || '未知错误'));
|
|
3205
|
+
}
|
|
3206
|
+
} catch (e) {
|
|
3207
|
+
_vaSetStatus('请求失败: ' + e.message);
|
|
3208
|
+
} finally {
|
|
3209
|
+
btn.textContent = '整理成需求';
|
|
3210
|
+
btn.disabled = false;
|
|
3211
|
+
}
|
|
3212
|
+
};
|
|
3213
|
+
window.vaSend = function() {
|
|
3214
|
+
const text = document.getElementById('va-result').value.trim();
|
|
3215
|
+
if (!text) return;
|
|
3216
|
+
cmdInput.value = text;
|
|
3217
|
+
window.closeVaSheet();
|
|
3218
|
+
if (!_isTouchDevice()) cmdInput.focus();
|
|
3219
|
+
};
|
|
3220
|
+
|
|
3221
|
+
// 大录音按钮事件(长按逻辑与主录音按钮一致)
|
|
3222
|
+
_vaBtn.addEventListener('pointerdown', (e) => {
|
|
3223
|
+
e.preventDefault();
|
|
3224
|
+
_vaStartTime2 = Date.now();
|
|
3225
|
+
_vaStart();
|
|
3226
|
+
});
|
|
3227
|
+
_vaBtn.addEventListener('pointerup', (e) => {
|
|
3228
|
+
e.preventDefault();
|
|
3229
|
+
if (!_vaActive) return;
|
|
3230
|
+
const elapsed = Date.now() - _vaStartTime2;
|
|
3231
|
+
if (elapsed < MIN_RECORD_MS) {
|
|
3232
|
+
_vaActive = false;
|
|
3233
|
+
_vaBtn.textContent = '按住说话';
|
|
3234
|
+
_vaBtn.style.background = '#1f6feb';
|
|
3235
|
+
try { _vaMR?.stream?.getTracks().forEach(t => t.stop()); } catch {}
|
|
3236
|
+
try { if (_vaMR?.state !== 'inactive') _vaMR?.stop(); } catch {}
|
|
3237
|
+
_vaMR = null;
|
|
3238
|
+
_vaSetStatus('请按住按钮说话,松手识别');
|
|
3239
|
+
return;
|
|
3240
|
+
}
|
|
3241
|
+
_vaStop();
|
|
3242
|
+
});
|
|
3243
|
+
_vaBtn.addEventListener('pointercancel', () => { if (_vaActive) _vaStop(); });
|
|
3244
|
+
_vaBtn.addEventListener('click', (e) => {
|
|
3245
|
+
if (_isTouchDevice()) return; // 触屏由 pointer 处理
|
|
3246
|
+
if (_vaActive) { _vaStop(); } else { _vaStart(); }
|
|
3247
|
+
});
|
|
3248
|
+
|
|
3063
3249
|
cmdInput.focus();
|
|
3064
3250
|
|
|
3065
3251
|
// ── 版本更新检查 ──────────────────────────────
|