agentvibes 5.7.7 → 5.9.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 (138) hide show
  1. package/.agentvibes/config.json +12 -5
  2. package/.agentvibes/install-manifest.json +188 -300
  3. package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
  4. package/.claude/commands/agent-vibes-rdp.md +24 -24
  5. package/.claude/config/audio-effects.cfg +3 -2
  6. package/.claude/config/audio-effects.cfg.sample +52 -52
  7. package/.claude/config/background-music-enabled.txt +1 -0
  8. package/.claude/config/background-music-position.txt +1 -1
  9. package/.claude/config/language.txt +1 -0
  10. package/.claude/docs/TERMUX_SETUP.md +408 -408
  11. package/.claude/github-star-reminder.txt +1 -1
  12. package/.claude/hooks/audio-cache-utils.sh +0 -0
  13. package/.claude/hooks/audio-processor.sh +0 -0
  14. package/.claude/hooks/background-music-manager.sh +0 -0
  15. package/.claude/hooks/bmad-party-speak.sh +0 -0
  16. package/.claude/hooks/bmad-speak-enhanced.sh +0 -0
  17. package/.claude/hooks/bmad-speak.sh +0 -0
  18. package/.claude/hooks/bmad-tts-injector.sh +0 -0
  19. package/.claude/hooks/bmad-voice-manager.sh +0 -0
  20. package/.claude/hooks/clawdbot-receiver-SECURE.sh +0 -0
  21. package/.claude/hooks/clawdbot-receiver.sh +0 -0
  22. package/.claude/hooks/clean-audio-cache.sh +0 -0
  23. package/.claude/hooks/cleanup-cache.sh +0 -0
  24. package/.claude/hooks/configure-rdp-mode.sh +0 -0
  25. package/.claude/hooks/download-extra-voices.sh +0 -0
  26. package/.claude/hooks/effects-manager.sh +0 -0
  27. package/.claude/hooks/github-star-reminder.sh +0 -0
  28. package/.claude/hooks/language-manager.sh +0 -0
  29. package/.claude/hooks/learn-manager.sh +0 -0
  30. package/.claude/hooks/macos-voice-manager.sh +0 -0
  31. package/.claude/hooks/migrate-background-music.sh +0 -0
  32. package/.claude/hooks/migrate-to-agentvibes.sh +0 -0
  33. package/.claude/hooks/optimize-background-music.sh +0 -0
  34. package/.claude/hooks/path-resolver.sh +0 -0
  35. package/.claude/hooks/personality-manager.sh +0 -0
  36. package/.claude/hooks/piper-download-voices.sh +0 -0
  37. package/.claude/hooks/piper-installer.sh +0 -0
  38. package/.claude/hooks/piper-multispeaker-registry.sh +0 -0
  39. package/.claude/hooks/piper-voice-manager.sh +0 -0
  40. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +0 -0
  41. package/.claude/hooks/play-tts-agentvibes-receiver.sh +1 -0
  42. package/.claude/hooks/play-tts-enhanced.sh +0 -0
  43. package/.claude/hooks/play-tts-macos.sh +0 -0
  44. package/.claude/hooks/play-tts-piper.sh +0 -0
  45. package/.claude/hooks/play-tts-soprano.sh +0 -0
  46. package/.claude/hooks/play-tts-ssh-remote.sh +2 -2
  47. package/.claude/hooks/play-tts-termux-ssh.sh +0 -0
  48. package/.claude/hooks/play-tts-windows-receiver.sh +0 -0
  49. package/.claude/hooks/play-tts.sh +0 -0
  50. package/.claude/hooks/prepare-release.sh +0 -0
  51. package/.claude/hooks/provider-commands.sh +0 -0
  52. package/.claude/hooks/provider-manager.sh +0 -0
  53. package/.claude/hooks/replay-target-audio.sh +0 -0
  54. package/.claude/hooks/requirements.txt +6 -6
  55. package/.claude/hooks/sentiment-manager.sh +0 -0
  56. package/.claude/hooks/session-start-tts.sh +0 -0
  57. package/.claude/hooks/soprano-gradio-synth.py +139 -139
  58. package/.claude/hooks/speed-manager.sh +0 -0
  59. package/.claude/hooks/stop-tts.sh +0 -0
  60. package/.claude/hooks/termux-installer.sh +0 -0
  61. package/.claude/hooks/translate-manager.sh +0 -0
  62. package/.claude/hooks/translator.py +237 -237
  63. package/.claude/hooks/tts-queue-worker.sh +0 -0
  64. package/.claude/hooks/tts-queue.sh +0 -0
  65. package/.claude/hooks/verbosity-manager.sh +0 -0
  66. package/.claude/hooks/voice-manager.sh +0 -0
  67. package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
  68. package/.claude/hooks-windows/audio-cache-utils.ps1.user.bak +119 -0
  69. package/.claude/hooks-windows/bmad-speak.ps1 +9 -38
  70. package/.claude/hooks-windows/play-tts-soprano.ps1 +13 -2
  71. package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
  72. package/.claude/hooks-windows/soprano-gradio-synth.py.user.bak +153 -0
  73. package/.claude/piper-voices-dir.txt +1 -1
  74. package/.claude/verbosity.txt +1 -1
  75. package/.clawdbot/README.md +105 -105
  76. package/.mcp.json +5 -14
  77. package/README.md +33 -2
  78. package/RELEASE_NOTES.md +80 -0
  79. package/WINDOWS-SETUP.md +208 -208
  80. package/bin/agent-vibes +39 -39
  81. package/bin/agentvibes-voice-browser.js +0 -0
  82. package/bin/agentvibes.js +0 -0
  83. package/bin/mcp-server.js +121 -121
  84. package/bin/mcp-server.sh +0 -0
  85. package/bin/test-bmad-pr +78 -78
  86. package/mcp-server/QUICK_START.md +203 -203
  87. package/mcp-server/README.md +345 -345
  88. package/mcp-server/WINDOWS_SETUP.md +0 -0
  89. package/mcp-server/examples/claude_desktop_config.json +11 -11
  90. package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
  91. package/mcp-server/examples/custom_instructions.md +169 -169
  92. package/mcp-server/install-deps.js +0 -0
  93. package/mcp-server/server.py +1797 -1797
  94. package/mcp-server/test_server.py +0 -0
  95. package/package.json +1 -1
  96. package/src/cli/list-personalities.js +110 -110
  97. package/src/cli/list-voices.js +114 -114
  98. package/src/commands/bmad-voices.js +394 -394
  99. package/src/commands/install-mcp.js +476 -476
  100. package/src/console/audio-env.js +4 -1
  101. package/src/console/brand-colors.js +13 -13
  102. package/src/console/constants/personalities.js +44 -44
  103. package/src/console/tabs/agents-tab.js +84 -61
  104. package/src/console/tabs/help-tab.js +314 -314
  105. package/src/console/tabs/music-tab.js +3 -2
  106. package/src/console/tabs/readme-tab.js +272 -272
  107. package/src/console/tabs/setup-tab.js +285 -41
  108. package/src/console/tabs/voices-tab.js +13 -1
  109. package/src/console/widgets/destroy-list.js +25 -25
  110. package/src/console/widgets/notice.js +55 -55
  111. package/src/i18n/de.js +202 -202
  112. package/src/i18n/es.js +202 -202
  113. package/src/i18n/fr.js +202 -202
  114. package/src/i18n/hi.js +202 -202
  115. package/src/i18n/ja.js +202 -202
  116. package/src/i18n/ko.js +202 -202
  117. package/src/i18n/pt.js +202 -202
  118. package/src/i18n/strings.js +54 -54
  119. package/src/i18n/zh-CN.js +202 -202
  120. package/src/installer/language-screen.js +31 -31
  121. package/src/installer/music-file-input.js +304 -304
  122. package/src/installer.js +0 -0
  123. package/src/services/config-service.js +264 -264
  124. package/src/services/language-service.js +47 -47
  125. package/src/services/provider-service.js +143 -143
  126. package/src/utils/audio-duration-validator.js +298 -298
  127. package/src/utils/audio-format-validator.js +277 -277
  128. package/src/utils/dependency-checker.js +469 -469
  129. package/src/utils/file-ownership-verifier.js +358 -358
  130. package/src/utils/list-formatter.js +194 -194
  131. package/src/utils/music-file-validator.js +285 -285
  132. package/src/utils/preview-list-prompt.js +136 -136
  133. package/src/utils/secure-music-storage.js +412 -412
  134. package/templates/agentvibes-receiver.sh +231 -231
  135. package/templates/audio/welcome-music.mp3 +0 -0
  136. package/.claude/hooks/bmad-party-manager.sh +0 -225
  137. package/.claude/hooks/stop.sh +0 -38
  138. /package/.claude/audio/tracks/{CelestialVelvet.mp3 → celestial_velvet.mp3} +0 -0
