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.
Files changed (219) hide show
  1. package/.agentvibes/bmad/bmad-voices.md +69 -69
  2. package/.agentvibes/config.json +12 -0
  3. package/.claude/activation-instructions +54 -54
  4. package/.claude/audio/tracks/README.md +52 -52
  5. package/.claude/commands/agent-vibes/add.md +21 -21
  6. package/.claude/commands/agent-vibes/agent-vibes.md +101 -101
  7. package/.claude/commands/agent-vibes/agent.md +79 -79
  8. package/.claude/commands/agent-vibes/background-music.md +111 -111
  9. package/.claude/commands/agent-vibes/bmad.md +198 -198
  10. package/.claude/commands/agent-vibes/clean.md +18 -18
  11. package/.claude/commands/agent-vibes/cleanup.md +18 -18
  12. package/.claude/commands/agent-vibes/commands.json +145 -145
  13. package/.claude/commands/agent-vibes/effects.md +97 -97
  14. package/.claude/commands/agent-vibes/get.md +9 -9
  15. package/.claude/commands/agent-vibes/hide.md +91 -91
  16. package/.claude/commands/agent-vibes/language.md +23 -23
  17. package/.claude/commands/agent-vibes/learn.md +67 -67
  18. package/.claude/commands/agent-vibes/list.md +13 -13
  19. package/.claude/commands/agent-vibes/mute.md +37 -37
  20. package/.claude/commands/agent-vibes/preview.md +17 -17
  21. package/.claude/commands/agent-vibes/provider.md +68 -68
  22. package/.claude/commands/agent-vibes/replay-target.md +14 -14
  23. package/.claude/commands/agent-vibes/sample.md +12 -12
  24. package/.claude/commands/agent-vibes/set-favorite-voice.md +84 -84
  25. package/.claude/commands/agent-vibes/set-pretext.md +65 -65
  26. package/.claude/commands/agent-vibes/set-speed.md +41 -41
  27. package/.claude/commands/agent-vibes/show.md +84 -84
  28. package/.claude/commands/agent-vibes/switch.md +87 -87
  29. package/.claude/commands/agent-vibes/target-voice.md +26 -26
  30. package/.claude/commands/agent-vibes/target.md +30 -30
  31. package/.claude/commands/agent-vibes/translate.md +68 -68
  32. package/.claude/commands/agent-vibes/unmute.md +45 -45
  33. package/.claude/commands/agent-vibes/verbosity.md +89 -89
  34. package/.claude/commands/agent-vibes/whoami.md +7 -7
  35. package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
  36. package/.claude/commands/agent-vibes-rdp.md +24 -24
  37. package/.claude/config/agentvibes.json +1 -0
  38. package/.claude/config/audio-effects.cfg +2 -2
  39. package/.claude/config/audio-effects.cfg.sample +52 -52
  40. package/.claude/config/background-music-volume.txt +1 -0
  41. package/.claude/config/intro-text.txt +1 -0
  42. package/.claude/config/piper-speech-rate.txt +4 -0
  43. package/.claude/config/piper-target-speech-rate.txt +1 -0
  44. package/.claude/config/reverb-level.txt +1 -0
  45. package/.claude/config/tts-speech-rate.txt +4 -0
  46. package/.claude/config/tts-target-speech-rate.txt +1 -0
  47. package/.claude/docs/TERMUX_SETUP.md +408 -408
  48. package/.claude/github-star-reminder.txt +1 -1
  49. package/.claude/hooks/README-TTS-QUEUE.md +135 -135
  50. package/.claude/hooks/audio-cache-utils.sh +246 -246
  51. package/.claude/hooks/audio-processor.sh +433 -433
  52. package/.claude/hooks/background-music-manager.sh +404 -404
  53. package/.claude/hooks/bmad-speak-enhanced.sh +165 -165
  54. package/.claude/hooks/bmad-speak.sh +269 -269
  55. package/.claude/hooks/bmad-tts-injector.sh +568 -568
  56. package/.claude/hooks/bmad-voice-manager.sh +928 -928
  57. package/.claude/hooks/clawdbot-receiver-SECURE.sh +129 -129
  58. package/.claude/hooks/clawdbot-receiver.sh +107 -107
  59. package/.claude/hooks/clean-audio-cache.sh +22 -22
  60. package/.claude/hooks/cleanup-cache.sh +106 -106
  61. package/.claude/hooks/configure-rdp-mode.sh +137 -137
  62. package/.claude/hooks/download-extra-voices.sh +244 -244
  63. package/.claude/hooks/effects-manager.sh +268 -268
  64. package/.claude/hooks/github-star-reminder.sh +154 -154
  65. package/.claude/hooks/language-manager.sh +362 -362
  66. package/.claude/hooks/learn-manager.sh +492 -492
  67. package/.claude/hooks/macos-voice-manager.sh +205 -205
  68. package/.claude/hooks/migrate-background-music.sh +125 -125
  69. package/.claude/hooks/migrate-to-agentvibes.sh +161 -161
  70. package/.claude/hooks/optimize-background-music.sh +87 -87
  71. package/.claude/hooks/path-resolver.sh +60 -60
  72. package/.claude/hooks/personality-manager.sh +448 -448
  73. package/.claude/hooks/piper-download-voices.sh +225 -225
  74. package/.claude/hooks/piper-installer.sh +292 -292
  75. package/.claude/hooks/piper-multispeaker-registry.sh +171 -171
  76. package/.claude/hooks/piper-voice-manager.sh +24 -3
  77. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +90 -90
  78. package/.claude/hooks/play-tts-enhanced.sh +105 -105
  79. package/.claude/hooks/play-tts-macos.sh +368 -368
  80. package/.claude/hooks/play-tts-piper.sh +679 -679
  81. package/.claude/hooks/play-tts-soprano.sh +356 -356
  82. package/.claude/hooks/play-tts-ssh-remote.sh +167 -167
  83. package/.claude/hooks/play-tts-termux-ssh.sh +169 -169
  84. package/.claude/hooks/play-tts.sh +301 -301
  85. package/.claude/hooks/prepare-release.sh +54 -54
  86. package/.claude/hooks/provider-commands.sh +617 -617
  87. package/.claude/hooks/provider-manager.sh +399 -399
  88. package/.claude/hooks/replay-target-audio.sh +95 -95
  89. package/.claude/hooks/requirements.txt +6 -6
  90. package/.claude/hooks/sentiment-manager.sh +201 -201
  91. package/.claude/hooks/session-start-tts.sh +81 -81
  92. package/.claude/hooks/soprano-gradio-synth.py +139 -139
  93. package/.claude/hooks/speed-manager.sh +291 -291
  94. package/.claude/hooks/stop-tts.sh +84 -84
  95. package/.claude/hooks/termux-installer.sh +261 -261
  96. package/.claude/hooks/translate-manager.sh +341 -341
  97. package/.claude/hooks/translator.py +237 -237
  98. package/.claude/hooks/tts-queue-worker.sh +145 -145
  99. package/.claude/hooks/tts-queue.sh +165 -165
  100. package/.claude/hooks/verbosity-manager.sh +178 -178
  101. package/.claude/hooks/voice-manager.sh +548 -548
  102. package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
  103. package/.claude/hooks-windows/background-music-manager.ps1 +348 -0
  104. package/.claude/hooks-windows/clean-audio-cache.ps1 +53 -0
  105. package/.claude/hooks-windows/download-extra-voices.ps1 +185 -0
  106. package/.claude/hooks-windows/effects-manager.ps1 +294 -0
  107. package/.claude/hooks-windows/language-manager.ps1 +193 -0
  108. package/.claude/hooks-windows/learn-manager.ps1 +241 -0
  109. package/.claude/hooks-windows/personality-manager.ps1 +266 -0
  110. package/.claude/hooks-windows/play-tts-piper.ps1 +209 -0
  111. package/.claude/hooks-windows/play-tts-sapi.ps1 +108 -0
  112. package/.claude/hooks-windows/play-tts-soprano.ps1 +159 -158
  113. package/.claude/hooks-windows/play-tts-windows-piper.ps1 +50 -5
  114. package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +108 -108
  115. package/.claude/hooks-windows/play-tts.ps1 +344 -266
  116. package/.claude/hooks-windows/provider-manager.ps1 +29 -10
  117. package/.claude/hooks-windows/session-start-tts.ps1 +124 -124
  118. package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
  119. package/.claude/hooks-windows/speed-manager.ps1 +166 -0
  120. package/.claude/hooks-windows/verbosity-manager.ps1 +119 -0
  121. package/.claude/hooks-windows/voice-manager-windows.ps1 +92 -8
  122. package/.claude/output-styles/agent-vibes.md +202 -202
  123. package/.claude/personalities/angry.md +14 -14
  124. package/.claude/personalities/annoying.md +14 -14
  125. package/.claude/personalities/crass.md +14 -14
  126. package/.claude/personalities/dramatic.md +14 -14
  127. package/.claude/personalities/dry-humor.md +50 -50
  128. package/.claude/personalities/flirty.md +20 -20
  129. package/.claude/personalities/funny.md +14 -14
  130. package/.claude/personalities/grandpa.md +32 -32
  131. package/.claude/personalities/millennial.md +14 -14
  132. package/.claude/personalities/moody.md +14 -14
  133. package/.claude/personalities/normal.md +16 -16
  134. package/.claude/personalities/pirate.md +14 -14
  135. package/.claude/personalities/poetic.md +14 -14
  136. package/.claude/personalities/professional.md +14 -14
  137. package/.claude/personalities/rapper.md +55 -55
  138. package/.claude/personalities/robot.md +14 -14
  139. package/.claude/personalities/sarcastic.md +38 -38
  140. package/.claude/personalities/sassy.md +14 -14
  141. package/.claude/personalities/surfer-dude.md +14 -14
  142. package/.claude/personalities/zen.md +14 -14
  143. package/.claude/settings.json +15 -15
  144. package/.claude/verbosity.txt +1 -1
  145. package/.clawdbot/README.md +105 -105
  146. package/.clawdbot/skill/SKILL.md +241 -241
  147. package/.mcp.json +12 -0
  148. package/CLAUDE.md +170 -170
  149. package/README.md +2029 -2007
  150. package/RELEASE_NOTES.md +1310 -1203
  151. package/WINDOWS-SETUP.md +208 -208
  152. package/bin/agent-vibes +39 -39
  153. package/bin/agentvibes-voice-browser.js +1840 -1840
  154. package/bin/agentvibes.js +48 -2
  155. package/bin/mcp-server.js +121 -121
  156. package/bin/mcp-server.sh +206 -206
  157. package/bin/test-bmad-pr +78 -78
  158. package/mcp-server/QUICK_START.md +203 -203
  159. package/mcp-server/README.md +345 -345
  160. package/mcp-server/WINDOWS_SETUP.md +260 -260
  161. package/mcp-server/docs/troubleshooting-audio.md +313 -313
  162. package/mcp-server/examples/claude_desktop_config.json +11 -11
  163. package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
  164. package/mcp-server/examples/custom_instructions.md +169 -169
  165. package/mcp-server/install-deps.js +130 -130
  166. package/mcp-server/pyproject.toml +52 -52
  167. package/mcp-server/requirements.txt +2 -2
  168. package/mcp-server/server.py +1465 -1453
  169. package/mcp-server/test_server.py +395 -395
  170. package/mcp-server/test_windows_script_parity.py +336 -0
  171. package/package.json +110 -110
  172. package/setup-windows.ps1 +815 -815
  173. package/src/bmad-detector.js +71 -71
  174. package/src/cli/list-personalities.js +110 -110
  175. package/src/cli/list-voices.js +114 -114
  176. package/src/commands/bmad-voices.js +394 -394
  177. package/src/commands/install-mcp.js +476 -476
  178. package/src/console/app.js +824 -824
  179. package/src/console/audio-env.js +20 -1
  180. package/src/console/brand-colors.js +13 -13
  181. package/src/console/constants/personalities.js +44 -44
  182. package/src/console/footer-config.js +50 -50
  183. package/src/console/modals/modal-overlay.js +247 -247
  184. package/src/console/navigation.js +62 -62
  185. package/src/console/tabs/agents-tab.js +1684 -1516
  186. package/src/console/tabs/help-tab.js +261 -261
  187. package/src/console/tabs/install-tab.js +1007 -991
  188. package/src/console/tabs/music-tab.js +22 -8
  189. package/src/console/tabs/placeholder-tab.js +53 -53
  190. package/src/console/tabs/readme-tab.js +267 -267
  191. package/src/console/tabs/receiver-tab.js +1472 -1212
  192. package/src/console/tabs/settings-tab.js +152 -79
  193. package/src/console/tabs/voices-tab.js +100 -21
  194. package/src/console/widgets/destroy-list.js +25 -25
  195. package/src/console/widgets/format-utils.js +89 -89
  196. package/src/console/widgets/notice.js +55 -55
  197. package/src/console/widgets/personality-picker.js +185 -185
  198. package/src/console/widgets/reverb-picker.js +94 -94
  199. package/src/console/widgets/track-picker.js +285 -285
  200. package/src/installer/music-file-input.js +304 -304
  201. package/src/installer.js +5882 -5829
  202. package/src/services/agent-voice-store.js +423 -423
  203. package/src/services/config-service.js +264 -264
  204. package/src/services/navigation-service.js +123 -123
  205. package/src/services/provider-service.js +132 -132
  206. package/src/services/verbosity-service.js +157 -157
  207. package/src/utils/audio-duration-validator.js +298 -298
  208. package/src/utils/audio-format-validator.js +277 -277
  209. package/src/utils/dependency-checker.js +469 -466
  210. package/src/utils/file-ownership-verifier.js +358 -358
  211. package/src/utils/list-formatter.js +194 -194
  212. package/src/utils/music-file-validator.js +285 -285
  213. package/src/utils/preview-list-prompt.js +136 -136
  214. package/src/utils/provider-validator.js +96 -12
  215. package/src/utils/secure-music-storage.js +412 -412
  216. package/templates/agentvibes-receiver.sh +482 -482
  217. package/templates/audio/welcome-music.mp3 +0 -0
  218. package/voice-assignments.json +8244 -8244
  219. 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: '#7986cb', // Light blue section dividers
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: '#00e5ff', // Cyan — focused button bg
59
- btnFocusFg: '#000000', // Black — focused button text
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: '#7986cb', // Light blue — borders
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
- _testMusicProc = spawn('sh', ['-c', musicCmd], {
417
- stdio: 'ignore', detached: true, env: _testEnv,
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('piper', _piperArgs, {
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), { stdio: 'ignore', detached: true, env: _testEnv })
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
- _musicTestProc = spawn('sh', ['-c', cmd], {
637
- stdio: 'ignore', detached: true, env: _testEnv,
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: '#00e5ff', bg: '#263238' },
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 = '#00e5ff';
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: 3,
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: 3,
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 = 3;
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: 5,
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: 5,
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 = 5;
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), { stdio: 'ignore', detached: true, env: _sampleEnv });
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('piper', _piperArgs2, {
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 = 5;
1049
+ playBtn.top = 7;
1009
1050
  playBtn.left = 64;
1010
1051
 
1011
1052
  const voiceFileText = blessed.text({
1012
1053
  parent: box,
1013
- top: 6,
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: '{#7986cb-fg} ⚡ Audio Effects {/#7986cb-fg}',
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: '{#7986cb-fg} 🎸 Background Music {/#7986cb-fg}',
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: 3,
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: 3,
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 = 3;
1257
+ verbosityChangeBtn.top = 5;
1205
1258
  verbosityChangeBtn.left = 52;
1206
1259
 
1207
1260
  const verbosityPathText = blessed.text({
1208
1261
  parent: box,
1209
- top: 4,
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: 5,
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: 5,
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 = 5;
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 = 5;
1308
+ personalityTestBtn.top = 7;
1256
1309
  personalityTestBtn.left = 64;
1257
1310
 
1258
1311
  const personalityFileText = blessed.text({
1259
1312
  parent: box,
1260
- top: 6,
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: 8,
1327
+ top: 10,
1275
1328
  left: 1,
1276
- content: '{#7986cb-fg} ✍️ Intro Text {/#7986cb-fg}',
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: 10,
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: 10,
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 = 10;
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 = 10;
1365
+ introClearBtn.top = 12;
1313
1366
  introClearBtn.left = 64;
1314
1367
 
1315
1368
  const introPathText = blessed.text({
1316
1369
  parent: box,
1317
- top: 11,
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: '{#7986cb-fg} 📡 Audio Destination {/#7986cb-fg}',
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: '#2e7d32' }); // green = recommended
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: '{#7986cb-fg} 💾 Config Storage {/#7986cb-fg}',
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: '#2e7d32' }); // green
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' ? '#2e7d32' : COLORS.btnChange;
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: 'windows-sapi', name: 'Windows SAPI', platforms: ['win32'], desc: 'Windows built-in text-to-speech' },
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
- 'windows-sapi': ['Built-in on Windows — no install required.', 'Only works in a native Windows shell,', 'not inside WSL. Use piper or soprano in WSL.'],
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: `{#00e5ff-fg}🖥 Environment:{/#00e5ff-fg} {bold}${envLabel}{/bold}`,
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(`{#7986cb-fg}Install — ${prov.name}:{/#7986cb-fg}`);
2329
- instrContent.setContent(lines.map(l => l ? `{#00e5ff-fg}${l}{/#00e5ff-fg}` : '').join('\n'));
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: '{#7986cb-fg}Install instructions — click Install beside a provider:{/#7986cb-fg}',
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: '#00e5ff' },
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: '#2e7d32' });
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
- ? ` {#00e5ff-fg}+ Add Custom Track{/#00e5ff-fg}`
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
- _previewProcess = spawn('sh', ['-c', cmd], {
2783
- stdio: 'ignore', detached: true, env: _previewEnv,
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: '#1a237e', fg: '#00e5ff', bold: true },
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: '#00e5ff', bg: COLORS.contentBg },
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(`{#00e5ff-fg}\u266A Previewing: ${label} (Space to stop){/#00e5ff-fg}`);
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: '#1a237e',
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: `{#7986cb-fg}${'Name'.padEnd(COL_NAME_W)}${'Gender'.padEnd(COL_GENDER_W)}Provider{/#7986cb-fg}`,
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: '#1a237e', fg: '#00e5ff', bold: true },
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: `{#7986cb-fg}── Voice Info ${'─'.repeat(50)}{/#7986cb-fg}`,
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: '#00e5ff', bg: COLORS.contentBg },
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('piper', _piperArgs3, {
3571
+ const piper = spawn(_piperBin3, _piperArgs3, {
3501
3572
  stdio: ['pipe', 'ignore', 'ignore'],
3502
- detached: true,
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(`{#00e5ff-fg}♪ Synthesizing: ${voiceId}…{/#00e5ff-fg}`);
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('{#00e5ff-fg}♪ Preview failed (piper error — is piper installed?){/#00e5ff-fg}');
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: true,
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(`{#00e5ff-fg}♪ Playing: ${voiceId} (Space to stop){/#00e5ff-fg}`);
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('{#00e5ff-fg}♪ Cannot find piper — install with: pipx install piper-tts{/#00e5ff-fg}');
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
  }