agentvibes 4.2.0 → 4.4.0
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/.agentvibes/bmad/bmad-voices.md +69 -69
- package/.agentvibes/config.json +12 -0
- package/.claude/activation-instructions +54 -54
- package/.claude/audio/tracks/README.md +52 -52
- package/.claude/commands/agent-vibes/add.md +21 -21
- package/.claude/commands/agent-vibes/agent-vibes.md +101 -101
- package/.claude/commands/agent-vibes/agent.md +79 -79
- package/.claude/commands/agent-vibes/background-music.md +111 -111
- package/.claude/commands/agent-vibes/bmad.md +198 -198
- package/.claude/commands/agent-vibes/clean.md +18 -18
- package/.claude/commands/agent-vibes/cleanup.md +18 -18
- package/.claude/commands/agent-vibes/commands.json +145 -145
- package/.claude/commands/agent-vibes/effects.md +97 -97
- package/.claude/commands/agent-vibes/get.md +9 -9
- package/.claude/commands/agent-vibes/hide.md +91 -91
- package/.claude/commands/agent-vibes/language.md +23 -23
- package/.claude/commands/agent-vibes/learn.md +67 -67
- package/.claude/commands/agent-vibes/list.md +13 -13
- package/.claude/commands/agent-vibes/mute.md +37 -37
- package/.claude/commands/agent-vibes/preview.md +17 -17
- package/.claude/commands/agent-vibes/provider.md +68 -68
- package/.claude/commands/agent-vibes/replay-target.md +14 -14
- package/.claude/commands/agent-vibes/sample.md +12 -12
- package/.claude/commands/agent-vibes/set-favorite-voice.md +84 -84
- package/.claude/commands/agent-vibes/set-pretext.md +65 -65
- package/.claude/commands/agent-vibes/set-speed.md +41 -41
- package/.claude/commands/agent-vibes/show.md +84 -84
- package/.claude/commands/agent-vibes/switch.md +87 -87
- package/.claude/commands/agent-vibes/target-voice.md +26 -26
- package/.claude/commands/agent-vibes/target.md +30 -30
- package/.claude/commands/agent-vibes/translate.md +68 -68
- package/.claude/commands/agent-vibes/unmute.md +45 -45
- package/.claude/commands/agent-vibes/verbosity.md +89 -89
- package/.claude/commands/agent-vibes/whoami.md +7 -7
- package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
- package/.claude/commands/agent-vibes-rdp.md +24 -24
- package/.claude/config/agentvibes.json +1 -0
- package/.claude/config/audio-effects.cfg +2 -2
- package/.claude/config/audio-effects.cfg.sample +52 -52
- package/.claude/config/background-music-volume.txt +1 -0
- package/.claude/config/intro-text.txt +1 -0
- package/.claude/config/piper-speech-rate.txt +4 -0
- package/.claude/config/piper-target-speech-rate.txt +1 -0
- package/.claude/config/reverb-level.txt +1 -0
- package/.claude/config/tts-speech-rate.txt +4 -0
- package/.claude/config/tts-target-speech-rate.txt +1 -0
- package/.claude/docs/TERMUX_SETUP.md +408 -408
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/README-TTS-QUEUE.md +135 -135
- package/.claude/hooks/audio-cache-utils.sh +246 -246
- package/.claude/hooks/audio-processor.sh +433 -433
- package/.claude/hooks/background-music-manager.sh +404 -404
- package/.claude/hooks/bmad-speak-enhanced.sh +165 -165
- package/.claude/hooks/bmad-speak.sh +269 -269
- package/.claude/hooks/bmad-tts-injector.sh +568 -568
- package/.claude/hooks/bmad-voice-manager.sh +928 -928
- package/.claude/hooks/clawdbot-receiver-SECURE.sh +129 -129
- package/.claude/hooks/clawdbot-receiver.sh +107 -107
- package/.claude/hooks/clean-audio-cache.sh +22 -22
- package/.claude/hooks/cleanup-cache.sh +106 -106
- package/.claude/hooks/configure-rdp-mode.sh +137 -137
- package/.claude/hooks/download-extra-voices.sh +244 -244
- package/.claude/hooks/effects-manager.sh +268 -268
- package/.claude/hooks/github-star-reminder.sh +154 -154
- package/.claude/hooks/language-manager.sh +362 -362
- package/.claude/hooks/learn-manager.sh +492 -492
- package/.claude/hooks/macos-voice-manager.sh +205 -205
- package/.claude/hooks/migrate-background-music.sh +125 -125
- package/.claude/hooks/migrate-to-agentvibes.sh +161 -161
- package/.claude/hooks/optimize-background-music.sh +87 -87
- package/.claude/hooks/path-resolver.sh +60 -60
- package/.claude/hooks/personality-manager.sh +448 -448
- package/.claude/hooks/piper-download-voices.sh +225 -225
- package/.claude/hooks/piper-installer.sh +292 -292
- package/.claude/hooks/piper-multispeaker-registry.sh +171 -171
- package/.claude/hooks/piper-voice-manager.sh +24 -3
- package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +90 -90
- package/.claude/hooks/play-tts-enhanced.sh +105 -105
- package/.claude/hooks/play-tts-macos.sh +368 -368
- package/.claude/hooks/play-tts-piper.sh +679 -679
- package/.claude/hooks/play-tts-soprano.sh +356 -356
- package/.claude/hooks/play-tts-ssh-remote.sh +167 -167
- package/.claude/hooks/play-tts-termux-ssh.sh +169 -169
- package/.claude/hooks/play-tts.sh +301 -301
- package/.claude/hooks/prepare-release.sh +54 -54
- package/.claude/hooks/provider-commands.sh +617 -617
- package/.claude/hooks/provider-manager.sh +399 -399
- package/.claude/hooks/replay-target-audio.sh +95 -95
- package/.claude/hooks/requirements.txt +6 -6
- package/.claude/hooks/sentiment-manager.sh +201 -201
- package/.claude/hooks/session-start-tts.sh +81 -81
- package/.claude/hooks/soprano-gradio-synth.py +139 -139
- package/.claude/hooks/speed-manager.sh +291 -291
- package/.claude/hooks/stop-tts.sh +84 -84
- package/.claude/hooks/termux-installer.sh +261 -261
- package/.claude/hooks/translate-manager.sh +341 -341
- package/.claude/hooks/translator.py +237 -237
- package/.claude/hooks/tts-queue-worker.sh +145 -145
- package/.claude/hooks/tts-queue.sh +165 -165
- package/.claude/hooks/verbosity-manager.sh +178 -178
- package/.claude/hooks/voice-manager.sh +548 -548
- package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
- package/.claude/hooks-windows/background-music-manager.ps1 +348 -0
- package/.claude/hooks-windows/clean-audio-cache.ps1 +53 -0
- package/.claude/hooks-windows/download-extra-voices.ps1 +185 -0
- package/.claude/hooks-windows/effects-manager.ps1 +294 -0
- package/.claude/hooks-windows/language-manager.ps1 +193 -0
- package/.claude/hooks-windows/learn-manager.ps1 +241 -0
- package/.claude/hooks-windows/personality-manager.ps1 +266 -0
- package/.claude/hooks-windows/play-tts-piper.ps1 +209 -0
- package/.claude/hooks-windows/play-tts-sapi.ps1 +108 -0
- package/.claude/hooks-windows/play-tts-soprano.ps1 +159 -158
- package/.claude/hooks-windows/play-tts-windows-piper.ps1 +50 -5
- package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +108 -108
- package/.claude/hooks-windows/play-tts.ps1 +344 -266
- package/.claude/hooks-windows/provider-manager.ps1 +29 -10
- package/.claude/hooks-windows/session-start-tts.ps1 +124 -124
- package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
- package/.claude/hooks-windows/speed-manager.ps1 +166 -0
- package/.claude/hooks-windows/verbosity-manager.ps1 +119 -0
- package/.claude/hooks-windows/voice-manager-windows.ps1 +92 -8
- package/.claude/output-styles/agent-vibes.md +202 -202
- package/.claude/personalities/angry.md +14 -14
- package/.claude/personalities/annoying.md +14 -14
- package/.claude/personalities/crass.md +14 -14
- package/.claude/personalities/dramatic.md +14 -14
- package/.claude/personalities/dry-humor.md +50 -50
- package/.claude/personalities/flirty.md +20 -20
- package/.claude/personalities/funny.md +14 -14
- package/.claude/personalities/grandpa.md +32 -32
- package/.claude/personalities/millennial.md +14 -14
- package/.claude/personalities/moody.md +14 -14
- package/.claude/personalities/normal.md +16 -16
- package/.claude/personalities/pirate.md +14 -14
- package/.claude/personalities/poetic.md +14 -14
- package/.claude/personalities/professional.md +14 -14
- package/.claude/personalities/rapper.md +55 -55
- package/.claude/personalities/robot.md +14 -14
- package/.claude/personalities/sarcastic.md +38 -38
- package/.claude/personalities/sassy.md +14 -14
- package/.claude/personalities/surfer-dude.md +14 -14
- package/.claude/personalities/zen.md +14 -14
- package/.claude/settings.json +15 -15
- package/.claude/verbosity.txt +1 -1
- package/.clawdbot/README.md +105 -105
- package/.clawdbot/skill/SKILL.md +241 -241
- package/.mcp.json +12 -0
- package/CLAUDE.md +170 -170
- package/README.md +2029 -2007
- package/RELEASE_NOTES.md +1310 -1203
- package/WINDOWS-SETUP.md +208 -208
- package/bin/agent-vibes +39 -39
- package/bin/agentvibes-voice-browser.js +1840 -1840
- package/bin/agentvibes.js +48 -2
- package/bin/mcp-server.js +121 -121
- package/bin/mcp-server.sh +206 -206
- package/bin/test-bmad-pr +78 -78
- package/mcp-server/QUICK_START.md +203 -203
- package/mcp-server/README.md +345 -345
- package/mcp-server/WINDOWS_SETUP.md +260 -260
- package/mcp-server/docs/troubleshooting-audio.md +313 -313
- package/mcp-server/examples/claude_desktop_config.json +11 -11
- package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
- package/mcp-server/examples/custom_instructions.md +169 -169
- package/mcp-server/install-deps.js +130 -130
- package/mcp-server/pyproject.toml +52 -52
- package/mcp-server/requirements.txt +2 -2
- package/mcp-server/server.py +1465 -1453
- package/mcp-server/test_server.py +395 -395
- package/mcp-server/test_windows_script_parity.py +336 -0
- package/package.json +110 -110
- package/setup-windows.ps1 +815 -815
- package/src/bmad-detector.js +71 -71
- package/src/cli/list-personalities.js +110 -110
- package/src/cli/list-voices.js +114 -114
- package/src/commands/bmad-voices.js +394 -394
- package/src/commands/install-mcp.js +476 -476
- package/src/console/app.js +824 -824
- package/src/console/audio-env.js +20 -1
- package/src/console/brand-colors.js +13 -13
- package/src/console/constants/personalities.js +44 -44
- package/src/console/footer-config.js +50 -50
- package/src/console/modals/modal-overlay.js +247 -247
- package/src/console/navigation.js +62 -62
- package/src/console/tabs/agents-tab.js +1684 -1516
- package/src/console/tabs/help-tab.js +261 -261
- package/src/console/tabs/install-tab.js +1007 -991
- package/src/console/tabs/music-tab.js +22 -8
- package/src/console/tabs/placeholder-tab.js +53 -53
- package/src/console/tabs/readme-tab.js +267 -267
- package/src/console/tabs/receiver-tab.js +1472 -1212
- package/src/console/tabs/settings-tab.js +152 -79
- package/src/console/tabs/voices-tab.js +100 -21
- package/src/console/widgets/destroy-list.js +25 -25
- package/src/console/widgets/format-utils.js +89 -89
- package/src/console/widgets/notice.js +55 -55
- package/src/console/widgets/personality-picker.js +185 -185
- package/src/console/widgets/reverb-picker.js +94 -94
- package/src/console/widgets/track-picker.js +285 -285
- package/src/installer/music-file-input.js +304 -304
- package/src/installer.js +5882 -5829
- package/src/services/agent-voice-store.js +423 -423
- package/src/services/config-service.js +264 -264
- package/src/services/navigation-service.js +123 -123
- package/src/services/provider-service.js +132 -132
- package/src/services/verbosity-service.js +157 -157
- package/src/utils/audio-duration-validator.js +298 -298
- package/src/utils/audio-format-validator.js +277 -277
- package/src/utils/dependency-checker.js +469 -466
- package/src/utils/file-ownership-verifier.js +358 -358
- package/src/utils/list-formatter.js +194 -194
- package/src/utils/music-file-validator.js +285 -285
- package/src/utils/preview-list-prompt.js +136 -136
- package/src/utils/provider-validator.js +96 -12
- package/src/utils/secure-music-storage.js +412 -412
- package/templates/agentvibes-receiver.sh +482 -482
- package/templates/audio/welcome-music.mp3 +0 -0
- package/voice-assignments.json +8244 -8244
- package/.claude/config/background-music-position.txt +0 -1
|
@@ -29,6 +29,25 @@ import { formatTrackName as _sharedFormatTrackName, formatReverbState as _shared
|
|
|
29
29
|
import { showNotice as _showNoticeWidget } from '../widgets/notice.js';
|
|
30
30
|
|
|
31
31
|
const IS_TEST = process.env.AGENTVIBES_TEST_MODE === 'true';
|
|
32
|
+
const _IS_WINDOWS = process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
|
|
33
|
+
|
|
34
|
+
/** Resolve piper binary path — uses exe on Windows, 'piper' in PATH on Unix */
|
|
35
|
+
function _resolvePiperBin() {
|
|
36
|
+
if (_IS_WINDOWS) {
|
|
37
|
+
const lad = process.env.LOCALAPPDATA ||
|
|
38
|
+
(process.env.USERPROFILE ? path.join(process.env.USERPROFILE, 'AppData', 'Local') : null);
|
|
39
|
+
if (lad) {
|
|
40
|
+
const exe = path.join(lad, 'Programs', 'Piper', 'piper.exe');
|
|
41
|
+
if (fs.existsSync(exe)) return exe;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return 'piper';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Build spawn options with Windows-safe defaults (no visible console, no detached) */
|
|
48
|
+
function _spawnOpts(env, extraOpts = {}) {
|
|
49
|
+
return { stdio: 'ignore', detached: !_IS_WINDOWS, windowsHide: true, env, ...extraOpts };
|
|
50
|
+
}
|
|
32
51
|
|
|
33
52
|
// Sanitize strings before passing as env vars to shell commands.
|
|
34
53
|
// Removes characters that could cause shell injection when expanded inside sh -c.
|
|
@@ -51,19 +70,19 @@ const _modalTitle = (text) => ` {${BRAND_PINK}-fg}${text}{/${BRAND_PINK}-fg} `;
|
|
|
51
70
|
|
|
52
71
|
const COLORS = {
|
|
53
72
|
contentBg: '#0a0e1a', // Near-black content background
|
|
54
|
-
sectionHdr: '
|
|
73
|
+
sectionHdr: 'bright-cyan', // Matches header "Agent" color
|
|
55
74
|
labelFg: '#e3f2fd', // Light blue text — labels
|
|
56
75
|
valueFg: '#ffff00', // Yellow — current values
|
|
57
76
|
btnDefault: '#37474f', // Dark slate — default button bg
|
|
58
|
-
btnFocus: '#
|
|
59
|
-
btnFocusFg: '#
|
|
77
|
+
btnFocus: '#2e7d32', // Green — focused/selected button bg
|
|
78
|
+
btnFocusFg: '#ffffff', // White — focused button text
|
|
60
79
|
btnPress: '#ff00ff', // Magenta — pressed button bg
|
|
61
80
|
btnChange: '#37474f', // Dark slate — Change buttons
|
|
62
81
|
btnTest: '#37474f', // Dark slate — Test buttons
|
|
63
82
|
btnEdit: '#37474f', // Dark slate — Edit buttons
|
|
64
83
|
btnEnableOn: '#37474f', // Dark slate — Enabled toggle
|
|
65
84
|
btnEnableOff: '#37474f', // Dark slate — Disabled toggle
|
|
66
|
-
borderFg: '
|
|
85
|
+
borderFg: 'bright-cyan', // Matches section headers
|
|
67
86
|
footerBg: '#2196f3', // Blue — settings footer
|
|
68
87
|
noticeFg: '#90a4ae', // Gray — stub notice text
|
|
69
88
|
};
|
|
@@ -413,9 +432,14 @@ export function createSettingsTab(screen, services) {
|
|
|
413
432
|
`play "${trackPath}" repeat 9999 vol ${volFraction}`,
|
|
414
433
|
`mpg123 -q --loop -1 "${trackPath}"`,
|
|
415
434
|
].join(' 2>/dev/null || ') + ' 2>/dev/null';
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
435
|
+
if (_IS_WINDOWS) {
|
|
436
|
+
const _mp3P = detectMp3Player(_testEnv);
|
|
437
|
+
_testMusicProc = _mp3P
|
|
438
|
+
? spawn(_mp3P.bin, _mp3P.args(trackPath), _spawnOpts(_testEnv))
|
|
439
|
+
: null;
|
|
440
|
+
} else {
|
|
441
|
+
_testMusicProc = spawn('sh', ['-c', musicCmd], _spawnOpts(_testEnv));
|
|
442
|
+
}
|
|
419
443
|
_testMusicProc.unref();
|
|
420
444
|
}
|
|
421
445
|
}
|
|
@@ -463,7 +487,7 @@ export function createSettingsTab(screen, services) {
|
|
|
463
487
|
`soprano "$_AV_PHRASE" -o "$_AV_WAV"`,
|
|
464
488
|
].join(' || ');
|
|
465
489
|
synthProc = spawn('sh', ['-c', cmd], {
|
|
466
|
-
stdio: 'ignore', detached: true, env: sopranoEnv,
|
|
490
|
+
stdio: 'ignore', detached: !_IS_WINDOWS, windowsHide: true, env: sopranoEnv,
|
|
467
491
|
});
|
|
468
492
|
} else {
|
|
469
493
|
const voiceId = providerService.getActiveVoiceId();
|
|
@@ -476,8 +500,8 @@ export function createSettingsTab(screen, services) {
|
|
|
476
500
|
}
|
|
477
501
|
const _piperArgs = ['--model', voicePath, '--output_file', tempWav];
|
|
478
502
|
if (_ms.speakerId != null) _piperArgs.push('--speaker', String(_ms.speakerId));
|
|
479
|
-
synthProc = spawn(
|
|
480
|
-
stdio: ['pipe', 'ignore', 'ignore'], detached: true, env: _testEnv,
|
|
503
|
+
synthProc = spawn(_resolvePiperBin(), _piperArgs, {
|
|
504
|
+
stdio: ['pipe', 'ignore', 'ignore'], detached: !_IS_WINDOWS, windowsHide: true, env: _testEnv,
|
|
481
505
|
});
|
|
482
506
|
synthProc.stdin.write(ttsInput + '\n');
|
|
483
507
|
synthProc.stdin.end();
|
|
@@ -528,7 +552,7 @@ export function createSettingsTab(screen, services) {
|
|
|
528
552
|
_setTestBtnsLabel('■ Stop');
|
|
529
553
|
const _wavPlayer1 = detectWavPlayer(_testEnv);
|
|
530
554
|
const playProc = _wavPlayer1
|
|
531
|
-
? spawn(_wavPlayer1.bin, _wavPlayer1.args(wavToPlay),
|
|
555
|
+
? spawn(_wavPlayer1.bin, _wavPlayer1.args(wavToPlay), _spawnOpts(_testEnv))
|
|
532
556
|
: null;
|
|
533
557
|
if (!playProc) { _killTest(); _restoreTestBtnsLabels(); return; }
|
|
534
558
|
_testVoiceProc = playProc;
|
|
@@ -633,9 +657,14 @@ export function createSettingsTab(screen, services) {
|
|
|
633
657
|
`mpg123 -q "${trackPath}"`,
|
|
634
658
|
].join(' 2>/dev/null || ') + ' 2>/dev/null';
|
|
635
659
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
660
|
+
if (_IS_WINDOWS) {
|
|
661
|
+
const _mp3P2 = detectMp3Player(_testEnv);
|
|
662
|
+
_musicTestProc = _mp3P2
|
|
663
|
+
? spawn(_mp3P2.bin, _mp3P2.args(trackPath), _spawnOpts(_testEnv))
|
|
664
|
+
: null;
|
|
665
|
+
} else {
|
|
666
|
+
_musicTestProc = spawn('sh', ['-c', cmd], _spawnOpts(_testEnv));
|
|
667
|
+
}
|
|
639
668
|
_musicTestProc.unref();
|
|
640
669
|
musicTestBtn.setContent('■ Stop');
|
|
641
670
|
screen.render();
|
|
@@ -728,7 +757,7 @@ export function createSettingsTab(screen, services) {
|
|
|
728
757
|
content: lbl, width: lbl.length, height: 1,
|
|
729
758
|
top: 0, left: _xOff,
|
|
730
759
|
keys: true, focusable: true,
|
|
731
|
-
style: { fg: '
|
|
760
|
+
style: { fg: 'bright-cyan', bg: '#263238' },
|
|
732
761
|
});
|
|
733
762
|
_subTabItemsMap[id] = item;
|
|
734
763
|
_xOff += lbl.length;
|
|
@@ -742,7 +771,7 @@ export function createSettingsTab(screen, services) {
|
|
|
742
771
|
item.style.bg = '#0288d1'; // light blue — active tab
|
|
743
772
|
item.style.bold = true;
|
|
744
773
|
} else {
|
|
745
|
-
item.style.fg = '
|
|
774
|
+
item.style.fg = 'bright-cyan';
|
|
746
775
|
item.style.bg = '#263238';
|
|
747
776
|
item.style.bold = false;
|
|
748
777
|
}
|
|
@@ -776,12 +805,24 @@ export function createSettingsTab(screen, services) {
|
|
|
776
805
|
});
|
|
777
806
|
}
|
|
778
807
|
|
|
808
|
+
// -------------------------------------------------------------------------
|
|
809
|
+
// Section header: ── Provider & Voice ──
|
|
810
|
+
|
|
811
|
+
const providerVoiceHeader = blessed.text({
|
|
812
|
+
parent: box,
|
|
813
|
+
top: 3,
|
|
814
|
+
left: 1,
|
|
815
|
+
content: '{bright-cyan-fg} 🎤 Provider & Voice {/bright-cyan-fg}',
|
|
816
|
+
tags: true,
|
|
817
|
+
style: { bg: COLORS.contentBg },
|
|
818
|
+
});
|
|
819
|
+
|
|
779
820
|
// -------------------------------------------------------------------------
|
|
780
821
|
// Provider row: label + value + [Switch] button
|
|
781
822
|
|
|
782
823
|
const providerLabel = blessed.text({
|
|
783
824
|
parent: box,
|
|
784
|
-
top:
|
|
825
|
+
top: 5,
|
|
785
826
|
left: 6,
|
|
786
827
|
content: 'Provider:',
|
|
787
828
|
style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
|
|
@@ -789,7 +830,7 @@ export function createSettingsTab(screen, services) {
|
|
|
789
830
|
|
|
790
831
|
const providerValue = blessed.text({
|
|
791
832
|
parent: box,
|
|
792
|
-
top:
|
|
833
|
+
top: 5,
|
|
793
834
|
left: 22,
|
|
794
835
|
width: 26, // truncate before [Switch] at left:40
|
|
795
836
|
wrap: false,
|
|
@@ -805,7 +846,7 @@ export function createSettingsTab(screen, services) {
|
|
|
805
846
|
screen.render();
|
|
806
847
|
}, _restoreFocus);
|
|
807
848
|
});
|
|
808
|
-
switchBtn.top =
|
|
849
|
+
switchBtn.top = 5;
|
|
809
850
|
switchBtn.left = 52;
|
|
810
851
|
|
|
811
852
|
// -------------------------------------------------------------------------
|
|
@@ -813,7 +854,7 @@ export function createSettingsTab(screen, services) {
|
|
|
813
854
|
|
|
814
855
|
const voiceLabel = blessed.text({
|
|
815
856
|
parent: box,
|
|
816
|
-
top:
|
|
857
|
+
top: 7,
|
|
817
858
|
left: 6,
|
|
818
859
|
content: 'Current Voice:',
|
|
819
860
|
style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
|
|
@@ -821,7 +862,7 @@ export function createSettingsTab(screen, services) {
|
|
|
821
862
|
|
|
822
863
|
const voiceValue = blessed.text({
|
|
823
864
|
parent: box,
|
|
824
|
-
top:
|
|
865
|
+
top: 7,
|
|
825
866
|
left: 22,
|
|
826
867
|
width: 26, // truncate before [Change] at left:40
|
|
827
868
|
wrap: false,
|
|
@@ -837,7 +878,7 @@ export function createSettingsTab(screen, services) {
|
|
|
837
878
|
screen.render();
|
|
838
879
|
}, _restoreFocus);
|
|
839
880
|
}, { bg: COLORS.btnChange });
|
|
840
|
-
changeBtn.top =
|
|
881
|
+
changeBtn.top = 7;
|
|
841
882
|
changeBtn.left = 52;
|
|
842
883
|
|
|
843
884
|
const playBtn = _createButton(box, screen, '▶ Play', COLORS, () => {
|
|
@@ -872,7 +913,7 @@ export function createSettingsTab(screen, services) {
|
|
|
872
913
|
screen.render();
|
|
873
914
|
const _wavPlayer2 = detectWavPlayer(_sampleEnv);
|
|
874
915
|
if (!_wavPlayer2) { _stopSpinner(); _killSample(); playBtn.setContent('▶ Play'); screen.render(); return; }
|
|
875
|
-
const playProc = spawn(_wavPlayer2.bin, _wavPlayer2.args(tempWav),
|
|
916
|
+
const playProc = spawn(_wavPlayer2.bin, _wavPlayer2.args(tempWav), _spawnOpts(_sampleEnv));
|
|
876
917
|
_sampleProcess = playProc;
|
|
877
918
|
const _done = () => { _killSample(); playBtn.setContent('▶ Play'); _focusButton(playBtn); try { fs.unlinkSync(tempWav); } catch {} };
|
|
878
919
|
playProc.on('exit', _done);
|
|
@@ -973,7 +1014,7 @@ export function createSettingsTab(screen, services) {
|
|
|
973
1014
|
].join(' || ');
|
|
974
1015
|
|
|
975
1016
|
const soprano = spawn('sh', ['-c', cmd], {
|
|
976
|
-
stdio: 'ignore', detached: true, env: sopranoEnv,
|
|
1017
|
+
stdio: 'ignore', detached: !_IS_WINDOWS, windowsHide: true, env: sopranoEnv,
|
|
977
1018
|
});
|
|
978
1019
|
_sampleProcess = soprano;
|
|
979
1020
|
soprano.on('exit', (code) => {
|
|
@@ -995,8 +1036,8 @@ export function createSettingsTab(screen, services) {
|
|
|
995
1036
|
}
|
|
996
1037
|
const _piperArgs2 = ['--model', voicePath, '--output_file', tempWav];
|
|
997
1038
|
if (_ms2.speakerId != null) _piperArgs2.push('--speaker', String(_ms2.speakerId));
|
|
998
|
-
const piper = spawn(
|
|
999
|
-
stdio: ['pipe', 'ignore', 'ignore'], detached: true, env: _sampleEnv,
|
|
1039
|
+
const piper = spawn(_resolvePiperBin(), _piperArgs2, {
|
|
1040
|
+
stdio: ['pipe', 'ignore', 'ignore'], detached: !_IS_WINDOWS, windowsHide: true, env: _sampleEnv,
|
|
1000
1041
|
});
|
|
1001
1042
|
piper.stdin.write(phrase + '\n');
|
|
1002
1043
|
piper.stdin.end();
|
|
@@ -1005,12 +1046,12 @@ export function createSettingsTab(screen, services) {
|
|
|
1005
1046
|
piper.on('error', () => { _stopSpinner(); _killSample(); playBtn.setContent('▶ Play'); _focusButton(playBtn); });
|
|
1006
1047
|
}
|
|
1007
1048
|
});
|
|
1008
|
-
playBtn.top =
|
|
1049
|
+
playBtn.top = 7;
|
|
1009
1050
|
playBtn.left = 64;
|
|
1010
1051
|
|
|
1011
1052
|
const voiceFileText = blessed.text({
|
|
1012
1053
|
parent: box,
|
|
1013
|
-
top:
|
|
1054
|
+
top: 8,
|
|
1014
1055
|
left: 22,
|
|
1015
1056
|
right: 2,
|
|
1016
1057
|
wrap: false,
|
|
@@ -1025,7 +1066,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1025
1066
|
parent: box,
|
|
1026
1067
|
top: 3,
|
|
1027
1068
|
left: 1,
|
|
1028
|
-
content: '{
|
|
1069
|
+
content: '{bright-cyan-fg} ⚡ Audio Effects {/bright-cyan-fg}',
|
|
1029
1070
|
tags: true,
|
|
1030
1071
|
style: { bg: COLORS.contentBg },
|
|
1031
1072
|
});
|
|
@@ -1081,7 +1122,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1081
1122
|
parent: box,
|
|
1082
1123
|
top: 7,
|
|
1083
1124
|
left: 1,
|
|
1084
|
-
content: '{
|
|
1125
|
+
content: '{bright-cyan-fg} 🎸 Background Music {/bright-cyan-fg}',
|
|
1085
1126
|
tags: true,
|
|
1086
1127
|
style: { bg: COLORS.contentBg },
|
|
1087
1128
|
});
|
|
@@ -1177,12 +1218,24 @@ export function createSettingsTab(screen, services) {
|
|
|
1177
1218
|
volumeChangeBtn.top = 11;
|
|
1178
1219
|
volumeChangeBtn.left = 52;
|
|
1179
1220
|
|
|
1221
|
+
// -------------------------------------------------------------------------
|
|
1222
|
+
// Section header: ── Style ──
|
|
1223
|
+
|
|
1224
|
+
const styleHeader = blessed.text({
|
|
1225
|
+
parent: box,
|
|
1226
|
+
top: 3,
|
|
1227
|
+
left: 1,
|
|
1228
|
+
content: '{bright-cyan-fg} 🎭 Style {/bright-cyan-fg}',
|
|
1229
|
+
tags: true,
|
|
1230
|
+
style: { bg: COLORS.contentBg },
|
|
1231
|
+
});
|
|
1232
|
+
|
|
1180
1233
|
// -------------------------------------------------------------------------
|
|
1181
1234
|
// Verbosity row: label + value + [Change] button
|
|
1182
1235
|
|
|
1183
1236
|
const verbosityLabel = blessed.text({
|
|
1184
1237
|
parent: box,
|
|
1185
|
-
top:
|
|
1238
|
+
top: 5,
|
|
1186
1239
|
left: 6,
|
|
1187
1240
|
content: 'Verbosity:',
|
|
1188
1241
|
style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
|
|
@@ -1190,7 +1243,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1190
1243
|
|
|
1191
1244
|
const verbosityValue = blessed.text({
|
|
1192
1245
|
parent: box,
|
|
1193
|
-
top:
|
|
1246
|
+
top: 5,
|
|
1194
1247
|
left: 22,
|
|
1195
1248
|
width: 26, // truncate before [Change] at left:40
|
|
1196
1249
|
wrap: false,
|
|
@@ -1201,12 +1254,12 @@ export function createSettingsTab(screen, services) {
|
|
|
1201
1254
|
const verbosityChangeBtn = _createButton(box, screen, 'Change', COLORS, () => {
|
|
1202
1255
|
_openVerbosityPicker(screen, configService, () => refreshDisplay(), _restoreFocus);
|
|
1203
1256
|
}, { bg: COLORS.btnChange });
|
|
1204
|
-
verbosityChangeBtn.top =
|
|
1257
|
+
verbosityChangeBtn.top = 5;
|
|
1205
1258
|
verbosityChangeBtn.left = 52;
|
|
1206
1259
|
|
|
1207
1260
|
const verbosityPathText = blessed.text({
|
|
1208
1261
|
parent: box,
|
|
1209
|
-
top:
|
|
1262
|
+
top: 6,
|
|
1210
1263
|
left: 22,
|
|
1211
1264
|
right: 2,
|
|
1212
1265
|
wrap: false,
|
|
@@ -1219,7 +1272,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1219
1272
|
|
|
1220
1273
|
const personalityLabel = blessed.text({
|
|
1221
1274
|
parent: box,
|
|
1222
|
-
top:
|
|
1275
|
+
top: 7,
|
|
1223
1276
|
left: 6,
|
|
1224
1277
|
content: 'Personality:',
|
|
1225
1278
|
style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
|
|
@@ -1227,7 +1280,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1227
1280
|
|
|
1228
1281
|
const personalityValue = blessed.text({
|
|
1229
1282
|
parent: box,
|
|
1230
|
-
top:
|
|
1283
|
+
top: 7,
|
|
1231
1284
|
left: 22,
|
|
1232
1285
|
width: 26, // truncate before [Change] at left:40
|
|
1233
1286
|
wrap: false,
|
|
@@ -1241,7 +1294,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1241
1294
|
refreshDisplay();
|
|
1242
1295
|
}, _restoreFocus);
|
|
1243
1296
|
}, { bg: COLORS.btnChange });
|
|
1244
|
-
personalityChangeBtn.top =
|
|
1297
|
+
personalityChangeBtn.top = 7;
|
|
1245
1298
|
personalityChangeBtn.left = 52;
|
|
1246
1299
|
|
|
1247
1300
|
const personalityTestBtn = _createButton(box, screen, '▶ Preview', COLORS, () => {
|
|
@@ -1252,12 +1305,12 @@ export function createSettingsTab(screen, services) {
|
|
|
1252
1305
|
: _buildPreviewPhrase();
|
|
1253
1306
|
_runTest(false, phrase);
|
|
1254
1307
|
}, { bg: COLORS.btnTest });
|
|
1255
|
-
personalityTestBtn.top =
|
|
1308
|
+
personalityTestBtn.top = 7;
|
|
1256
1309
|
personalityTestBtn.left = 64;
|
|
1257
1310
|
|
|
1258
1311
|
const personalityFileText = blessed.text({
|
|
1259
1312
|
parent: box,
|
|
1260
|
-
top:
|
|
1313
|
+
top: 8,
|
|
1261
1314
|
left: 22,
|
|
1262
1315
|
right: 2,
|
|
1263
1316
|
wrap: false,
|
|
@@ -1271,9 +1324,9 @@ export function createSettingsTab(screen, services) {
|
|
|
1271
1324
|
|
|
1272
1325
|
const introTextHeader = blessed.text({
|
|
1273
1326
|
parent: box,
|
|
1274
|
-
top:
|
|
1327
|
+
top: 10,
|
|
1275
1328
|
left: 1,
|
|
1276
|
-
content: '{
|
|
1329
|
+
content: '{bright-cyan-fg} ✍️ Intro Text {/bright-cyan-fg}',
|
|
1277
1330
|
tags: true,
|
|
1278
1331
|
style: { bg: COLORS.contentBg },
|
|
1279
1332
|
});
|
|
@@ -1283,7 +1336,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1283
1336
|
|
|
1284
1337
|
const introTextLabel = blessed.text({
|
|
1285
1338
|
parent: box,
|
|
1286
|
-
top:
|
|
1339
|
+
top: 12,
|
|
1287
1340
|
left: 6,
|
|
1288
1341
|
content: 'Intro Text:',
|
|
1289
1342
|
style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
|
|
@@ -1291,7 +1344,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1291
1344
|
|
|
1292
1345
|
const introTextValue = blessed.text({
|
|
1293
1346
|
parent: box,
|
|
1294
|
-
top:
|
|
1347
|
+
top: 12,
|
|
1295
1348
|
left: 22,
|
|
1296
1349
|
width: 26, // truncate before [Edit] at left:40
|
|
1297
1350
|
wrap: false,
|
|
@@ -1302,19 +1355,19 @@ export function createSettingsTab(screen, services) {
|
|
|
1302
1355
|
const introEditBtn = _createButton(box, screen, 'Edit', COLORS, () => {
|
|
1303
1356
|
_openIntroTextEditor(screen, configService, () => { refreshDisplay(); }, _restoreFocus);
|
|
1304
1357
|
}, { bg: COLORS.btnEdit });
|
|
1305
|
-
introEditBtn.top =
|
|
1358
|
+
introEditBtn.top = 12;
|
|
1306
1359
|
introEditBtn.left = 52;
|
|
1307
1360
|
|
|
1308
1361
|
const introClearBtn = _createButton(box, screen, 'Clear', COLORS, () => {
|
|
1309
1362
|
configService.set('pretext', '');
|
|
1310
1363
|
refreshDisplay();
|
|
1311
1364
|
}, { bg: '#c62828' });
|
|
1312
|
-
introClearBtn.top =
|
|
1365
|
+
introClearBtn.top = 12;
|
|
1313
1366
|
introClearBtn.left = 64;
|
|
1314
1367
|
|
|
1315
1368
|
const introPathText = blessed.text({
|
|
1316
1369
|
parent: box,
|
|
1317
|
-
top:
|
|
1370
|
+
top: 13,
|
|
1318
1371
|
left: 22,
|
|
1319
1372
|
right: 2,
|
|
1320
1373
|
wrap: false,
|
|
@@ -1334,7 +1387,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1334
1387
|
parent: box,
|
|
1335
1388
|
top: 3,
|
|
1336
1389
|
left: 2,
|
|
1337
|
-
content: '{
|
|
1390
|
+
content: '{bright-cyan-fg} 📡 Audio Destination {/bright-cyan-fg}',
|
|
1338
1391
|
tags: true,
|
|
1339
1392
|
style: { bg: COLORS.contentBg },
|
|
1340
1393
|
});
|
|
@@ -1463,7 +1516,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1463
1516
|
const current = configService.getConfig().audio_stream_mode ?? 'text';
|
|
1464
1517
|
configService.set('audio_stream_mode', current === 'text' ? 'pulse' : 'text');
|
|
1465
1518
|
refreshDisplay();
|
|
1466
|
-
}, { bg: '#
|
|
1519
|
+
}, { bg: '#1565c0' }); // blue — distinct from green focus
|
|
1467
1520
|
audioStreamModeBtn.top = 7;
|
|
1468
1521
|
audioStreamModeBtn.left = 64;
|
|
1469
1522
|
audioStreamModeBtn.hide();
|
|
@@ -1487,7 +1540,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1487
1540
|
parent: box,
|
|
1488
1541
|
top: 11,
|
|
1489
1542
|
left: 2,
|
|
1490
|
-
content: '{
|
|
1543
|
+
content: '{bright-cyan-fg} 💾 Config Storage {/bright-cyan-fg}',
|
|
1491
1544
|
tags: true,
|
|
1492
1545
|
style: { bg: COLORS.contentBg },
|
|
1493
1546
|
});
|
|
@@ -1553,7 +1606,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1553
1606
|
refreshConfigDisplay();
|
|
1554
1607
|
_showNotice(screen, 'Settings Saved');
|
|
1555
1608
|
}, () => { _currentIdx = _buttons.indexOf(saveLocallyBtn); _focusButton(saveLocallyBtn); });
|
|
1556
|
-
}, { bg: '#
|
|
1609
|
+
}, { bg: '#1565c0' }); // blue — distinct from green focus
|
|
1557
1610
|
saveLocallyBtn.bottom = 0;
|
|
1558
1611
|
saveLocallyBtn.left = 46;
|
|
1559
1612
|
|
|
@@ -1581,6 +1634,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1581
1634
|
// Widget groups for each sub-tab (used by _showSubTab to show/hide)
|
|
1582
1635
|
const _subTabWidgets = {
|
|
1583
1636
|
voice: [
|
|
1637
|
+
providerVoiceHeader,
|
|
1584
1638
|
providerLabel, providerValue, switchBtn,
|
|
1585
1639
|
voiceLabel, voiceValue, changeBtn, playBtn, voiceFileText,
|
|
1586
1640
|
],
|
|
@@ -1592,6 +1646,7 @@ export function createSettingsTab(screen, services) {
|
|
|
1592
1646
|
volumeLabel, volumeValue, volumeChangeBtn,
|
|
1593
1647
|
],
|
|
1594
1648
|
personality: [
|
|
1649
|
+
styleHeader,
|
|
1595
1650
|
verbosityLabel, verbosityValue, verbosityChangeBtn, verbosityPathText,
|
|
1596
1651
|
personalityLabel, personalityValue, personalityChangeBtn, personalityTestBtn, personalityFileText,
|
|
1597
1652
|
introTextHeader,
|
|
@@ -2052,7 +2107,7 @@ export function createSettingsTab(screen, services) {
|
|
|
2052
2107
|
audioSshValue.setContent(audioAlias || '(none)');
|
|
2053
2108
|
const streamMode = cfg.audio_stream_mode ?? 'text';
|
|
2054
2109
|
audioStreamModeBtn.setContent(streamMode === 'pulse' ? 'Streaming Pulse Audio' : 'Streaming Text Only ✓');
|
|
2055
|
-
audioStreamModeBtn.style.bg = streamMode === 'text' ? '#
|
|
2110
|
+
audioStreamModeBtn.style.bg = streamMode === 'text' ? '#1565c0' : COLORS.btnChange;
|
|
2056
2111
|
} else {
|
|
2057
2112
|
audioSshLabel.hide();
|
|
2058
2113
|
audioSshValue.hide();
|
|
@@ -2223,14 +2278,14 @@ function _createButton(parent, screen, label, COLORS, onClick, opts = {}) {
|
|
|
2223
2278
|
const _ALL_PROVIDERS = [
|
|
2224
2279
|
{ id: 'piper', name: 'Piper TTS', platforms: ['linux', 'darwin', 'win32'], desc: 'High-quality local neural TTS' },
|
|
2225
2280
|
{ id: 'soprano', name: 'Soprano', platforms: ['linux', 'darwin'], desc: 'Ultra-fast neural TTS (single voice)' },
|
|
2226
|
-
{ id: '
|
|
2281
|
+
{ id: 'sapi', name: 'Windows SAPI', platforms: ['win32'], desc: 'Windows built-in text-to-speech' },
|
|
2227
2282
|
{ id: 'macos', name: 'Mac Say', platforms: ['darwin'], desc: 'macOS built-in text-to-speech' },
|
|
2228
2283
|
];
|
|
2229
2284
|
|
|
2230
2285
|
const _INSTALL_CMDS = {
|
|
2231
2286
|
piper: ['pip install piper-tts', 'OR: pipx install piper-tts', '', 'Voices are downloaded separately:', 'Run: agentvibes install (then choose Piper)'],
|
|
2232
2287
|
soprano: ['pip install soprano-tts', 'OR: pipx install soprano-tts', '', 'Keep model loaded for fast synthesis:', 'soprano-webui'],
|
|
2233
|
-
|
|
2288
|
+
sapi: ['Built-in on Windows — no install required.', 'Only works in a native Windows shell,', 'not inside WSL. Use piper or soprano in WSL.'],
|
|
2234
2289
|
macos: ['Built-in on macOS — no install required.', 'The say command ships with every Mac.'],
|
|
2235
2290
|
};
|
|
2236
2291
|
|
|
@@ -2275,7 +2330,7 @@ function _openProviderPicker(screen, providerService, onSelect, onClose) {
|
|
|
2275
2330
|
// Environment header
|
|
2276
2331
|
blessed.text({
|
|
2277
2332
|
parent: modal, top: 0, left: 1, tags: true,
|
|
2278
|
-
content: `{
|
|
2333
|
+
content: `{bright-cyan-fg}🖥 Environment:{/bright-cyan-fg} {bold}${envLabel}{/bold}`,
|
|
2279
2334
|
style: { bg: COLORS.contentBg },
|
|
2280
2335
|
});
|
|
2281
2336
|
blessed.text({
|
|
@@ -2325,8 +2380,8 @@ function _openProviderPicker(screen, providerService, onSelect, onClose) {
|
|
|
2325
2380
|
_close(); onSelect(prov.id);
|
|
2326
2381
|
} else {
|
|
2327
2382
|
const lines = _INSTALL_CMDS[prov.id] ?? ['No instructions available.'];
|
|
2328
|
-
instrTitle.setContent(`{
|
|
2329
|
-
instrContent.setContent(lines.map(l => l ? `{
|
|
2383
|
+
instrTitle.setContent(`{bright-cyan-fg}Install — ${prov.name}:{/bright-cyan-fg}`);
|
|
2384
|
+
instrContent.setContent(lines.map(l => l ? `{bright-cyan-fg}${l}{/bright-cyan-fg}` : '').join('\n'));
|
|
2330
2385
|
screen.render();
|
|
2331
2386
|
}
|
|
2332
2387
|
});
|
|
@@ -2340,7 +2395,7 @@ function _openProviderPicker(screen, providerService, onSelect, onClose) {
|
|
|
2340
2395
|
|
|
2341
2396
|
const instrTitle = blessed.text({
|
|
2342
2397
|
parent: modal, top: 11, left: 1, width: 66, tags: true,
|
|
2343
|
-
content: '{
|
|
2398
|
+
content: '{bright-cyan-fg}Install instructions — click Install beside a provider:{/bright-cyan-fg}',
|
|
2344
2399
|
style: { bg: COLORS.contentBg },
|
|
2345
2400
|
});
|
|
2346
2401
|
const instrContent = blessed.text({
|
|
@@ -2452,7 +2507,7 @@ function _showSavePreview(screen, filePath, data, onConfirm, onClose) {
|
|
|
2452
2507
|
style: {
|
|
2453
2508
|
fg: '#e3f2fd',
|
|
2454
2509
|
bg: COLORS.contentBg,
|
|
2455
|
-
border: { fg: '
|
|
2510
|
+
border: { fg: 'bright-cyan' },
|
|
2456
2511
|
},
|
|
2457
2512
|
});
|
|
2458
2513
|
|
|
@@ -2471,7 +2526,7 @@ function _showSavePreview(screen, filePath, data, onConfirm, onClose) {
|
|
|
2471
2526
|
const okBtn = _createButton(modal, screen, 'OK — Save', COLORS, () => {
|
|
2472
2527
|
_close();
|
|
2473
2528
|
onConfirm();
|
|
2474
|
-
}, { bg: '#
|
|
2529
|
+
}, { bg: '#1565c0' });
|
|
2475
2530
|
okBtn.top = btnRow;
|
|
2476
2531
|
okBtn.left = midX + 2;
|
|
2477
2532
|
|
|
@@ -2540,7 +2595,7 @@ function _openTrackPicker(screen, configService, onSelect, onClose) {
|
|
|
2540
2595
|
const currentTrack = (configService.getConfig().backgroundMusic?.track ?? MUSIC_DEFAULTS.track);
|
|
2541
2596
|
const items = allItems.map(t =>
|
|
2542
2597
|
t.file === ADD_SENTINEL
|
|
2543
|
-
? ` {
|
|
2598
|
+
? ` {bright-cyan-fg}+ Add Custom Track{/bright-cyan-fg}`
|
|
2544
2599
|
: (t.file === currentTrack ? `● ${t.label}` : ` ${t.label}`)
|
|
2545
2600
|
);
|
|
2546
2601
|
const currentIdx = tracks.findIndex(t => t.file === currentTrack);
|
|
@@ -2779,9 +2834,14 @@ function _openVolumePicker(screen, configService, onSelect, onClose) {
|
|
|
2779
2834
|
`mpg123 -q "${trackPath}"`,
|
|
2780
2835
|
].join(' 2>/dev/null || ') + ' 2>/dev/null';
|
|
2781
2836
|
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2837
|
+
if (_IS_WINDOWS) {
|
|
2838
|
+
const _mp3P3 = detectMp3Player(_previewEnv);
|
|
2839
|
+
_previewProcess = _mp3P3
|
|
2840
|
+
? spawn(_mp3P3.bin, _mp3P3.args(trackPath), _spawnOpts(_previewEnv))
|
|
2841
|
+
: null;
|
|
2842
|
+
} else {
|
|
2843
|
+
_previewProcess = spawn('sh', ['-c', cmd], _spawnOpts(_previewEnv));
|
|
2844
|
+
}
|
|
2785
2845
|
_previewProcess.unref();
|
|
2786
2846
|
_refreshList();
|
|
2787
2847
|
|
|
@@ -2917,7 +2977,7 @@ function _openMusicBrowserModal(screen, configService, navigationService, onDone
|
|
|
2917
2977
|
fg: COLORS.labelFg,
|
|
2918
2978
|
bg: COLORS.contentBg,
|
|
2919
2979
|
border: { fg: COLORS.borderFg },
|
|
2920
|
-
selected: { bg: '#
|
|
2980
|
+
selected: { bg: '#2e7d32', fg: '#ffffff', bold: true },
|
|
2921
2981
|
item: { fg: COLORS.labelFg },
|
|
2922
2982
|
},
|
|
2923
2983
|
});
|
|
@@ -2930,7 +2990,7 @@ function _openMusicBrowserModal(screen, configService, navigationService, onDone
|
|
|
2930
2990
|
right: 2,
|
|
2931
2991
|
tags: true,
|
|
2932
2992
|
content: '',
|
|
2933
|
-
style: { fg: '
|
|
2993
|
+
style: { fg: 'bright-cyan', bg: COLORS.contentBg },
|
|
2934
2994
|
});
|
|
2935
2995
|
|
|
2936
2996
|
// ---- File location hint ----
|
|
@@ -3024,15 +3084,16 @@ function _openMusicBrowserModal(screen, configService, navigationService, onDone
|
|
|
3024
3084
|
|
|
3025
3085
|
const _mp3Player = detectMp3Player(_modalEnv);
|
|
3026
3086
|
if (!_mp3Player) return;
|
|
3087
|
+
const _isWin = process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
|
|
3027
3088
|
_previewProcess = spawn(_mp3Player.bin, _mp3Player.args(trackPath), {
|
|
3028
|
-
stdio: 'ignore', detached: true, env: _modalEnv,
|
|
3089
|
+
stdio: 'ignore', detached: !_isWin, windowsHide: true, env: _modalEnv,
|
|
3029
3090
|
});
|
|
3030
3091
|
_previewProcess.unref();
|
|
3031
3092
|
_previewTrackId = trackId;
|
|
3032
3093
|
|
|
3033
3094
|
const label = _allTracks.find(t => t.id === trackId)?.label ?? formatTrackLabel(trackId);
|
|
3034
3095
|
if (!_closed) {
|
|
3035
|
-
modalPreviewLine.setContent(`{
|
|
3096
|
+
modalPreviewLine.setContent(`{bright-cyan-fg}\u266A Previewing: ${label} (Space to stop){/bright-cyan-fg}`);
|
|
3036
3097
|
screen.render();
|
|
3037
3098
|
}
|
|
3038
3099
|
|
|
@@ -3327,7 +3388,7 @@ function _openVoiceBrowserModal(screen, providerService, configService, navigati
|
|
|
3327
3388
|
keys: true,
|
|
3328
3389
|
style: {
|
|
3329
3390
|
fg: COLORS.valueFg,
|
|
3330
|
-
bg: '#
|
|
3391
|
+
bg: '#1a3a5c',
|
|
3331
3392
|
focus: { bg: '#283593' },
|
|
3332
3393
|
},
|
|
3333
3394
|
});
|
|
@@ -3337,7 +3398,7 @@ function _openVoiceBrowserModal(screen, providerService, configService, navigati
|
|
|
3337
3398
|
parent: modal,
|
|
3338
3399
|
top: 2,
|
|
3339
3400
|
left: 6,
|
|
3340
|
-
content: `{
|
|
3401
|
+
content: `{bright-cyan-fg}${'Name'.padEnd(COL_NAME_W)}${'Gender'.padEnd(COL_GENDER_W)}Provider{/bright-cyan-fg}`,
|
|
3341
3402
|
tags: true,
|
|
3342
3403
|
style: { bg: COLORS.contentBg },
|
|
3343
3404
|
});
|
|
@@ -3358,7 +3419,7 @@ function _openVoiceBrowserModal(screen, providerService, configService, navigati
|
|
|
3358
3419
|
fg: COLORS.labelFg,
|
|
3359
3420
|
bg: COLORS.contentBg,
|
|
3360
3421
|
border: { fg: COLORS.borderFg },
|
|
3361
|
-
selected: { bg: '#
|
|
3422
|
+
selected: { bg: '#2e7d32', fg: '#ffffff', bold: true },
|
|
3362
3423
|
item: { fg: COLORS.labelFg },
|
|
3363
3424
|
},
|
|
3364
3425
|
});
|
|
@@ -3368,7 +3429,7 @@ function _openVoiceBrowserModal(screen, providerService, configService, navigati
|
|
|
3368
3429
|
parent: modal,
|
|
3369
3430
|
bottom: 5,
|
|
3370
3431
|
left: 2,
|
|
3371
|
-
content: `{
|
|
3432
|
+
content: `{bright-cyan-fg}── Voice Info ${'─'.repeat(50)}{/bright-cyan-fg}`,
|
|
3372
3433
|
tags: true,
|
|
3373
3434
|
style: { bg: COLORS.contentBg },
|
|
3374
3435
|
});
|
|
@@ -3390,7 +3451,7 @@ function _openVoiceBrowserModal(screen, providerService, configService, navigati
|
|
|
3390
3451
|
right: 2,
|
|
3391
3452
|
tags: true,
|
|
3392
3453
|
content: '',
|
|
3393
|
-
style: { fg: '
|
|
3454
|
+
style: { fg: 'bright-cyan', bg: COLORS.contentBg },
|
|
3394
3455
|
});
|
|
3395
3456
|
|
|
3396
3457
|
// ---- Key hint bar ----
|
|
@@ -3495,11 +3556,22 @@ function _openVoiceBrowserModal(screen, providerService, configService, navigati
|
|
|
3495
3556
|
const tempWav = path.join(os.tmpdir(), `agentvibes-preview-${Date.now()}.wav`);
|
|
3496
3557
|
const phrase = SAMPLE_PHRASES[Math.floor(Math.random() * SAMPLE_PHRASES.length)];
|
|
3497
3558
|
|
|
3559
|
+
const _isWinPreview = process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
|
|
3560
|
+
let _piperBin3 = 'piper';
|
|
3561
|
+
if (_isWinPreview) {
|
|
3562
|
+
const _lad = process.env.LOCALAPPDATA ||
|
|
3563
|
+
(process.env.USERPROFILE ? path.join(process.env.USERPROFILE, 'AppData', 'Local') : null);
|
|
3564
|
+
if (_lad) {
|
|
3565
|
+
const _exe = path.join(_lad, 'Programs', 'Piper', 'piper.exe');
|
|
3566
|
+
if (fs.existsSync(_exe)) _piperBin3 = _exe;
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3498
3569
|
const _piperArgs3 = ['--model', voicePath, '--output_file', tempWav];
|
|
3499
3570
|
if (_ms3.speakerId != null) _piperArgs3.push('--speaker', String(_ms3.speakerId));
|
|
3500
|
-
const piper = spawn(
|
|
3571
|
+
const piper = spawn(_piperBin3, _piperArgs3, {
|
|
3501
3572
|
stdio: ['pipe', 'ignore', 'ignore'],
|
|
3502
|
-
detached:
|
|
3573
|
+
detached: !_isWinPreview,
|
|
3574
|
+
windowsHide: true,
|
|
3503
3575
|
env: _spawnEnv,
|
|
3504
3576
|
});
|
|
3505
3577
|
piper.stdin.write(phrase + '\n');
|
|
@@ -3508,7 +3580,7 @@ function _openVoiceBrowserModal(screen, providerService, configService, navigati
|
|
|
3508
3580
|
_playingProcess = piper;
|
|
3509
3581
|
_playingVoiceId = voiceId;
|
|
3510
3582
|
if (!_closed) {
|
|
3511
|
-
modalPreviewLine.setContent(`{
|
|
3583
|
+
modalPreviewLine.setContent(`{bright-cyan-fg}♪ Synthesizing: ${voiceId}…{/bright-cyan-fg}`);
|
|
3512
3584
|
screen.render();
|
|
3513
3585
|
}
|
|
3514
3586
|
|
|
@@ -3521,7 +3593,7 @@ function _openVoiceBrowserModal(screen, providerService, configService, navigati
|
|
|
3521
3593
|
_playingVoiceId = null;
|
|
3522
3594
|
_playingProcess = null;
|
|
3523
3595
|
if (!_closed) {
|
|
3524
|
-
modalPreviewLine.setContent('{
|
|
3596
|
+
modalPreviewLine.setContent('{bright-cyan-fg}♪ Preview failed (piper error — is piper installed?){/bright-cyan-fg}');
|
|
3525
3597
|
screen.render();
|
|
3526
3598
|
setTimeout(() => { if (!_closed) { modalPreviewLine.setContent(''); screen.render(); } }, 4000);
|
|
3527
3599
|
}
|
|
@@ -3532,13 +3604,14 @@ function _openVoiceBrowserModal(screen, providerService, configService, navigati
|
|
|
3532
3604
|
if (!_wavPlayer3) return;
|
|
3533
3605
|
const playProc = spawn(_wavPlayer3.bin, _wavPlayer3.args(tempWav), {
|
|
3534
3606
|
stdio: 'ignore',
|
|
3535
|
-
detached:
|
|
3607
|
+
detached: !_isWinPreview,
|
|
3608
|
+
windowsHide: true,
|
|
3536
3609
|
env: _spawnEnv,
|
|
3537
3610
|
});
|
|
3538
3611
|
_playingProcess = playProc;
|
|
3539
3612
|
|
|
3540
3613
|
if (!_closed) {
|
|
3541
|
-
modalPreviewLine.setContent(`{
|
|
3614
|
+
modalPreviewLine.setContent(`{bright-cyan-fg}♪ Playing: ${voiceId} (Space to stop){/bright-cyan-fg}`);
|
|
3542
3615
|
screen.render();
|
|
3543
3616
|
}
|
|
3544
3617
|
|
|
@@ -3563,7 +3636,7 @@ function _openVoiceBrowserModal(screen, providerService, configService, navigati
|
|
|
3563
3636
|
_playingVoiceId = null;
|
|
3564
3637
|
_playingProcess = null;
|
|
3565
3638
|
if (!_closed) {
|
|
3566
|
-
modalPreviewLine.setContent('{
|
|
3639
|
+
modalPreviewLine.setContent('{bright-cyan-fg}♪ Cannot find piper — install with: pipx install piper-tts{/bright-cyan-fg}');
|
|
3567
3640
|
screen.render();
|
|
3568
3641
|
setTimeout(() => { if (!_closed) { modalPreviewLine.setContent(''); screen.render(); } }, 4000);
|
|
3569
3642
|
}
|