qwenproxy-cli 1.6.1 → 1.6.3

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.
@@ -59,11 +59,17 @@ export class AccountsView implements TuiView {
59
59
  private isAddModalOpen = false;
60
60
  private isBatchModalOpen = false;
61
61
  private isValidatingAccount = false;
62
+ private isOtpRequesting = false;
63
+ private isOtpCodeSent = false;
64
+ private addLoginMode: "password" | "otp" = "password";
65
+ private otpSessionId: string | null = null;
62
66
  private addEmailInput = "";
63
67
  private addPasswordInput = "";
68
+ private addOtpCodeInput = "";
64
69
  private addEmailCursor = 0;
65
70
  private addPasswordCursor = 0;
66
- private addActiveField: "email" | "password" = "email";
71
+ private addOtpCodeCursor = 0;
72
+ private addActiveField: "email" | "password" | "code" = "email";
67
73
  private batchInput = "";
68
74
  private batchCursor = 0;
69
75
  private batchHoveredButton: "import" | "cancel" | null = null;
@@ -72,7 +78,7 @@ export class AccountsView implements TuiView {
72
78
  private lastBatchModalStartRow = 4;
73
79
  private hoveredActionRow: number | null = null;
74
80
  private hoveredAccountIndex: number | null = null;
75
- private modalHoveredField: "email" | "password" | "save" | "cancel" | null = null;
81
+ private modalHoveredField: "email" | "password" | "code" | "save" | "cancel" | "mode" | null = null;
76
82
  private lastModalLeftPad = 0;
77
83
  private lastLeftW = 46;
78
84
  private accountsScrollOffset = 0;
@@ -149,11 +155,116 @@ export class AccountsView implements TuiView {
149
155
  }, 4000);
150
156
  }
151
157
 