@@ -1,314 +1,314 @@
1
- /**
2
- * AgentVibes TUI Console — Help Tab
3
- * Epic 13: Story 13.1
4
- *
5
- * Implements the Tab Component Contract:
6
- * createHelpTab(screen, services) → { box, show, hide, onFocus, onBlur, getFooterText, getFooterColor }
7
- *
8
- * Features: keyboard shortcuts reference, two sections, [/] search.
9
- */
10
-
11
- import { t } from '../../i18n/strings.js';
12
-
13
- const IS_TEST = process.env.AGENTVIBES_TEST_MODE === 'true';
14
-
15
- let blessed;
16
- if (!IS_TEST) {
17
- const { default: b } = await import('blessed');
18
- blessed = b;
19
- }
20
-
21
- // ---------------------------------------------------------------------------
22
-
23
- const COLORS = {
24
- contentBg: '#0a0e1a',
25
- sectionHdr: '#546e7a', // Blue-gray — Help tab
26
- labelFg: '#e3f2fd',
27
- keyFg: '#ffff00', // Yellow — keyboard shortcuts
28
- descFg: '#90a4ae', // Gray — descriptions
29
- borderFg: '#607d8b',
30
- footerBg: '#607d8b', // Gray — Help tab footer
31
- };
32
-
33
- // ---------------------------------------------------------------------------
34
-
35
- /**
36
- * Return all shortcut sections.
37
- * @returns {{ title: string, shortcuts: { key: string, desc: string }[] }[]}
38
- */
39
- export function getShortcutSections() {
40
- return [
41
- {
42
- title: 'Global Shortcuts',
43
- shortcuts: [
44
- { key: 'Q', desc: 'Quit the console' },
45
- { key: 'Ctrl+C', desc: 'Force quit' },
46
- { key: 'S', desc: 'Switch to Settings tab' },
47
- { key: 'V', desc: 'Switch to Voices tab' },
48
- { key: 'M', desc: 'Switch to Music tab' },
49
- { key: 'R', desc: 'Switch to Readme tab' },
50
- { key: 'H', desc: 'Switch to Help tab' },
51
- { key: 'I', desc: 'Switch to Install tab' },
52
- { key: 'Esc', desc: 'Close modal / go back' },
53
- ],
54
- },
55
- {
56
- title: 'Navigation Shortcuts',
57
- shortcuts: [
58
- { key: '↑↓ / j k', desc: 'Navigate lists' },
59
- { key: 'Enter', desc: 'Select / activate' },
60
- { key: 'Space', desc: 'Toggle / preview' },
61
- { key: 'Tab', desc: 'Next button' },
62
- { key: 'Shift+Tab', desc: 'Previous button' },
63
- { key: '/', desc: 'Open search/filter' },
64
- { key: 'F', desc: 'Toggle favorites filter (Voices/Music)' },
65
- { key: '*', desc: 'Toggle favorite (Music tab)' },
66
- { key: 'M', desc: 'Toggle music on/off (Music tab)' },
67
- ],
68
- },
69
- {
70
- title: 'Tab Color Guide',
71
- shortcuts: [
72
- { key: 'Blue (#2196f3)', desc: 'Settings tab footer' },
73
- { key: 'Teal (#00695c)', desc: 'Voices tab footer' },
74
- { key: 'Orange (#ff9800)', desc: 'Music tab footer' },
75
- { key: 'Dark (#455a64)', desc: 'Readme tab footer' },
76
- { key: 'Gray (#607d8b)', desc: 'Help tab footer' },
77
- { key: 'Indigo (#3f51b5)', desc: 'Install tab footer' },
78
- ],
79
- },
80
- ];
81
- }
82
-
83
- // ---------------------------------------------------------------------------
84
-
85
- const _FOOTER_TEXT_EN = '[↑↓/jk] Scroll [/] Search [PgUp/PgDn] Page [S/V/M/A/R] Tab [Q] Quit';
86
-
87
- function createTestStub() {
88
- return {
89
- box: {},
90
- show: () => {},
91
- hide: () => {},
92
- onFocus: () => {},
93
- onBlur: () => {},
94
- getFooterText: () => _FOOTER_TEXT_EN,
95
- getFooterColor: () => COLORS.footerBg,
96
- };
97
- }
98
-
99
- // ---------------------------------------------------------------------------
100
-
101
- /**
102
- * Create the Help tab component.
103
- *
104
- * @param {object} screen - Blessed screen instance
105
- * @param {object} services
106
- * @returns {{ box, show, hide, onFocus, onBlur, getFooterText, getFooterColor }}
107
- */
108
- export function createHelpTab(screen, services) {
109
- if (IS_TEST) return createTestStub();
110
-
111
- const { focusMainTabBar, languageService } = services;
112
- const _tl = (key) => languageService ? languageService.t(key) : t('en', key);
113
-
114
- function _buildSections() {
115
- return [
116
- {
117
- title: _tl('helpSectionGlobal'),
118
- shortcuts: [
119
- { key: 'Q', desc: _tl('helpQuit') },
120
- { key: 'Ctrl+C', desc: _tl('helpForceQuit') },
121
- { key: 'S', desc: _tl('helpSwitchSettings') },
122
- { key: 'V', desc: _tl('helpSwitchVoices') },
123
- { key: 'M', desc: _tl('helpSwitchMusic') },
124
- { key: 'R', desc: _tl('helpSwitchReadme') },
125
- { key: 'H', desc: _tl('helpSwitchHelp') },
126
- { key: 'I', desc: _tl('helpSwitchInstall') },
127
- { key: 'Esc', desc: _tl('helpCloseModal') },
128
- ],
129
- },
130
- {
131
- title: _tl('helpSectionNavigation'),
132
- shortcuts: [
133
- { key: '↑↓ / j k', desc: _tl('helpNavigateLists') },
134
- { key: 'Enter', desc: _tl('helpSelectActivate') },
135
- { key: 'Space', desc: _tl('helpTogglePreview') },
136
- { key: 'Tab', desc: _tl('helpNextButton') },
137
- { key: 'Shift+Tab', desc: _tl('helpPrevButton') },
138
- { key: '/', desc: _tl('helpOpenSearch') },
139
- { key: 'F', desc: _tl('helpToggleFavFilter') },
140
- { key: '*', desc: _tl('helpToggleFav') },
141
- { key: 'M', desc: _tl('helpToggleMusic') },
142
- ],
143
- },
144
- {
145
- title: _tl('helpSectionColors'),
146
- shortcuts: [
147
- { key: 'Blue (#2196f3)', desc: _tl('helpColorSettings') },
148
- { key: 'Teal (#00695c)', desc: _tl('helpColorVoices') },
149
- { key: 'Orange (#ff9800)', desc: _tl('helpColorMusic') },
150
- { key: 'Dark (#455a64)', desc: _tl('helpColorReadme') },
151
- { key: 'Gray (#607d8b)', desc: _tl('helpColorHelp') },
152
- { key: 'Indigo (#3f51b5)', desc: _tl('helpColorInstall') },
153
- ],
154
- },
155
- ];
156
- }
157
-
158
- // -------------------------------------------------------------------------
159
- // Container
160
-
161
- const box = blessed.box({
162
- parent: screen,
163
- top: 5,
164
- left: 0,
165
- width: '100%',
166
- bottom: 2,
167
- hidden: true,
168
- style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
169
- border: { type: 'line' },
170
- borderStyle: { fg: COLORS.borderFg },
171
- });
172
-
173
- // -------------------------------------------------------------------------
174
- // Build content text
175
-
176
- function _buildContent(filterText) {
177
- const lines = [];
178
- for (const section of _buildSections()) {
179
- lines.push(`{bold}{#546e7a-fg}── ${section.title} ${'─'.repeat(Math.max(0, 60 - section.title.length))}{/#546e7a-fg}{/bold}`);
180
- for (const { key, desc } of section.shortcuts) {
181
- const displayKey = key.padEnd(20);
182
- const displayDesc = desc;
183
- if (filterText && !key.toLowerCase().includes(filterText) && !desc.toLowerCase().includes(filterText)) {
184
- continue;
185
- }
186
- lines.push(` {${COLORS.keyFg}-fg}${displayKey}{/${COLORS.keyFg}-fg} {${COLORS.descFg}-fg}${displayDesc}{/${COLORS.descFg}-fg}`);
187
- }
188
- lines.push('');
189
- }
190
- return lines.join('\n');
191
- }
192
-
193
- // -------------------------------------------------------------------------
194
- // Scrollable content
195
-
196
- const scrollBox = blessed.box({
197
- parent: box,
198
- top: 1,
199
- left: 2,
200
- width: '96%',
201
- bottom: 4,
202
- scrollable: true,
203
- alwaysScroll: true,
204
- tags: true,
205
- keys: true,
206
- vi: true,
207
- mouse: true,
208
- scrollbar: { ch: '│', style: { fg: COLORS.sectionHdr } },
209
- content: _buildContent(''),
210
- style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
211
- });
212
-
213
- // -------------------------------------------------------------------------
214
- // Search
215
-
216
- const searchBox = blessed.textbox({
217
- parent: box,
218
- bottom: 2,
219
- left: 10,
220
- width: 30,
221
- height: 1,
222
- hidden: true,
223
- inputOnFocus: true,
224
- keys: true,
225
- style: { fg: COLORS.keyFg, bg: '#1a3a5c', focus: { bg: '#245a80' } },
226
- });
227
-
228
- const searchLabel = blessed.text({
229
- parent: box,
230
- bottom: 2,
231
- left: 2,
232
- content: _tl('helpSearchLabel'),
233
- style: { fg: COLORS.descFg, bg: COLORS.contentBg },
234
- });
235
-
236
- searchBox.on('keypress', () => {
237
- setTimeout(() => {
238
- const filter = searchBox.getValue().toLowerCase().trim();
239
- scrollBox.setContent(_buildContent(filter));
240
- screen.render();
241
- }, 0);
242
- });
243
-
244
- searchBox.key(['escape'], () => {
245
- searchBox.clearValue();
246
- searchBox.hide();
247
- scrollBox.setContent(_buildContent(''));
248
- scrollBox.focus();
249
- screen.render();
250
- });
251
-
252
- scrollBox.key(['/'], () => {
253
- searchBox.show();
254
- searchBox.clearValue();
255
- searchBox.focus();
256
- screen.render();
257
- });
258
-
259
- // [↑] at top of content → jump to main header tab bar
260
- scrollBox.key(['up'], () => {
261
- if (scrollBox.getScroll() === 0 && typeof focusMainTabBar === 'function') {
262
- focusMainTabBar();
263
- }
264
- });
265
-
266
- // Escape → return to header tab bar
267
- scrollBox.key(['escape'], () => {
268
- if (typeof focusMainTabBar === 'function') { focusMainTabBar(); screen.render(); }
269
- });
270
-
271
- // -------------------------------------------------------------------------
272
- // Language change handler
273
-
274
- if (languageService) {
275
- languageService.onChange(() => {
276
- scrollBox.setContent(_buildContent(''));
277
- searchLabel.setContent(_tl('helpSearchLabel'));
278
- screen.render();
279
- });
280
- }
281
-
282
- // -------------------------------------------------------------------------
283
- // Tab Component Contract
284
-
285
- return {
286
- box,
287
-
288
- show() {
289
- box.show();
290
- scrollBox.setContent(_buildContent(''));
291
- screen.render();
292
- },
293
-
294
- hide() {
295
- box.hide();
296
- screen.render();
297
- },
298
-
299
- onFocus() {
300
- scrollBox.focus();
301
- screen.render();
302
- },
303
-
304
- onBlur() {},
305
-
306
- getFooterText() {
307
- return _tl('helpFooter');
308
- },
309
-
310
- getFooterColor() {
311
- return COLORS.footerBg;
312
- },
313
- };
314
- }
1
+ /**
2
+ * AgentVibes TUI Console — Help Tab
3
+ * Epic 13: Story 13.1
4
+ *
5
+ * Implements the Tab Component Contract:
6
+ * createHelpTab(screen, services) → { box, show, hide, onFocus, onBlur, getFooterText, getFooterColor }
7
+ *
8
+ * Features: keyboard shortcuts reference, two sections, [/] search.
9
+ */
10
+
11
+ import { t } from '../../i18n/strings.js';
12
+
13
+ const IS_TEST = process.env.AGENTVIBES_TEST_MODE === 'true';
14
+
15
+ let blessed;
16
+ if (!IS_TEST) {
17
+ const { default: b } = await import('blessed');
18
+ blessed = b;
19
+ }
20
+
21
+ // ---------------------------------------------------------------------------
22
+
23
+ const COLORS = {
24
+ contentBg: '#0a0e1a',
25
+ sectionHdr: '#546e7a', // Blue-gray — Help tab
26
+ labelFg: '#e3f2fd',
27
+ keyFg: '#ffff00', // Yellow — keyboard shortcuts
28
+ descFg: '#90a4ae', // Gray — descriptions
29
+ borderFg: '#607d8b',
30
+ footerBg: '#607d8b', // Gray — Help tab footer
31
+ };
32
+
33
+ // ---------------------------------------------------------------------------
34
+
35
+ /**
36
+ * Return all shortcut sections.
37
+ * @returns {{ title: string, shortcuts: { key: string, desc: string }[] }[]}
38
+ */
39
+ export function getShortcutSections() {
40
+ return [
41
+ {
42
+ title: 'Global Shortcuts',
43
+ shortcuts: [
44
+ { key: 'Q', desc: 'Quit the console' },
45
+ { key: 'Ctrl+C', desc: 'Force quit' },
46
+ { key: 'S', desc: 'Switch to Settings tab' },
47
+ { key: 'V', desc: 'Switch to Voices tab' },
48
+ { key: 'M', desc: 'Switch to Music tab' },
49
+ { key: 'R', desc: 'Switch to Readme tab' },
50
+ { key: 'H', desc: 'Switch to Help tab' },
51
+ { key: 'I', desc: 'Switch to Install tab' },
52
+ { key: 'Esc', desc: 'Close modal / go back' },
53
+ ],
54
+ },
55
+ {
56
+ title: 'Navigation Shortcuts',
57
+ shortcuts: [
58
+ { key: '↑↓ / j k', desc: 'Navigate lists' },
59
+ { key: 'Enter', desc: 'Select / activate' },
60
+ { key: 'Space', desc: 'Toggle / preview' },
61
+ { key: 'Tab', desc: 'Next button' },
62
+ { key: 'Shift+Tab', desc: 'Previous button' },
63
+ { key: '/', desc: 'Open search/filter' },
64
+ { key: 'F', desc: 'Toggle favorites filter (Voices/Music)' },
65
+ { key: '*', desc: 'Toggle favorite (Music tab)' },
66
+ { key: 'M', desc: 'Toggle music on/off (Music tab)' },
67
+ ],
68
+ },
69
+ {
70
+ title: 'Tab Color Guide',
71
+ shortcuts: [
72
+ { key: 'Blue (#2196f3)', desc: 'Settings tab footer' },
73
+ { key: 'Teal (#00695c)', desc: 'Voices tab footer' },
74
+ { key: 'Orange (#ff9800)', desc: 'Music tab footer' },
75
+ { key: 'Dark (#455a64)', desc: 'Readme tab footer' },
76
+ { key: 'Gray (#607d8b)', desc: 'Help tab footer' },
77
+ { key: 'Indigo (#3f51b5)', desc: 'Install tab footer' },
78
+ ],
79
+ },
80
+ ];
81
+ }
82
+
83
+ // ---------------------------------------------------------------------------
84
+
85
+ const _FOOTER_TEXT_EN = '[↑↓/jk] Scroll [/] Search [PgUp/PgDn] Page [S/V/M/A/R] Tab [Q] Quit';
86
+
87
+ function createTestStub() {
88
+ return {
89
+ box: {},
90
+ show: () => {},
91
+ hide: () => {},
92
+ onFocus: () => {},
93
+ onBlur: () => {},
94
+ getFooterText: () => _FOOTER_TEXT_EN,
95
+ getFooterColor: () => COLORS.footerBg,
96
+ };
97
+ }
98
+
99
+ // ---------------------------------------------------------------------------
100
+
101
+ /**
102
+ * Create the Help tab component.
103
+ *
104
+ * @param {object} screen - Blessed screen instance
105
+ * @param {object} services
106
+ * @returns {{ box, show, hide, onFocus, onBlur, getFooterText, getFooterColor }}
107
+ */
108
+ export function createHelpTab(screen, services) {
109
+ if (IS_TEST) return createTestStub();
110
+
111
+ const { focusMainTabBar, languageService } = services;
112
+ const _tl = (key) => languageService ? languageService.t(key) : t('en', key);
113
+
114
+ function _buildSections() {
115
+ return [
116
+ {
117
+ title: _tl('helpSectionGlobal'),
118
+ shortcuts: [
119
+ { key: 'Q', desc: _tl('helpQuit') },
120
+ { key: 'Ctrl+C', desc: _tl('helpForceQuit') },
121
+ { key: 'S', desc: _tl('helpSwitchSettings') },
122
+ { key: 'V', desc: _tl('helpSwitchVoices') },
123
+ { key: 'M', desc: _tl('helpSwitchMusic') },
124
+ { key: 'R', desc: _tl('helpSwitchReadme') },
125
+ { key: 'H', desc: _tl('helpSwitchHelp') },
126
+ { key: 'I', desc: _tl('helpSwitchInstall') },
127
+ { key: 'Esc', desc: _tl('helpCloseModal') },
128
+ ],
129
+ },
130
+ {
131
+ title: _tl('helpSectionNavigation'),
132
+ shortcuts: [
133
+ { key: '↑↓ / j k', desc: _tl('helpNavigateLists') },
134
+ { key: 'Enter', desc: _tl('helpSelectActivate') },
135
+ { key: 'Space', desc: _tl('helpTogglePreview') },
136
+ { key: 'Tab', desc: _tl('helpNextButton') },
137
+ { key: 'Shift+Tab', desc: _tl('helpPrevButton') },
138
+ { key: '/', desc: _tl('helpOpenSearch') },
139
+ { key: 'F', desc: _tl('helpToggleFavFilter') },
140
+ { key: '*', desc: _tl('helpToggleFav') },
141
+ { key: 'M', desc: _tl('helpToggleMusic') },
142
+ ],
143
+ },
144
+ {
145
+ title: _tl('helpSectionColors'),
146
+ shortcuts: [
147
+ { key: 'Blue (#2196f3)', desc: _tl('helpColorSettings') },
148
+ { key: 'Teal (#00695c)', desc: _tl('helpColorVoices') },
149
+ { key: 'Orange (#ff9800)', desc: _tl('helpColorMusic') },
150
+ { key: 'Dark (#455a64)', desc: _tl('helpColorReadme') },
151
+ { key: 'Gray (#607d8b)', desc: _tl('helpColorHelp') },
152
+ { key: 'Indigo (#3f51b5)', desc: _tl('helpColorInstall') },
153
+ ],
154
+ },
155
+ ];
156
+ }
157
+
158
+ // -------------------------------------------------------------------------
159
+ // Container
160
+
161
+ const box = blessed.box({
162
+ parent: screen,
163
+ top: 5,
164
+ left: 0,
165
+ width: '100%',
166
+ bottom: 2,
167
+ hidden: true,
168
+ style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
169
+ border: { type: 'line' },
170
+ borderStyle: { fg: COLORS.borderFg },
171
+ });
172
+
173
+ // -------------------------------------------------------------------------
174
+ // Build content text
175
+
176
+ function _buildContent(filterText) {
177
+ const lines = [];
178
+ for (const section of _buildSections()) {
179
+ lines.push(`{bold}{#546e7a-fg}── ${section.title} ${'─'.repeat(Math.max(0, 60 - section.title.length))}{/#546e7a-fg}{/bold}`);
180
+ for (const { key, desc } of section.shortcuts) {
181
+ const displayKey = key.padEnd(20);
182
+ const displayDesc = desc;
183
+ if (filterText && !key.toLowerCase().includes(filterText) && !desc.toLowerCase().includes(filterText)) {
184
+ continue;
185
+ }
186
+ lines.push(` {${COLORS.keyFg}-fg}${displayKey}{/${COLORS.keyFg}-fg} {${COLORS.descFg}-fg}${displayDesc}{/${COLORS.descFg}-fg}`);
187
+ }
188
+ lines.push('');
189
+ }
190
+ return lines.join('\n');
191
+ }
192
+
193
+ // -------------------------------------------------------------------------
194
+ // Scrollable content
195
+
196
+ const scrollBox = blessed.box({
197
+ parent: box,
198
+ top: 1,
199
+ left: 2,
200
+ width: '96%',
201
+ bottom: 4,
202
+ scrollable: true,
203
+ alwaysScroll: true,
204
+ tags: true,
205
+ keys: true,
206
+ vi: true,
207
+ mouse: true,
208
+ scrollbar: { ch: '│', style: { fg: COLORS.sectionHdr } },
209
+ content: _buildContent(''),
210
+ style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
211
+ });
212
+
213
+ // -------------------------------------------------------------------------
214
+ // Search
215
+
216
+ const searchBox = blessed.textbox({
217
+ parent: box,
218
+ bottom: 2,
219
+ left: 10,
220
+ width: 30,
221
+ height: 1,
222
+ hidden: true,
223
+ inputOnFocus: true,
224
+ keys: true,
225
+ style: { fg: COLORS.keyFg, bg: '#1a3a5c', focus: { bg: '#245a80' } },
226
+ });
227
+
228
+ const searchLabel = blessed.text({
229
+ parent: box,
230
+ bottom: 2,
231
+ left: 2,
232
+ content: _tl('helpSearchLabel'),
233
+ style: { fg: COLORS.descFg, bg: COLORS.contentBg },
234
+ });
235
+
236
+ searchBox.on('keypress', () => {
237
+ setTimeout(() => {
238
+ const filter = searchBox.getValue().toLowerCase().trim();
239
+ scrollBox.setContent(_buildContent(filter));
240
+ screen.render();
241
+ }, 0);
242
+ });
243
+
244
+ searchBox.key(['escape'], () => {
245
+ searchBox.clearValue();
246
+ searchBox.hide();
247
+ scrollBox.setContent(_buildContent(''));
248
+ scrollBox.focus();
249
+ screen.render();
250
+ });
251
+
252
+ scrollBox.key(['/'], () => {
253
+ searchBox.show();
254
+ searchBox.clearValue();
255
+ searchBox.focus();
256
+ screen.render();
257
+ });
258
+
259
+ // [↑] at top of content → jump to main header tab bar
260
+ scrollBox.key(['up'], () => {
261
+ if (scrollBox.getScroll() === 0 && typeof focusMainTabBar === 'function') {
262
+ focusMainTabBar();
263
+ }
264
+ });
265
+
266
+ // Escape → return to header tab bar
267
+ scrollBox.key(['escape'], () => {
268
+ if (typeof focusMainTabBar === 'function') { focusMainTabBar(); screen.render(); }
269
+ });
270
+
271
+ // -------------------------------------------------------------------------
272
+ // Language change handler
273
+
274
+ if (languageService) {
275
+ languageService.onChange(() => {
276
+ scrollBox.setContent(_buildContent(''));
277
+ searchLabel.setContent(_tl('helpSearchLabel'));
278
+ screen.render();
279
+ });
280
+ }
281
+
282
+ // -------------------------------------------------------------------------
283
+ // Tab Component Contract
284
+
285
+ return {
286
+ box,
287
+
288
+ show() {
289
+ box.show();
290
+ scrollBox.setContent(_buildContent(''));
291
+ screen.render();
292
+ },
293
+
294
+ hide() {
295
+ box.hide();
296
+ screen.render();
297
+ },
298
+
299
+ onFocus() {
300
+ scrollBox.focus();
301
+ screen.render();
302
+ },
303
+
304
+ onBlur() {},
305
+
306
+ getFooterText() {
307
+ return _tl('helpFooter');
308
+ },
309
+
310
+ getFooterColor() {
311
+ return COLORS.footerBg;
312
+ },
313
+ };
314
+ }
@@ -64,7 +64,8 @@ const TRACK_DISPLAY = Object.freeze({
64
64
  'agent_vibes_arabic_v2_loop.mp3': '🎵 Arabic Oud',
65
65
  'agent_vibes_bachata_v1_loop.mp3': '🎺 Bachata',
66
66
  'agent_vibes_bossa_nova_v2_loop.mp3': '🌸 Bossa Nova',
67
- 'CelestialVelvet.mp3': '🌌 Celestial Velvet',
67
+ 'celestial_velvet.mp3': '🌌 Celestial Velvet',
68
+ 'CelestialVelvet.mp3': '🌌 Celestial Velvet', // legacy alias — display only, file renamed in v5.7.7
68
69
  'agent_vibes_celtic_harp_v1_loop.mp3': '🎶 Celtic Harp',
69
70
  'agent_vibes_chillwave_v2_loop.mp3': '🌊 Chillwave',
70
71
  'agent_vibes_cumbia_v1_loop.mp3': '🎸 Cumbia',
@@ -86,7 +87,7 @@ const BUILT_IN_TRACK_CATALOG = Object.freeze([
86
87
  { id: 'agent_vibes_arabic_v2_loop.mp3', label: '🎵 Arabic Oud' },
87
88
  { id: 'agent_vibes_bachata_v1_loop.mp3', label: '🎺 Bachata' },
88
89
  { id: 'agent_vibes_bossa_nova_v2_loop.mp3', label: '🌸 Bossa Nova' },
89
- { id: 'CelestialVelvet.mp3', label: '🌌 Celestial Velvet' },
90
+ { id: 'celestial_velvet.mp3', label: '🌌 Celestial Velvet' },
90
91
  { id: 'agent_vibes_celtic_harp_v1_loop.mp3', label: '🎶 Celtic Harp' },
91
92
  { id: 'agent_vibes_chillwave_v2_loop.mp3', label: '🌊 Chillwave' },
92
93
  { id: 'agent_vibes_cumbia_v1_loop.mp3', label: '🎸 Cumbia' },