agentvibes 4.4.1 → 4.5.7
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/config.json +4 -4
- package/.claude/config/audio-effects.cfg +1 -0
- package/.claude/config/background-music-enabled.txt +1 -0
- package/.claude/config/reverb-level.txt +1 -1
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/audio-processor.sh +1 -1
- package/.claude/hooks/bmad-speak.sh +16 -2
- package/.claude/hooks-windows/bmad-speak.ps1 +200 -0
- package/.claude/hooks-windows/play-tts-piper.ps1 +3 -4
- package/.claude/hooks-windows/play-tts-sapi.ps1 +3 -4
- package/.claude/hooks-windows/play-tts-soprano.ps1 +2 -3
- package/.claude/hooks-windows/play-tts-termux-ssh.ps1 +138 -0
- package/.claude/hooks-windows/play-tts.ps1 +14 -6
- package/.claude/hooks-windows/provider-manager.ps1 +16 -1
- package/CLAUDE.md +4 -0
- package/README.md +39 -9
- package/RELEASE_NOTES.md +78 -0
- package/bin/agent-vibes +1 -1
- package/bin/agentvibes-voice-browser.js +1 -1
- package/bin/bmad-speak.js +52 -0
- package/bin/mcp-server.js +1 -1
- package/bin/test-bmad-pr +1 -1
- package/package.json +1 -1
- package/setup-windows.ps1 +4 -4
- package/src/console/app.js +63 -12
- package/src/console/navigation.js +5 -2
- package/src/console/tabs/agents-tab.js +72 -76
- package/src/console/tabs/help-tab.js +107 -54
- package/src/console/tabs/install-tab.js +132 -56
- package/src/console/tabs/music-tab.js +1039 -1011
- package/src/console/tabs/placeholder-tab.js +27 -0
- package/src/console/tabs/readme-tab.js +9 -7
- package/src/console/tabs/receiver-tab.js +23 -12
- package/src/console/tabs/settings-tab.js +4001 -3783
- package/src/console/tabs/voices-tab.js +1680 -1653
- package/src/console/widgets/personality-picker.js +35 -7
- package/src/console/widgets/reverb-picker.js +9 -6
- package/src/console/widgets/track-picker.js +7 -2
- package/src/i18n/de.js +203 -0
- package/src/i18n/en.js +203 -0
- package/src/i18n/es.js +203 -0
- package/src/i18n/fr.js +203 -0
- package/src/i18n/hi.js +203 -0
- package/src/i18n/ja.js +203 -0
- package/src/i18n/ko.js +203 -0
- package/src/i18n/pt.js +203 -0
- package/src/i18n/strings.js +54 -0
- package/src/i18n/zh-CN.js +203 -0
- package/src/installer/language-screen.js +31 -0
- package/src/installer.js +79 -25
- package/src/services/language-service.js +47 -0
- package/src/utils/file-ownership-verifier.js +2 -2
- package/src/utils/provider-validator.js +9 -13
- package/.claude/hooks-windows/play-tts-windows-piper.ps1 +0 -209
- package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +0 -108
|
@@ -19,6 +19,7 @@ import { promisify } from 'node:util';
|
|
|
19
19
|
import fs from 'node:fs';
|
|
20
20
|
import { promises as _fsP } from 'node:fs';
|
|
21
21
|
import { buildAudioEnv } from '../audio-env.js';
|
|
22
|
+
import { SUPPORTED_LANGUAGES, t } from '../../i18n/strings.js';
|
|
22
23
|
import {
|
|
23
24
|
copyCommandFiles, copyHookFiles, copyPersonalityFiles,
|
|
24
25
|
copyPluginFiles, copyBmadConfigFiles, copyBackgroundMusicFiles,
|
|
@@ -140,7 +141,7 @@ function createTestStub() {
|
|
|
140
141
|
hide: () => {},
|
|
141
142
|
onFocus: () => {},
|
|
142
143
|
onBlur: () => {},
|
|
143
|
-
getFooterText: () =>
|
|
144
|
+
getFooterText: () => t('en', 'footerText'),
|
|
144
145
|
getFooterColor: () => COLORS.footerBg,
|
|
145
146
|
};
|
|
146
147
|
}
|
|
@@ -158,7 +159,7 @@ function createTestStub() {
|
|
|
158
159
|
export function createInstallTab(screen, services) {
|
|
159
160
|
if (IS_TEST) return createTestStub();
|
|
160
161
|
|
|
161
|
-
const { configService, providerService, navigationService, focusMainTabBar } = services;
|
|
162
|
+
const { configService, providerService, navigationService, focusMainTabBar, languageService } = services;
|
|
162
163
|
|
|
163
164
|
// -------------------------------------------------------------------------
|
|
164
165
|
// Container
|
|
@@ -178,8 +179,13 @@ export function createInstallTab(screen, services) {
|
|
|
178
179
|
// -------------------------------------------------------------------------
|
|
179
180
|
// Wizard state
|
|
180
181
|
|
|
181
|
-
let _screen =
|
|
182
|
-
let _lastScreen =
|
|
182
|
+
let _screen = 0;
|
|
183
|
+
let _lastScreen = -1;
|
|
184
|
+
// _lang is now owned by languageService; keep a local helper for convenience
|
|
185
|
+
// and a local _langIdx for the language-picker UI (Screen 0).
|
|
186
|
+
const _getLang = () => languageService?.getLang() ?? 'en';
|
|
187
|
+
const _tl = (key) => languageService?.t(key) ?? t('en', key);
|
|
188
|
+
let _langIdx = 0;
|
|
183
189
|
let _deps = null;
|
|
184
190
|
let _checking = false;
|
|
185
191
|
let _selectedProvider = null;
|
|
@@ -437,9 +443,10 @@ export function createInstallTab(screen, services) {
|
|
|
437
443
|
|
|
438
444
|
_renderScreen5();
|
|
439
445
|
|
|
440
|
-
// Show
|
|
441
|
-
|
|
442
|
-
|
|
446
|
+
// Show buttons now that install is done (success or error)
|
|
447
|
+
_s5QuitBtn.show();
|
|
448
|
+
_s5CustomizeBtn.show();
|
|
449
|
+
_s5QuitBtn.focus();
|
|
443
450
|
screen.render();
|
|
444
451
|
|
|
445
452
|
// Play TTS greeting on success
|
|
@@ -525,8 +532,8 @@ export function createInstallTab(screen, services) {
|
|
|
525
532
|
return btn;
|
|
526
533
|
}
|
|
527
534
|
|
|
528
|
-
const _editBtn = _createInstallBtn('
|
|
529
|
-
const _acceptBtn = _createInstallBtn('
|
|
535
|
+
const _editBtn = _createInstallBtn(_tl('editInstallBtn'), '#1565c0', _doEdit);
|
|
536
|
+
const _acceptBtn = _createInstallBtn(_tl('acceptInstallBtn'), COLORS.btnDefault, _doAccept);
|
|
530
537
|
|
|
531
538
|
// Edit sits inline with the intro text row; Accept & Install is below
|
|
532
539
|
_editBtn.top = 8; _editBtn.left = 36;
|
|
@@ -540,11 +547,11 @@ export function createInstallTab(screen, services) {
|
|
|
540
547
|
// -------------------------------------------------------------------------
|
|
541
548
|
// Screen 1 buttons — Begin (cyan) and Exit (grey)
|
|
542
549
|
|
|
543
|
-
const _s1BeginBtn = _createInstallBtn('
|
|
550
|
+
const _s1BeginBtn = _createInstallBtn(_tl('beginBtn'), '#00838f', () => {
|
|
544
551
|
_screen++;
|
|
545
552
|
_showCurrentScreen();
|
|
546
553
|
});
|
|
547
|
-
const _s1ExitBtn = _createInstallBtn('
|
|
554
|
+
const _s1ExitBtn = _createInstallBtn(_tl('exitBtn'), '#546e7a', () => {
|
|
548
555
|
box.hide();
|
|
549
556
|
screen.render();
|
|
550
557
|
if (typeof focusMainTabBar === 'function') focusMainTabBar();
|
|
@@ -562,7 +569,7 @@ export function createInstallTab(screen, services) {
|
|
|
562
569
|
// -------------------------------------------------------------------------
|
|
563
570
|
// Screen 2 button — Continue (shown after deps check passes)
|
|
564
571
|
|
|
565
|
-
const _s2ContinueBtn = _createInstallBtn('
|
|
572
|
+
const _s2ContinueBtn = _createInstallBtn(_tl('continueArrowBtn'), '#1565c0', () => {
|
|
566
573
|
_screen++;
|
|
567
574
|
_showCurrentScreen();
|
|
568
575
|
});
|
|
@@ -573,12 +580,25 @@ export function createInstallTab(screen, services) {
|
|
|
573
580
|
// Screen 3: no Continue button — Enter/→ on the list confirms selection and advances
|
|
574
581
|
|
|
575
582
|
// -------------------------------------------------------------------------
|
|
576
|
-
// Screen 5
|
|
583
|
+
// Screen 5 buttons — Customize More (left) + Done - Quit (right, default focused)
|
|
584
|
+
// Layout: [ Customize More ] [ Done - Quit ]
|
|
585
|
+
// Button width = label + 2 padding chars; gap between buttons = 3 chars
|
|
577
586
|
|
|
578
|
-
const
|
|
587
|
+
const _s5CustomizeBtn = _createInstallBtn(_tl('doneCustomizeBtn'), '#1565c0', () => {
|
|
579
588
|
_dismissCompletionModal();
|
|
580
589
|
});
|
|
581
|
-
|
|
590
|
+
_s5CustomizeBtn.bottom = 3; _s5CustomizeBtn.left = 4;
|
|
591
|
+
|
|
592
|
+
const _s5QuitBtn = _createInstallBtn(_tl('doneQuitBtn'), '#b71c1c', () => {
|
|
593
|
+
screen.destroy();
|
|
594
|
+
process.exit(0);
|
|
595
|
+
});
|
|
596
|
+
// left = start(4) + customizeLabel + 2 padding + 3 gap
|
|
597
|
+
_s5QuitBtn.bottom = 3; _s5QuitBtn.left = 4 + _tl('doneCustomizeBtn').length + 2 + 3;
|
|
598
|
+
|
|
599
|
+
// Arrow/Tab navigation between the two buttons
|
|
600
|
+
_s5CustomizeBtn.key(['tab', 'right'], () => { _s5QuitBtn.focus(); screen.render(); });
|
|
601
|
+
_s5QuitBtn.key(['tab', 'left', 'S-tab'], () => { _s5CustomizeBtn.focus(); screen.render(); });
|
|
582
602
|
|
|
583
603
|
// -------------------------------------------------------------------------
|
|
584
604
|
// Screen renderers
|
|
@@ -586,15 +606,35 @@ export function createInstallTab(screen, services) {
|
|
|
586
606
|
const _HDR = (emoji, label) =>
|
|
587
607
|
`{${COLORS.sectionHdr}-fg}${emoji} ${label} ${'─'.repeat(100)}{/${COLORS.sectionHdr}-fg}`;
|
|
588
608
|
|
|
609
|
+
function _renderScreen0() {
|
|
610
|
+
const lines = [
|
|
611
|
+
_HDR('🌐', 'Language / Idioma / Langue / Sprache / 言語 / भाषा / 语言 / 언어'),
|
|
612
|
+
'',
|
|
613
|
+
' Select your language:',
|
|
614
|
+
'',
|
|
615
|
+
...SUPPORTED_LANGUAGES.map((l, i) =>
|
|
616
|
+
i === _langIdx
|
|
617
|
+
? ` {green-fg}► ${l.name}{/green-fg}`
|
|
618
|
+
: ` ${l.name}`
|
|
619
|
+
),
|
|
620
|
+
];
|
|
621
|
+
contentBox.setContent(_c(lines));
|
|
622
|
+
hintLine.setContent(' Screen 0: Language | [↑/↓] Select | [Enter] Apply & Continue | [→] Skip (English)');
|
|
623
|
+
screen.render();
|
|
624
|
+
}
|
|
625
|
+
|
|
589
626
|
function _renderScreen1() {
|
|
627
|
+
// Update button labels to current language before focus triggers the decorator
|
|
628
|
+
_s1BeginBtn.setContent(t(_getLang(), 'beginBtn'));
|
|
629
|
+
_s1ExitBtn.setContent(t(_getLang(), 'exitBtn'));
|
|
590
630
|
contentBox.setContent(_c([
|
|
591
|
-
_HDR('🔧',
|
|
631
|
+
_HDR('🔧', t(_getLang(), 'setupWizard')),
|
|
592
632
|
'',
|
|
593
|
-
` {${COLORS.noticeFg}-fg}
|
|
633
|
+
` {${COLORS.noticeFg}-fg}${t(_getLang(), 'setupWizardSubtitle')}{/${COLORS.noticeFg}-fg}`,
|
|
594
634
|
'',
|
|
595
635
|
'', // ← [▶ Begin] [✗ Exit] buttons here (box row 5)
|
|
596
636
|
]));
|
|
597
|
-
hintLine.setContent(
|
|
637
|
+
hintLine.setContent(` ${t(_getLang(), 'screen1Hint')}`);
|
|
598
638
|
_s1BeginBtn.focus();
|
|
599
639
|
screen.render();
|
|
600
640
|
}
|
|
@@ -606,19 +646,19 @@ export function createInstallTab(screen, services) {
|
|
|
606
646
|
_s2ContinueBtn.hide(); // hidden during spinner
|
|
607
647
|
|
|
608
648
|
contentBox.setContent(_c([
|
|
609
|
-
_HDR('🔍',
|
|
649
|
+
_HDR('🔍', t(_getLang(), 'dependencyCheck')),
|
|
610
650
|
'',
|
|
611
|
-
` {${COLORS.noticeFg}-fg}${frames[0]}
|
|
651
|
+
` {${COLORS.noticeFg}-fg}${frames[0]} ${t(_getLang(), 'checkingDependencies')}{/${COLORS.noticeFg}-fg}`,
|
|
612
652
|
]));
|
|
613
|
-
hintLine.setContent(
|
|
653
|
+
hintLine.setContent(` ${t(_getLang(), 'screen2Hint')}`);
|
|
614
654
|
screen.render();
|
|
615
655
|
|
|
616
656
|
const spinInterval = setInterval(() => {
|
|
617
657
|
frameIdx = (frameIdx + 1) % frames.length;
|
|
618
658
|
contentBox.setContent(_c([
|
|
619
|
-
_HDR('🔍',
|
|
659
|
+
_HDR('🔍', t(_getLang(), 'dependencyCheck')),
|
|
620
660
|
'',
|
|
621
|
-
` {${COLORS.noticeFg}-fg}${frames[frameIdx]}
|
|
661
|
+
` {${COLORS.noticeFg}-fg}${frames[frameIdx]} ${t(_getLang(), 'checkingDependencies')}{/${COLORS.noticeFg}-fg}`,
|
|
622
662
|
]));
|
|
623
663
|
screen.render();
|
|
624
664
|
}, 100);
|
|
@@ -630,28 +670,29 @@ export function createInstallTab(screen, services) {
|
|
|
630
670
|
_checking = false;
|
|
631
671
|
}
|
|
632
672
|
|
|
633
|
-
const ok = () => `{${COLORS.successFg}-fg}✅
|
|
634
|
-
const bad = () => `{${COLORS.errorFg}-fg}❌
|
|
673
|
+
const ok = () => `{${COLORS.successFg}-fg}✅ ${t(_getLang(), 'installed')}{/${COLORS.successFg}-fg}`;
|
|
674
|
+
const bad = () => `{${COLORS.errorFg}-fg}❌ ${t(_getLang(), 'notFound')}{/${COLORS.errorFg}-fg}`;
|
|
635
675
|
|
|
636
676
|
const ttsOk = _deps.piper || _deps.soprano;
|
|
637
677
|
contentBox.setContent(_c([
|
|
638
|
-
_HDR('🔍',
|
|
678
|
+
_HDR('🔍', t(_getLang(), 'dependencyCheck')),
|
|
639
679
|
'',
|
|
640
|
-
` {${COLORS.noticeFg}-fg}${'
|
|
680
|
+
` {${COLORS.noticeFg}-fg}${t(_getLang(), 'depColumn').padEnd(14)}${t(_getLang(), 'statusColumn')}{/${COLORS.noticeFg}-fg}`,
|
|
641
681
|
` {${COLORS.noticeFg}-fg}${'─'.repeat(78)}{/${COLORS.noticeFg}-fg}`,
|
|
642
682
|
` {${COLORS.labelFg}-fg}${'Node.js'.padEnd(14)}{/${COLORS.labelFg}-fg}${_deps.node ? ok() : bad()}`,
|
|
643
683
|
` {${COLORS.labelFg}-fg}${'npm'.padEnd(14)}{/${COLORS.labelFg}-fg}${_deps.npm ? ok() : bad()}`,
|
|
644
684
|
` {${COLORS.labelFg}-fg}${'Piper TTS'.padEnd(14)}{/${COLORS.labelFg}-fg}${_deps.piper ? ok() : bad()}`,
|
|
645
685
|
` {${COLORS.labelFg}-fg}${'Soprano TTS'.padEnd(14)}{/${COLORS.labelFg}-fg}${_deps.soprano ? ok() : bad()}`,
|
|
646
|
-
` {${COLORS.labelFg}-fg}${'ffmpeg'.padEnd(14)}{/${COLORS.labelFg}-fg}${_deps.ffmpeg ? ok() : `{${COLORS.errorFg}-fg}⚠
|
|
686
|
+
` {${COLORS.labelFg}-fg}${'ffmpeg'.padEnd(14)}{/${COLORS.labelFg}-fg}${_deps.ffmpeg ? ok() : `{${COLORS.errorFg}-fg}⚠ ${t(_getLang(), 'ffmpegMissing')}{/${COLORS.errorFg}-fg}`}`,
|
|
647
687
|
'',
|
|
648
688
|
ttsOk
|
|
649
|
-
? ` {${COLORS.successFg}-fg}✅
|
|
650
|
-
: ` {${COLORS.errorFg}-fg}⚠
|
|
689
|
+
? ` {${COLORS.successFg}-fg}✅ ${t(_getLang(), 'ttsDetected')}{/${COLORS.successFg}-fg}`
|
|
690
|
+
: ` {${COLORS.errorFg}-fg}⚠ ${t(_getLang(), 'noTtsFound')}{/${COLORS.errorFg}-fg}`,
|
|
651
691
|
'', // blank separator
|
|
652
692
|
'', // ← [Continue →] button here (box row 12) when TTS detected
|
|
653
693
|
]));
|
|
654
694
|
if (ttsOk) {
|
|
695
|
+
_s2ContinueBtn.setContent(_tl('continueArrowBtn'));
|
|
655
696
|
_s2ContinueBtn.show();
|
|
656
697
|
_s2ContinueBtn.focus();
|
|
657
698
|
}
|
|
@@ -685,14 +726,14 @@ export function createInstallTab(screen, services) {
|
|
|
685
726
|
const _blank = ' '.repeat(120);
|
|
686
727
|
const _trail = Array(12).fill(_blank);
|
|
687
728
|
contentBox.setContent(_c([
|
|
688
|
-
_HDR('🎤',
|
|
729
|
+
_HDR('🎤', t(_getLang(), 'providerSelection')),
|
|
689
730
|
'',
|
|
690
|
-
` {${COLORS.noticeFg}-fg}${
|
|
731
|
+
` {${COLORS.noticeFg}-fg}${t(_getLang(), 'availableProviders').padEnd(94)}{/${COLORS.noticeFg}-fg}`,
|
|
691
732
|
'',
|
|
692
733
|
...paddedItems.map(i => ` ${i}`),
|
|
693
734
|
..._trail,
|
|
694
735
|
]));
|
|
695
|
-
hintLine.setContent(
|
|
736
|
+
hintLine.setContent(` ${t(_getLang(), 'screen3Hint')}`);
|
|
696
737
|
box.focus();
|
|
697
738
|
screen.render();
|
|
698
739
|
}
|
|
@@ -705,36 +746,38 @@ export function createInstallTab(screen, services) {
|
|
|
705
746
|
const voiceId = providerService?.getActiveVoiceId?.() ?? 'en_US-amy-medium';
|
|
706
747
|
|
|
707
748
|
contentBox.setContent(_c([
|
|
708
|
-
_HDR('🎤',
|
|
749
|
+
_HDR('🎤', t(_getLang(), 'providerAndVoice')),
|
|
709
750
|
'',
|
|
710
|
-
` {${COLORS.labelFg}-fg}${'
|
|
711
|
-
` {${COLORS.labelFg}-fg}${'
|
|
751
|
+
` {${COLORS.labelFg}-fg}${`${t(_getLang(), 'providerLabel')}:`.padEnd(14)}{/${COLORS.labelFg}-fg}{${COLORS.valueFg}-fg}${provider}{/${COLORS.valueFg}-fg}`,
|
|
752
|
+
` {${COLORS.labelFg}-fg}${`${t(_getLang(), 'voiceLabel')}:`.padEnd(14)}{/${COLORS.labelFg}-fg}{${COLORS.valueFg}-fg}${voiceId}{/${COLORS.valueFg}-fg} {${COLORS.noticeFg}-fg}${t(_getLang(), 'voiceChangeHint')}{/${COLORS.noticeFg}-fg}`,
|
|
712
753
|
'',
|
|
713
|
-
_HDR('✍️',
|
|
754
|
+
_HDR('✍️', t(_getLang(), 'introText')),
|
|
714
755
|
'',
|
|
715
|
-
` {${COLORS.labelFg}-fg}${
|
|
756
|
+
` {${COLORS.labelFg}-fg}${`${t(_getLang(), 'introTextLabel')}:`.padEnd(14)}{/${COLORS.labelFg}-fg}{${COLORS.valueFg}-fg}${intro || `(${t(_getLang(), 'none')})`}{/${COLORS.valueFg}-fg}`,
|
|
716
757
|
// ↑ [Edit] button rendered inline at box row 8, left=36
|
|
717
758
|
'',
|
|
718
|
-
` {${COLORS.noticeFg}-fg}
|
|
759
|
+
` {${COLORS.noticeFg}-fg}${t(_getLang(), 'example')}:{/${COLORS.noticeFg}-fg} {${COLORS.valueFg}-fg}"${example}"{/${COLORS.valueFg}-fg}`,
|
|
719
760
|
'',
|
|
720
761
|
'',
|
|
721
762
|
'', // ← [✓ Accept & Install] button rendered as real widget here (box row 13)
|
|
722
763
|
]));
|
|
723
|
-
hintLine.setContent(
|
|
764
|
+
hintLine.setContent(` ${t(_getLang(), 'screen4Hint')}`);
|
|
765
|
+
_editBtn.setContent(_tl('editInstallBtn'));
|
|
766
|
+
_acceptBtn.setContent(_tl('acceptInstallBtn'));
|
|
724
767
|
_acceptBtn.focus();
|
|
725
768
|
screen.render();
|
|
726
769
|
}
|
|
727
770
|
|
|
728
771
|
function _renderScreen5() {
|
|
729
772
|
const header = _installError
|
|
730
|
-
? _HDR('❌',
|
|
773
|
+
? _HDR('❌', t(_getLang(), 'installationFailed'))
|
|
731
774
|
: _installComplete
|
|
732
|
-
? _HDR('✅',
|
|
733
|
-
: _HDR('⚙️',
|
|
775
|
+
? _HDR('✅', t(_getLang(), 'installComplete'))
|
|
776
|
+
: _HDR('⚙️', t(_getLang(), 'installing'));
|
|
734
777
|
|
|
735
778
|
const hint = (_installComplete || _installError)
|
|
736
|
-
?
|
|
737
|
-
:
|
|
779
|
+
? ` ${t(_getLang(), 'screen5HintDone')}`
|
|
780
|
+
: ` ${t(_getLang(), 'screen5HintWait')}`;
|
|
738
781
|
|
|
739
782
|
// Show last 18 log lines so content fits in the box
|
|
740
783
|
const MAX_LINES = 18;
|
|
@@ -778,7 +821,7 @@ export function createInstallTab(screen, services) {
|
|
|
778
821
|
_completionModalBox = null;
|
|
779
822
|
}
|
|
780
823
|
_completionModalOpen = false;
|
|
781
|
-
_screen =
|
|
824
|
+
_screen = 0;
|
|
782
825
|
box.hide();
|
|
783
826
|
_showInstallNotice('Installation Complete — Settings Saved');
|
|
784
827
|
screen.render();
|
|
@@ -793,14 +836,18 @@ export function createInstallTab(screen, services) {
|
|
|
793
836
|
_s1BeginBtn.hide(); _s1ExitBtn.hide();
|
|
794
837
|
}
|
|
795
838
|
|
|
839
|
+
// Screen 0 has no button widgets — nav is handled via key handlers
|
|
840
|
+
|
|
796
841
|
// Screen 2 continue button: hidden on other screens; _renderScreen2 manages show/focus
|
|
797
842
|
if (_screen !== 2) _s2ContinueBtn.hide();
|
|
798
843
|
|
|
799
|
-
// Screen 5
|
|
844
|
+
// Screen 5 buttons: hidden during active install, shown by _runInstall() on completion
|
|
800
845
|
if (_screen === 5 && (_installComplete || _installError)) {
|
|
801
|
-
|
|
846
|
+
_s5QuitBtn.show();
|
|
847
|
+
_s5CustomizeBtn.show();
|
|
802
848
|
} else {
|
|
803
|
-
|
|
849
|
+
_s5QuitBtn.hide();
|
|
850
|
+
_s5CustomizeBtn.hide();
|
|
804
851
|
}
|
|
805
852
|
|
|
806
853
|
// Show Screen 4 action buttons only on screen 4
|
|
@@ -842,6 +889,7 @@ export function createInstallTab(screen, services) {
|
|
|
842
889
|
setTimeout(() => {
|
|
843
890
|
if (_screen !== targetScreen) return;
|
|
844
891
|
switch (_screen) {
|
|
892
|
+
case 0: _renderScreen0(); break;
|
|
845
893
|
case 1: _renderScreen1(); break;
|
|
846
894
|
case 2: _renderScreen2(); break;
|
|
847
895
|
case 3: _renderScreen3(); break;
|
|
@@ -852,6 +900,7 @@ export function createInstallTab(screen, services) {
|
|
|
852
900
|
return;
|
|
853
901
|
}
|
|
854
902
|
switch (_screen) {
|
|
903
|
+
case 0: _renderScreen0(); break;
|
|
855
904
|
case 1: _renderScreen1(); break;
|
|
856
905
|
case 2: _renderScreen2(); break;
|
|
857
906
|
case 3: _renderScreen3(); break;
|
|
@@ -870,6 +919,12 @@ export function createInstallTab(screen, services) {
|
|
|
870
919
|
screen.key(['enter'], () => {
|
|
871
920
|
if (box.hidden || _checking) return;
|
|
872
921
|
if (_completionModalOpen) { _dismissCompletionModal(); return; } // always first
|
|
922
|
+
if (_screen === 0) { // Screen 0: apply selected language and advance
|
|
923
|
+
if (languageService) languageService.setLang(SUPPORTED_LANGUAGES[_langIdx].value);
|
|
924
|
+
_screen = 1;
|
|
925
|
+
_showCurrentScreen();
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
873
928
|
if (_screen === 1) return; // Screen 1: Enter handled by Begin/Exit buttons
|
|
874
929
|
if (_screen === 2) return; // Screen 2: Enter handled by Continue button
|
|
875
930
|
if (_screen === 4) return; // Screen 4: Enter handled by the focused button
|
|
@@ -883,7 +938,7 @@ export function createInstallTab(screen, services) {
|
|
|
883
938
|
screen.key(['escape'], () => {
|
|
884
939
|
if (box.hidden || _checking) return;
|
|
885
940
|
if (_completionModalOpen) { _dismissCompletionModal(); return; }
|
|
886
|
-
if (_screen >
|
|
941
|
+
if (_screen > 0) {
|
|
887
942
|
_screen--;
|
|
888
943
|
_showCurrentScreen();
|
|
889
944
|
} else {
|
|
@@ -899,6 +954,11 @@ export function createInstallTab(screen, services) {
|
|
|
899
954
|
|
|
900
955
|
screen.key(['up'], () => {
|
|
901
956
|
if (box.hidden) return;
|
|
957
|
+
if (_screen === 0) {
|
|
958
|
+
_langIdx = Math.max(0, _langIdx - 1);
|
|
959
|
+
_renderScreen0();
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
902
962
|
if (_screen === 3 && _deps) {
|
|
903
963
|
const providers = [];
|
|
904
964
|
if (_deps.piper) providers.push('piper');
|
|
@@ -914,16 +974,23 @@ export function createInstallTab(screen, services) {
|
|
|
914
974
|
screen.key(['left'], () => {
|
|
915
975
|
if (box.hidden || _checking) return;
|
|
916
976
|
if (_screen === 4) return;
|
|
917
|
-
if (_screen >
|
|
977
|
+
if (_screen > 0) {
|
|
918
978
|
_screen--;
|
|
919
979
|
_showCurrentScreen();
|
|
920
980
|
}
|
|
921
981
|
});
|
|
922
982
|
|
|
923
983
|
// Right arrow = go forward (same logic as Enter, without save/finish side-effects)
|
|
984
|
+
// Screen 0: → skips language selection (keeps English)
|
|
924
985
|
// Screen 1: right arrow handled by button ←/→ navigation
|
|
925
986
|
screen.key(['right'], () => {
|
|
926
987
|
if (box.hidden || _checking) return;
|
|
988
|
+
if (_screen === 0) { // → skips: keep current _lang (default 'en') and advance
|
|
989
|
+
if (languageService) languageService.setLang(SUPPORTED_LANGUAGES[_langIdx].value);
|
|
990
|
+
_screen = 1;
|
|
991
|
+
_showCurrentScreen();
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
927
994
|
if (_screen === 1) return;
|
|
928
995
|
if (_screen === 2) return; // Screen 2: → handled by Continue button
|
|
929
996
|
if (_screen === 3) { _screen++; _showCurrentScreen(); return; } // → confirms provider and advances
|
|
@@ -931,10 +998,15 @@ export function createInstallTab(screen, services) {
|
|
|
931
998
|
if (_screen === 5) return; // Screen 5: → handled by button nav
|
|
932
999
|
});
|
|
933
1000
|
|
|
934
|
-
// Down arrow: Screen 3 provider nav; Screen 1 ↓ is handled by button key handlers
|
|
1001
|
+
// Down arrow: Screen 0 language nav; Screen 3 provider nav; Screen 1 ↓ is handled by button key handlers
|
|
935
1002
|
// (tab bar's el.key(['down']) → onFocus() focuses Begin, then button ↓ → Exit)
|
|
936
1003
|
screen.key(['down'], () => {
|
|
937
1004
|
if (box.hidden) return;
|
|
1005
|
+
if (_screen === 0) {
|
|
1006
|
+
_langIdx = Math.min(SUPPORTED_LANGUAGES.length - 1, _langIdx + 1);
|
|
1007
|
+
_renderScreen0();
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
938
1010
|
if (_screen === 3 && _deps) {
|
|
939
1011
|
const providers = [];
|
|
940
1012
|
if (_deps.piper) providers.push('piper');
|
|
@@ -961,7 +1033,9 @@ export function createInstallTab(screen, services) {
|
|
|
961
1033
|
box,
|
|
962
1034
|
|
|
963
1035
|
show() {
|
|
964
|
-
_screen =
|
|
1036
|
+
_screen = 0;
|
|
1037
|
+
_langIdx = 0;
|
|
1038
|
+
// _lang now lives in languageService — don't reset it here
|
|
965
1039
|
_screen5Announced = false;
|
|
966
1040
|
_installLog = [];
|
|
967
1041
|
_installRunning = false;
|
|
@@ -982,12 +1056,14 @@ export function createInstallTab(screen, services) {
|
|
|
982
1056
|
|
|
983
1057
|
onFocus() {
|
|
984
1058
|
// Focus the active interactive element, not just the box container
|
|
985
|
-
if (_screen ===
|
|
1059
|
+
if (_screen === 0) {
|
|
1060
|
+
box.focus(); // Screen 0 uses key handlers, no button widgets
|
|
1061
|
+
} else if (_screen === 1) {
|
|
986
1062
|
_s1BeginBtn.focus();
|
|
987
1063
|
} else if (_screen === 4) {
|
|
988
1064
|
_editBtn.focus();
|
|
989
1065
|
} else if (_screen === 5 && (_installComplete || _installError)) {
|
|
990
|
-
|
|
1066
|
+
_s5QuitBtn.focus();
|
|
991
1067
|
} else {
|
|
992
1068
|
box.focus();
|
|
993
1069
|
}
|
|
@@ -997,7 +1073,7 @@ export function createInstallTab(screen, services) {
|
|
|
997
1073
|
onBlur() {},
|
|
998
1074
|
|
|
999
1075
|
getFooterText() {
|
|
1000
|
-
return
|
|
1076
|
+
return _tl('footerText');
|
|
1001
1077
|
},
|
|
1002
1078
|
|
|
1003
1079
|
getFooterColor() {
|