158
+ private resetAddModalState(): void {
159
+ if (this.otpSessionId && !process.env.NODE_TEST_CONTEXT) {
160
+ const sid = this.otpSessionId;
161
+ import("../../services/playwright.ts")
162
+ .then(({ cancelEmailOtpLogin }) => cancelEmailOtpLogin(sid))
163
+ .catch(() => {});
164
+ }
165
+ this.isAddModalOpen = false;
166
+ this.isValidatingAccount = false;
167
+ this.isOtpRequesting = false;
168
+ this.isOtpCodeSent = false;
169
+ this.otpSessionId = null;
170
+ this.addEmailInput = "";
171
+ this.addPasswordInput = "";
172
+ this.addOtpCodeInput = "";
173
+ this.addEmailCursor = 0;
174
+ this.addPasswordCursor = 0;
175
+ this.addOtpCodeCursor = 0;
176
+ this.addActiveField = "email";
177
+ this.addLoginMode = "password";
178
+ }
179
+
152
180
  private async saveModalAccount(): Promise<void> {
153
- if (this.isValidatingAccount) return;
181
+ if (this.isValidatingAccount || this.isOtpRequesting) return;
154
182
  const email = this.addEmailInput.trim();
183
+ if (!email) {
184
+ this.setStatusMessage(theme.yellow("[!] E-mail é obrigatório"));
185
+ return;
186
+ }
187
+
188
+ if (this.addLoginMode === "otp") {
189
+ if (!this.isOtpCodeSent) {
190
+ if (process.env.NODE_TEST_CONTEXT) {
191
+ this.isOtpCodeSent = true;
192
+ this.addActiveField = "code";
193
+ this.otpSessionId = "mock-otp-" + Date.now();
194
+ this.setStatusMessage(theme.green("✓ Código enviado para o e-mail!"));
195
+ return;
196
+ }
197
+
198
+ this.isOtpRequesting = true;
199
+ this.setStatusMessage(theme.yellow(`[...] Enviando código OTP para ${email}...`));
200
+
201
+ try {
202
+ const { startEmailOtpLogin } = await import("../../services/playwright.ts");
203
+ const res = await startEmailOtpLogin(email, { headless: config.playwright.headless });
204
+ this.isOtpRequesting = false;
205
+ if (!res.success || !res.sessionId) {
206
+ this.setStatusMessage(theme.red(`✗ Falha ao enviar código: ${res.error || "Erro desconhecido"}`));
207
+ return;
208
+ }
209
+ this.otpSessionId = res.sessionId;
210
+ this.isOtpCodeSent = true;
211
+ this.addActiveField = "code";
212
+ this.setStatusMessage(theme.green("✓ Código enviado! Digite os 6 dígitos abaixo."));
213
+ } catch (err: any) {
214
+ this.isOtpRequesting = false;
215
+ this.setStatusMessage(theme.red(`✗ Erro: ${err?.message || String(err)}`));
216
+ }
217
+ return;
218
+ }
219
+
220
+ const code = this.addOtpCodeInput.trim();
221
+ if (!code) {
222
+ this.setStatusMessage(theme.yellow("[!] Digite o código de 6 dígitos recebido por e-mail"));
223
+ return;
224
+ }
225
+
226
+ if (process.env.NODE_TEST_CONTEXT) {
227
+ try {
228
+ const newAcc = addAccount(email, "");
229
+ this.resetAddModalState();
230
+ await this.refresh();
231
+ this.setStatusMessage(theme.green(`✓ Conta ${email} salva via código OTP!`));
232
+ } catch (err: any) {
233
+ this.setStatusMessage(theme.red(`✗ Erro ao salvar: ${err?.message || String(err)}`));
234
+ }
235
+ return;
236
+ }
237
+
238
+ this.isValidatingAccount = true;
239
+ this.setStatusMessage(theme.yellow("[...] Validando código e capturando sessão de 30 dias..."));
240
+ try {
241
+ const { verifyEmailOtpLogin } = await import("../../services/playwright.ts");
242
+ const res = await verifyEmailOtpLogin(this.otpSessionId!, code);
243
+ this.isValidatingAccount = false;
244
+ if (!res.success || !res.account) {
245
+ this.setStatusMessage(theme.red(`✗ Código inválido: ${res.error || "Verificação falhou"}`));
246
+ return;
247
+ }
248
+ this.resetAddModalState();
249
+ await this.refresh();
250
+ this.setStatusMessage(theme.green(`✓ Conta ${res.account.email} autenticada via código OTP!`));
251
+
252
+ if (process.stdout.isTTY) {
253
+ const sManager = ServerManager.getInstance();
254
+ const sState = sManager.getState();
255
+ if (sState !== "online" && sState !== "warming") {
256
+ void sManager.ensureStarted().then(() => this.refresh());
257
+ }
258
+ }
259
+ } catch (err: any) {
260
+ this.isValidatingAccount = false;
261
+ this.setStatusMessage(theme.red(`✗ Erro de verificação: ${err?.message || String(err)}`));
262
+ }
263
+ return;
264
+ }
265
+
155
266
  const password = this.addPasswordInput.trim();
156
- if (!email || !password) {
267
+ if (!password) {
157
268
  this.setStatusMessage(theme.yellow("[!] E-mail e senha são obrigatórios"));
158
269
  return;
159
270
  }
@@ -161,11 +272,7 @@ export class AccountsView implements TuiView {
161
272
  if (process.env.NODE_TEST_CONTEXT) {
162
273
  try {
163
274
  const newAcc = addAccount(email, password);
164
- this.isAddModalOpen = false;
165
- this.addEmailInput = "";
166
- this.addPasswordInput = "";
167
- this.addEmailCursor = 0;
168
- this.addPasswordCursor = 0;
275
+ this.resetAddModalState();
169
276
  await this.refresh();
170
277
  this.setStatusMessage(theme.green(`✓ Conta ${email} salva! Conectando...`));
171
278
  } catch (err: any) {
@@ -197,11 +304,7 @@ export class AccountsView implements TuiView {
197
304
  }
198
305
 
199
306
  const newAcc = addAccount(email, password, tempId);
200
- this.isAddModalOpen = false;
201
- this.addEmailInput = "";
202
- this.addPasswordInput = "";
203
- this.addEmailCursor = 0;
204
- this.addPasswordCursor = 0;
307
+ this.resetAddModalState();
205
308
  await this.refresh();
206
309
  this.setStatusMessage(theme.green(`✓ Conta ${email} validada e salva! Conectando...`));
207
310
 
@@ -492,31 +595,46 @@ export class AccountsView implements TuiView {
492
595
 
493
596
  // 1. Add Account Modal Active
494
597
  if (this.isAddModalOpen) {
495
- if (this.isValidatingAccount) return true;
598
+ if (this.isValidatingAccount || this.isOtpRequesting) return true;
496
599
  if (key.name === "escape") {
497
- this.isAddModalOpen = false;
498
- this.addEmailInput = "";
499
- this.addPasswordInput = "";
600
+ this.resetAddModalState();
500
601
  return true;
501
602
  }
502
603
 
604
+ // Switch mode with Tab (when code has not been sent yet)
605
+ if (key.name === "tab") {
606
+ if (!this.isOtpCodeSent) {
607
+ this.addLoginMode = this.addLoginMode === "password" ? "otp" : "password";
608
+ this.addActiveField = "email";
609
+ this.setStatusMessage(
610
+ theme.cyan(
611
+ `Modo alterado para: ${
612
+ this.addLoginMode === "password" ? "Senha" : "Código E-mail (Sem Senha)"
613
+ }`,
614
+ ),
615
+ );
616
+ return true;
617
+ }
618
+ }
619
+
503
620
  // Mouse hover in Add Account modal
504
621
  if (key.name === "hover" && key.mouse) {
505
622
  const { row, col } = key.mouse;
506
623
  const leftPad = this.lastModalLeftPad || 0;
507
624
  const relCol = col - leftPad;
508
- if (row === 5 || row === 6) {
625
+ if (row === 6) {
509
626
  if (this.modalHoveredField !== "email") {
510
627
  this.modalHoveredField = "email";
511
628
  return true;
512
629
  }
513
- } else if (row === 7 || row === 8) {
514
- if (this.modalHoveredField !== "password") {
515
- this.modalHoveredField = "password";
630
+ } else if (row === 7) {
631
+ const field = this.addLoginMode === "password" ? "password" : "code";
632
+ if (this.modalHoveredField !== field) {
633
+ this.modalHoveredField = field;
516
634
  return true;
517
635
  }
518
636
  } else if (row === 9) {
519
- const btn = relCol <= 24 ? "save" : "cancel";
637
+ const btn = relCol <= 28 ? "save" : relCol <= 49 ? "cancel" : "mode";
520
638
  if (this.modalHoveredField !== btn) {
521
639
  this.modalHoveredField = btn;
522
640
  return true;
@@ -532,34 +650,52 @@ export class AccountsView implements TuiView {
532
650
  const { row, col } = key.mouse;
533
651
  const leftPad = this.lastModalLeftPad || 0;
534
652
  const relCol = col - leftPad;
535
- // Click on email field row (rows 5 and 6)
536
- if (row === 5 || row === 6) {
653
+ // Click on email field row (row 6)
654
+ if (row === 6) {
537
655
  this.addActiveField = "email";
538
656
  return true;
539
657
  }
540
- // Click on password field row (rows 7 and 8)
541
- if (row === 7 || row === 8) {
542
- this.addActiveField = "password";
658
+ // Click on second field row (row 7)
659
+ if (row === 7) {
660
+ if (this.addLoginMode === "password") {
661
+ this.addActiveField = "password";
662
+ } else if (this.isOtpCodeSent) {
663
+ this.addActiveField = "code";
664
+ }
543
665
  return true;
544
666
  }
545
667
  // Click on buttons row (row 9)
546
668
  if (row === 9) {
547
- if (relCol <= 24) {
669
+ if (relCol <= 28) {
548
670
  await this.saveModalAccount();
549
671
  return true;
550
- } else {
551
- this.isAddModalOpen = false;
552
- this.addEmailInput = "";
553
- this.addPasswordInput = "";
554
- this.addEmailCursor = 0;
555
- this.addPasswordCursor = 0;
672
+ } else if (relCol <= 49) {
673
+ this.resetAddModalState();
556
674
  return true;
675
+ } else {
676
+ // Clicked mode toggle button
677
+ if (!this.isOtpCodeSent) {
678
+ this.addLoginMode = this.addLoginMode === "password" ? "otp" : "password";
679
+ this.addActiveField = "email";
680
+ this.setStatusMessage(
681
+ theme.cyan(
682
+ `Modo alterado para: ${
683
+ this.addLoginMode === "password" ? "Senha" : "Código E-mail (Sem Senha)"
684
+ }`,
685
+ ),
686
+ );
687
+ return true;
688
+ }
557
689
  }
558
690
  }
559
691
  }
560
692
  // Switch field with Up / Down arrow keys
561
693
  if (key.name === "up" || key.name === "down") {
562
- this.addActiveField = this.addActiveField === "email" ? "password" : "email";
694
+ if (this.addLoginMode === "password") {
695
+ this.addActiveField = this.addActiveField === "email" ? "password" : "email";
696
+ } else if (this.isOtpCodeSent) {
697
+ this.addActiveField = this.addActiveField === "email" ? "code" : "email";
698
+ }
563
699
  return true;
564
700
  }
565
701
 
@@ -567,27 +703,33 @@ export class AccountsView implements TuiView {
567
703
  if (key.name === "left") {
568
704
  if (this.addActiveField === "email") {
569
705
  this.addEmailCursor = Math.max(0, this.addEmailCursor - 1);
570
- } else {
706
+ } else if (this.addActiveField === "password") {
571
707
  this.addPasswordCursor = Math.max(0, this.addPasswordCursor - 1);
708
+ } else {
709
+ this.addOtpCodeCursor = Math.max(0, this.addOtpCodeCursor - 1);
572
710
  }
573
711
  return true;
574
712
  }
575
713
  if (key.name === "right") {
576
714
  if (this.addActiveField === "email") {
577
715
  this.addEmailCursor = Math.min(this.addEmailInput.length, this.addEmailCursor + 1);
578
- } else {
716
+ } else if (this.addActiveField === "password") {
579
717
  this.addPasswordCursor = Math.min(this.addPasswordInput.length, this.addPasswordCursor + 1);
718
+ } else {
719
+ this.addOtpCodeCursor = Math.min(this.addOtpCodeInput.length, this.addOtpCodeCursor + 1);
580
720
  }
581
721
  return true;
582
722
  }
583
723
  if (key.name === "home") {
584
724
  if (this.addActiveField === "email") this.addEmailCursor = 0;
585
- else this.addPasswordCursor = 0;
725
+ else if (this.addActiveField === "password") this.addPasswordCursor = 0;
726
+ else this.addOtpCodeCursor = 0;
586
727
  return true;
587
728
  }
588
729
  if (key.name === "end") {
589
730
  if (this.addActiveField === "email") this.addEmailCursor = this.addEmailInput.length;
590
- else this.addPasswordCursor = this.addPasswordInput.length;
731
+ else if (this.addActiveField === "password") this.addPasswordCursor = this.addPasswordInput.length;
732
+ else this.addOtpCodeCursor = this.addOtpCodeInput.length;
591
733
  return true;
592
734
  }
593
735
 
@@ -601,12 +743,18 @@ export class AccountsView implements TuiView {
601
743
  pasted +
602
744
  this.addEmailInput.slice(this.addEmailCursor);
603
745
  this.addEmailCursor += pasted.length;
604
- } else {
746
+ } else if (this.addActiveField === "password") {
605
747
  this.addPasswordInput =
606
748
  this.addPasswordInput.slice(0, this.addPasswordCursor) +
607
749
  pasted +
608
750
  this.addPasswordInput.slice(this.addPasswordCursor);
609
751
  this.addPasswordCursor += pasted.length;
752
+ } else {
753
+ this.addOtpCodeInput =
754
+ this.addOtpCodeInput.slice(0, this.addOtpCodeCursor) +
755
+ pasted +
756
+ this.addOtpCodeInput.slice(this.addOtpCodeCursor);
757
+ this.addOtpCodeCursor += pasted.length;
610
758
  }
611
759
  return true;
612
760
  }
@@ -616,9 +764,12 @@ export class AccountsView implements TuiView {
616
764
  if (this.addActiveField === "email") {
617
765
  this.addEmailInput = "";
618
766
  this.addEmailCursor = 0;
619
- } else {
767
+ } else if (this.addActiveField === "password") {
620
768
  this.addPasswordInput = "";
621
769
  this.addPasswordCursor = 0;
770
+ } else {
771
+ this.addOtpCodeInput = "";
772
+ this.addOtpCodeCursor = 0;
622
773
  }
623
774
  return true;
624
775
  }
@@ -632,13 +783,20 @@ export class AccountsView implements TuiView {
632
783
  this.addEmailInput.slice(this.addEmailCursor);
633
784
  this.addEmailCursor--;
634
785
  }
635
- } else {
786
+ } else if (this.addActiveField === "password") {
636
787
  if (this.addPasswordCursor > 0) {
637
788
  this.addPasswordInput =
638
789
  this.addPasswordInput.slice(0, this.addPasswordCursor - 1) +
639
790
  this.addPasswordInput.slice(this.addPasswordCursor);
640
791
  this.addPasswordCursor--;
641
792
  }
793
+ } else {
794
+ if (this.addOtpCodeCursor > 0) {
795
+ this.addOtpCodeInput =
796
+ this.addOtpCodeInput.slice(0, this.addOtpCodeCursor - 1) +
797
+ this.addOtpCodeInput.slice(this.addOtpCodeCursor);
798
+ this.addOtpCodeCursor--;
799
+ }
642
800
  }
643
801
  return true;
644
802
  }
@@ -651,12 +809,18 @@ export class AccountsView implements TuiView {
651
809
  this.addEmailInput.slice(0, this.addEmailCursor) +
652
810
  this.addEmailInput.slice(this.addEmailCursor + 1);
653
811
  }
654
- } else {
812
+ } else if (this.addActiveField === "password") {
655
813
  if (this.addPasswordCursor < this.addPasswordInput.length) {
656
814
  this.addPasswordInput =
657
815
  this.addPasswordInput.slice(0, this.addPasswordCursor) +
658
816
  this.addPasswordInput.slice(this.addPasswordCursor + 1);
659
817
  }
818
+ } else {
819
+ if (this.addOtpCodeCursor < this.addOtpCodeInput.length) {
820
+ this.addOtpCodeInput =
821
+ this.addOtpCodeInput.slice(0, this.addOtpCodeCursor) +
822
+ this.addOtpCodeInput.slice(this.addOtpCodeCursor + 1);
823
+ }
660
824
  }
661
825
  return true;
662
826
  }
@@ -666,7 +830,6 @@ export class AccountsView implements TuiView {
666
830
  await this.saveModalAccount();
667
831
  return true;
668
832
  }
669
- // Type character into active field
670
833
  // Type character into active field at cursor position
671
834
  if (key.char && !key.ctrl && !key.meta && key.name !== "tab") {
672
835
  if (key.char >= " ") {
@@ -676,12 +839,20 @@ export class AccountsView implements TuiView {
676
839
  key.char +
677
840
  this.addEmailInput.slice(this.addEmailCursor);
678
841
  this.addEmailCursor += key.char.length;
679
- } else {
842
+ } else if (this.addActiveField === "password") {
680
843
  this.addPasswordInput =
681
844
  this.addPasswordInput.slice(0, this.addPasswordCursor) +
682
845
  key.char +
683
846
  this.addPasswordInput.slice(this.addPasswordCursor);
684
847
  this.addPasswordCursor += key.char.length;
848
+ } else if (this.addActiveField === "code") {
849
+ if (this.addOtpCodeInput.length < 10) {
850
+ this.addOtpCodeInput =
851
+ this.addOtpCodeInput.slice(0, this.addOtpCodeCursor) +
852
+ key.char +
853
+ this.addOtpCodeInput.slice(this.addOtpCodeCursor);
854
+ this.addOtpCodeCursor += key.char.length;
855
+ }
685
856
  }
686
857
  return true;
687
858
  }
@@ -808,15 +979,15 @@ export class AccountsView implements TuiView {
808
979
  return true;
809
980
  }
810
981
 
811
- // Right panel action buttons hover (rows 15 to 20)
982
+ // Right panel action buttons hover (rows 16 to 21)
812
983
  if (col >= leftW) {
813
- if (row === 15) {
814
- const actionRow = col < leftW + 18 ? 15 : 21;
984
+ if (row === 16) {
985
+ const actionRow = col < leftW + 18 ? 16 : 22;
815
986
  if (this.hoveredActionRow !== actionRow) {
816
987
  this.hoveredActionRow = actionRow;
817
988
  return true;
818
989
  }
819
- } else if (row >= 16 && row <= 20) {
990
+ } else if (row >= 17 && row <= 21) {
820
991
  if (this.hoveredActionRow !== row) {
821
992
  this.hoveredActionRow = row;
822
993
  return true;
@@ -844,9 +1015,9 @@ export class AccountsView implements TuiView {
844
1015
  return true;
845
1016
  }
846
1017
  }
847
- // Right panel action buttons click (rows 15, 16, 17, 18, 19, 20)
1018
+ // Right panel action buttons click (rows 16 to 21)
848
1019
  if (col >= leftW) {
849
- if (row === 15) {
1020
+ if (row === 16) {
850
1021
  if (col < leftW + 18) {
851
1022
  await this.handleKey({ name: "a", ctrl: false, shift: false, meta: false });
852
1023
  } else {
@@ -854,23 +1025,23 @@ export class AccountsView implements TuiView {
854
1025
  }
855
1026
  return true;
856
1027
  }
857
- if (row === 16) {
1028
+ if (row === 17) {
858
1029
  await this.handleKey({ name: "d", ctrl: false, shift: false, meta: false });
859
1030
  return true;
860
1031
  }
861
- if (row === 17) {
1032
+ if (row === 18) {
862
1033
  await this.handleKey({ name: "c", ctrl: false, shift: false, meta: false });
863
1034
  return true;
864
1035
  }
865
- if (row === 18) {
1036
+ if (row === 19) {
866
1037
  await this.handleKey({ name: "z", ctrl: false, shift: false, meta: false });
867
1038
  return true;
868
1039
  }
869
- if (row === 19) {
1040
+ if (row === 20) {
870
1041
  await this.handleKey({ name: "x", ctrl: false, shift: false, meta: false });
871
1042
  return true;
872
1043
  }
873
- if (row === 20) {
1044
+ if (row === 21) {
874
1045
  await this.handleKey({ name: "l", ctrl: false, shift: false, meta: false });
875
1046
  return true;
876
1047
  }
@@ -1046,9 +1217,10 @@ export class AccountsView implements TuiView {
1046
1217
  rightContent.push("");
1047
1218
  rightContent.push("");
1048
1219
  rightContent.push("");
1220
+ rightContent.push("");
1049
1221
  rightContent.push(` ${theme.dim("─────────────────────────────────")}`);
1050
- const btnA = this.hoveredActionRow === 15 ? theme.bgHover(` ${theme.cyan("[ A ] Adicionar")} `) : `${theme.cyan("[ A ]")} Adicionar`;
1051
- const btnB = this.hoveredActionRow === 21 ? theme.bgHover(` ${theme.cyan("[ B ] Em Lote")} `) : `${theme.cyan("[ B ]")} Em Lote`;
1222
+ const btnA = this.hoveredActionRow === 16 ? theme.bgHover(` ${theme.cyan("[ A ] Adicionar")} `) : ` ${theme.cyan("[ A ]")} Adicionar `;
1223
+ const btnB = this.hoveredActionRow === 22 ? theme.bgHover(` ${theme.cyan("[ B ] Em Lote")} `) : ` ${theme.cyan("[ B ]")} Em Lote `;
1052
1224
  rightContent.push(` ${btnA} ${btnB}`);
1053
1225
  } else {
1054
1226
  const email = truncate(selected.emailOrName, 18);
@@ -1082,14 +1254,14 @@ export class AccountsView implements TuiView {
1082
1254
  rightContent.push("");
1083
1255
  }
1084
1256
  rightContent.push(` ${theme.dim("─────────────────────────────────")}`);
1085
- const btnA = this.hoveredActionRow === 15 ? theme.bgHover(` ${theme.cyan("[ A ] Adicionar")} `) : `${theme.cyan("[ A ]")} Adicionar`;
1086
- const btnB = this.hoveredActionRow === 21 ? theme.bgHover(` ${theme.cyan("[ B ] Em Lote")} `) : `${theme.cyan("[ B ]")} Em Lote`;
1257
+ const btnA = this.hoveredActionRow === 16 ? theme.bgHover(` ${theme.cyan("[ A ] Adicionar")} `) : ` ${theme.cyan("[ A ]")} Adicionar `;
1258
+ const btnB = this.hoveredActionRow === 22 ? theme.bgHover(` ${theme.cyan("[ B ] Em Lote")} `) : ` ${theme.cyan("[ B ]")} Em Lote `;
1087
1259
  rightContent.push(` ${btnA} ${btnB}`);
1088
- rightContent.push(` ${this.hoveredActionRow === 16 ? theme.bgHover(` ${theme.red("[ D ] Remover Conta")} `) : `${theme.red("[ D ]")} Remover Conta`}`);
1089
- rightContent.push(` ${this.hoveredActionRow === 17 ? theme.bgHover(` ${theme.yellow("[ C ] Zerar Cooldown")} `) : `${theme.yellow("[ C ]")} Zerar Cooldown`}`);
1090
- rightContent.push(` ${this.hoveredActionRow === 18 ? theme.bgHover(` ${theme.green("[ Z ] Zerar Todas")} `) : `${theme.green("[ Z ]")} Zerar Todas`}`);
1091
- rightContent.push(` ${this.hoveredActionRow === 19 ? theme.bgHover(` ${theme.peach("[ X ] Limpar Chats (Conta)")} `) : `${theme.peach("[ X ]")} Limpar Chats (Conta)`}`);
1092
- rightContent.push(` ${this.hoveredActionRow === 20 ? theme.bgHover(` ${theme.red("[ L ] Limpar Todos os Chats")} `) : `${theme.red("[ L ]")} Limpar Todos os Chats`}`);
1260
+ rightContent.push(` ${this.hoveredActionRow === 17 ? theme.bgHover(` ${theme.red("[ D ] Remover Conta")} `) : ` ${theme.red("[ D ]")} Remover Conta `}`);
1261
+ rightContent.push(` ${this.hoveredActionRow === 18 ? theme.bgHover(` ${theme.yellow("[ C ] Zerar Cooldown")} `) : ` ${theme.yellow("[ C ]")} Zerar Cooldown `}`);
1262
+ rightContent.push(` ${this.hoveredActionRow === 19 ? theme.bgHover(` ${theme.green("[ Z ] Zerar Todas")} `) : ` ${theme.green("[ Z ]")} Zerar Todas `}`);
1263
+ rightContent.push(` ${this.hoveredActionRow === 20 ? theme.bgHover(` ${theme.peach("[ X ] Limpar Chats (Conta)")} `) : ` ${theme.peach("[ X ]")} Limpar Chats (Conta) `}`);
1264
+ rightContent.push(` ${this.hoveredActionRow === 21 ? theme.bgHover(` ${theme.red("[ L ] Limpar Todos os Chats")} `) : ` ${theme.red("[ L ]")} Limpar Todos os Chats `}`);
1093
1265
  }
1094
1266
  const rightBox = drawBox({
1095
1267
  title: "Inspeção de Conta",
@@ -1182,16 +1354,14 @@ export class AccountsView implements TuiView {
1182
1354
  return modalBox.map((line) => padStr + line);
1183
1355
  }
1184
1356
  if (this.isAddModalOpen) {
1185
- const modalW = Math.min(width - 4, 66);
1357
+ const modalW = Math.min(width - 4, 76);
1186
1358
  this.lastModalLeftPad = Math.max(0, Math.floor((width - modalW) / 2));
1187
1359
 
1188
1360
  const isEmail = this.addActiveField === "email";
1189
1361
  const isPass = this.addActiveField === "password";
1362
+ const isCode = this.addActiveField === "code";
1190
1363
 
1191
- const emailHover = !isEmail && this.modalHoveredField === "email";
1192
- const passHover = !isPass && this.modalHoveredField === "password";
1193
-
1194
- // Render Email field with clean cursor
1364
+ // Render Email field with clean cursor (no white background hover)
1195
1365
  let emailDisplay: string;
1196
1366
  if (isEmail) {
1197
1367
  if (this.addEmailInput.length === 0) {
@@ -1204,47 +1374,94 @@ export class AccountsView implements TuiView {
1204
1374
  }
1205
1375
  } else {
1206
1376
  emailDisplay = this.addEmailInput
1207
- ? (emailHover ? theme.bgHover(` ${this.addEmailInput} `) : theme.cyan(` ${this.addEmailInput} `))
1208
- : (emailHover ? theme.bgHover(" (digite o e-mail) ") : theme.muted(" (digite o e-mail) "));
1377
+ ? theme.cyan(this.addEmailInput)
1378
+ : theme.muted("(digite o e-mail)");
1209
1379
  }
1210
1380
 
1211
- // Render Password field with clean cursor
1212
- let passDisplay: string;
1213
- const maskedPass = "•".repeat(this.addPasswordInput.length);
1214
- if (isPass) {
1215
- if (this.addPasswordInput.length === 0) {
1216
- passDisplay = `${theme.inverse(" ")} ${theme.dim("(digite a senha)")}`;
1381
+ // Render Password or Code field with clean cursor
1382
+ let secondFieldLabel: string;
1383
+ let secondFieldDisplay: string;
1384
+
1385
+ if (this.addLoginMode === "password") {
1386
+ secondFieldLabel = "Senha:";
1387
+ const maskedPass = "•".repeat(this.addPasswordInput.length);
1388
+ if (isPass) {
1389
+ if (this.addPasswordInput.length === 0) {
1390
+ secondFieldDisplay = `${theme.inverse(" ")} ${theme.dim("(digite a senha)")}`;
1391
+ } else {
1392
+ const before = maskedPass.slice(0, this.addPasswordCursor);
1393
+ const at = maskedPass[this.addPasswordCursor] || " ";
1394
+ const after = maskedPass.slice(this.addPasswordCursor + 1);
1395
+ secondFieldDisplay = `${theme.cyan(before)}${theme.inverse(at)}${theme.cyan(after)}`;
1396
+ }
1217
1397
  } else {
1218
- const before = maskedPass.slice(0, this.addPasswordCursor);
1219
- const at = maskedPass[this.addPasswordCursor] || " ";
1220
- const after = maskedPass.slice(this.addPasswordCursor + 1);
1221
- passDisplay = `${theme.cyan(before)}${theme.inverse(at)}${theme.cyan(after)}`;
1398
+ secondFieldDisplay = this.addPasswordInput
1399
+ ? theme.cyan(maskedPass)
1400
+ : theme.muted("(digite a senha)");
1222
1401
  }
1223
1402
  } else {
1224
- passDisplay = this.addPasswordInput
1225
- ? (passHover ? theme.bgHover(` ${maskedPass} `) : theme.cyan(` ${maskedPass} `))
1226
- : (passHover ? theme.bgHover(" (digite a senha) ") : theme.muted(" (digite a senha) "));
1403
+ secondFieldLabel = "Código:";
1404
+ if (!this.isOtpCodeSent) {
1405
+ secondFieldDisplay = theme.dim("(será enviado p/ seu e-mail)");
1406
+ } else if (isCode) {
1407
+ if (this.addOtpCodeInput.length === 0) {
1408
+ secondFieldDisplay = `${theme.inverse(" ")} ${theme.dim("(digite o código de 6 dígitos)")}`;
1409
+ } else {
1410
+ const before = this.addOtpCodeInput.slice(0, this.addOtpCodeCursor);
1411
+ const at = this.addOtpCodeInput[this.addOtpCodeCursor] || " ";
1412
+ const after = this.addOtpCodeInput.slice(this.addOtpCodeCursor + 1);
1413
+ secondFieldDisplay = `${theme.cyan(before)}${theme.inverse(at)}${theme.cyan(after)}`;
1414
+ }
1415
+ } else {
1416
+ secondFieldDisplay = this.addOtpCodeInput
1417
+ ? theme.cyan(this.addOtpCodeInput)
1418
+ : theme.muted("(digite o código de 6 dígitos)");
1419
+ }
1420
+ }
1421
+
1422
+ let saveBtnText: string;
1423
+ if (this.addLoginMode === "password") {
1424
+ saveBtnText = "[ Enter ] Salvar";
1425
+ } else if (!this.isOtpCodeSent) {
1426
+ saveBtnText = "[ Enter ] Enviar Código";
1427
+ } else {
1428
+ saveBtnText = "[ Enter ] Confirmar Código";
1227
1429
  }
1228
- const saveBtn = this.isValidatingAccount
1229
- ? theme.yellow(" [ Validando... ] ")
1430
+
1431
+ const saveBtn = (this.isValidatingAccount || this.isOtpRequesting)
1432
+ ? theme.yellow(" [ Aguarde... ] ")
1230
1433
  : this.modalHoveredField === "save"
1231
- ? theme.bgHover(theme.green(" [ Enter ] Salvar "))
1232
- : theme.green("[ Enter ] Salvar");
1434
+ ? theme.bgHover(` ${saveBtnText} `)
1435
+ : ` ${theme.green(saveBtnText)} `;
1233
1436
 
1234
1437
  const cancelBtn =
1235
1438
  this.modalHoveredField === "cancel"
1236
- ? theme.bgHover(theme.red(" [ Esc ] Cancelar "))
1237
- : theme.muted("[ Esc ] Cancelar");
1439
+ ? theme.bgHover(" [ Esc ] Cancelar ")
1440
+ : ` ${theme.muted("[ Esc ] Cancelar")} `;
1441
+
1442
+ const modeBtnText = this.addLoginMode === "password"
1443
+ ? "[ Tab ] Sem Senha (OTP)"
1444
+ : "[ Tab ] Modo Senha";
1445
+
1446
+ const modeBtn =
1447
+ this.modalHoveredField === "mode"
1448
+ ? theme.bgHover(` ${modeBtnText} `)
1449
+ : ` ${theme.cyan(modeBtnText)} `;
1450
+
1238
1451
  const modalContent = [
1239
1452
  "",
1240
- ` ${theme.bold("E-mail:")} ${emailDisplay}`,
1241
- ` ${theme.bold("Senha:")} ${passDisplay}`,
1453
+ ` ${theme.bold("E-mail:")} ${emailDisplay}${this.isOtpCodeSent ? ` ${theme.green("✓ Código Enviado")}` : ""}`,
1454
+ ` ${theme.bold(secondFieldLabel)} ${secondFieldDisplay}`,
1242
1455
  "",
1243
- ` ${saveBtn} ${cancelBtn} ${theme.dim("(↑↓/mouse alternar)")}`,
1456
+ ` ${saveBtn} ${cancelBtn} ${modeBtn}`,
1244
1457
  ];
1245
1458
 
1459
+ const modalTitle = this.addLoginMode === "password"
1460
+ ? "Adicionar Nova Conta Qwen (Login com Senha)"
1461
+ : "Adicionar Nova Conta Qwen (Login sem Senha - OTP)";
1462
+
1246
1463
  const modalBox = drawBox({
1247
- title: "Adicionar Nova Conta Qwen (Login)",
1464
+ title: modalTitle,
1248
1465
  width: modalW,
1249
1466
  height: Math.min(contentH, 11),
1250
1467
  borderColor: theme.borderActive,