qwenproxy-cli 1.0.32 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.es.md +269 -0
- package/README.md +175 -782
- package/README.pt-BR.md +1033 -0
- package/package.json +4 -4
- package/src/api/server.ts +18 -2
- package/src/core/accounts.ts +130 -0
- package/src/core/config.ts +95 -4
- package/src/index.ts +3 -0
- package/src/routes/chat/context.ts +9 -9
- package/src/routes/chat/index.ts +15 -6
- package/src/services/qwen-chat-pool.ts +4 -5
- package/src/services/qwen.ts +2 -5
- package/src/tui/app.ts +8 -9
- package/src/tui/index.ts +3 -0
- package/src/tui/proxy-client.ts +3 -2
- package/src/tui/screen.ts +10 -3
- package/src/tui/settings.ts +2 -0
- package/src/tui/theme.ts +1 -0
- package/src/tui/types.ts +1 -0
- package/src/tui/views/accounts-view.ts +343 -23
- package/src/tui/views/chat-view.ts +209 -14
- package/src/tui/views/status-view.ts +34 -3
|
@@ -9,6 +9,7 @@ import { streamChatCompletions, fetchLiveModels } from "../proxy-client.ts";
|
|
|
9
9
|
import { ServerManager } from "../server-manager.ts";
|
|
10
10
|
import { formatMarkdown, formatReasoning } from "../markdown.ts";
|
|
11
11
|
import { loadTuiSettings, saveTuiSettings } from "../settings.ts";
|
|
12
|
+
import { setRuntimeChatMode, getRuntimeChatMode } from "../../core/config.ts";
|
|
12
13
|
|
|
13
14
|
interface ChatMessage {
|
|
14
15
|
role: "user" | "assistant";
|
|
@@ -59,13 +60,15 @@ export class ChatView implements TuiView {
|
|
|
59
60
|
private lastHeight = 24;
|
|
60
61
|
private lastMaxOffset = 0;
|
|
61
62
|
private lastVisibleCapacity = 0;
|
|
62
|
-
private hoveredHeaderBtn: "model" | "effort" | null = null;
|
|
63
|
+
private hoveredHeaderBtn: "model" | "effort" | "mode" | null = null;
|
|
63
64
|
private isScrollbarHovered = false;
|
|
64
65
|
private isDraggingScrollbar = false;
|
|
65
66
|
private modelBtnStartCol = 0;
|
|
66
67
|
private modelBtnEndCol = 0;
|
|
67
68
|
private effortBtnStartCol = 0;
|
|
68
69
|
private effortBtnEndCol = 0;
|
|
70
|
+
private modeBtnStartCol = 0;
|
|
71
|
+
private modeBtnEndCol = 0;
|
|
69
72
|
private isModelModalOpen = false;
|
|
70
73
|
private modalSelectedIndex = 0;
|
|
71
74
|
private availableEfforts: Array<{
|
|
@@ -96,6 +99,40 @@ export class ChatView implements TuiView {
|
|
|
96
99
|
private selectedEffort: "high" | "medium" | "low" = "high";
|
|
97
100
|
private isEffortModalOpen = false;
|
|
98
101
|
private effortSelectedIndex = 0;
|
|
102
|
+
private availableModes: Array<{
|
|
103
|
+
id: "thread" | "thread-temp" | "stateless" | "stateless-temp";
|
|
104
|
+
label: string;
|
|
105
|
+
desc: string;
|
|
106
|
+
badge: string;
|
|
107
|
+
}> = [
|
|
108
|
+
{
|
|
109
|
+
id: "thread",
|
|
110
|
+
label: "thread (Padrão)",
|
|
111
|
+
desc: "Persistente Web (Delta ~1KB, salva na conta Qwen)",
|
|
112
|
+
badge: theme.cyan("[thread]"),
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: "thread-temp",
|
|
116
|
+
label: "thread-temp",
|
|
117
|
+
desc: "Efêmero Rápido (Delta ~1KB, não salva no site)",
|
|
118
|
+
badge: theme.green("[thread-temp]"),
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: "stateless-temp",
|
|
122
|
+
label: "stateless-temp",
|
|
123
|
+
desc: "Oficial OpenAI Efêmero (Histórico total, não salva)",
|
|
124
|
+
badge: theme.yellow("[stateless-temp]"),
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: "stateless",
|
|
128
|
+
label: "stateless",
|
|
129
|
+
desc: "Oficial OpenAI Salvo (Histórico total, salva no site)",
|
|
130
|
+
badge: theme.lavender("[stateless]"),
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
private selectedChatMode: "thread" | "thread-temp" | "stateless" | "stateless-temp" = "thread";
|
|
134
|
+
private isModeModalOpen = false;
|
|
135
|
+
private modeSelectedIndex = 0;
|
|
99
136
|
private isGenerating = false;
|
|
100
137
|
private currentAbortController: AbortController | null = null;
|
|
101
138
|
private statusNote = "";
|
|
@@ -138,14 +175,26 @@ export class ChatView implements TuiView {
|
|
|
138
175
|
this.effortSelectedIndex = effIdx;
|
|
139
176
|
}
|
|
140
177
|
}
|
|
178
|
+
const runtimeMode = getRuntimeChatMode();
|
|
179
|
+
this.selectedChatMode = runtimeMode;
|
|
180
|
+
const mIdx = this.availableModes.findIndex((m) => m.id === runtimeMode);
|
|
181
|
+
if (mIdx !== -1) {
|
|
182
|
+
this.modeSelectedIndex = mIdx;
|
|
183
|
+
}
|
|
141
184
|
void this.refreshModels();
|
|
142
185
|
}
|
|
143
186
|
public onActivate(): void {
|
|
187
|
+
const runtimeMode = getRuntimeChatMode();
|
|
188
|
+
this.selectedChatMode = runtimeMode;
|
|
189
|
+
const mIdx = this.availableModes.findIndex((m) => m.id === runtimeMode);
|
|
190
|
+
if (mIdx !== -1) {
|
|
191
|
+
this.modeSelectedIndex = mIdx;
|
|
192
|
+
}
|
|
144
193
|
void this.refreshModels();
|
|
145
194
|
}
|
|
146
195
|
|
|
147
196
|
public isModalOpen(): boolean {
|
|
148
|
-
return this.isModelModalOpen || this.isEffortModalOpen;
|
|
197
|
+
return this.isModelModalOpen || this.isEffortModalOpen || this.isModeModalOpen;
|
|
149
198
|
}
|
|
150
199
|
|
|
151
200
|
public async refreshModels(): Promise<void> {
|
|
@@ -179,6 +228,12 @@ export class ChatView implements TuiView {
|
|
|
179
228
|
{ key: "Esc", label: `${glyphs.cross} Manter` },
|
|
180
229
|
];
|
|
181
230
|
}
|
|
231
|
+
if (this.isModeModalOpen) {
|
|
232
|
+
return [
|
|
233
|
+
{ key: "Enter", label: `${glyphs.enter} Confirmar` },
|
|
234
|
+
{ key: "Esc", label: `${glyphs.cross} Manter` },
|
|
235
|
+
];
|
|
236
|
+
}
|
|
182
237
|
return [
|
|
183
238
|
{ key: "Enter", label: `${glyphs.enter} Enviar` },
|
|
184
239
|
{ key: "Esc", label: `${glyphs.cross} Parar` },
|
|
@@ -195,6 +250,7 @@ export class ChatView implements TuiView {
|
|
|
195
250
|
chat: {
|
|
196
251
|
model: chosen,
|
|
197
252
|
effort: this.selectedEffort,
|
|
253
|
+
mode: this.selectedChatMode,
|
|
198
254
|
},
|
|
199
255
|
});
|
|
200
256
|
const info = classifyModel(chosen);
|
|
@@ -311,6 +367,7 @@ export class ChatView implements TuiView {
|
|
|
311
367
|
chat: {
|
|
312
368
|
model: currentM,
|
|
313
369
|
effort: this.selectedEffort,
|
|
370
|
+
mode: this.selectedChatMode,
|
|
314
371
|
},
|
|
315
372
|
});
|
|
316
373
|
this.statusNote = `Modelo: ${currentM} | Effort: ${this.availableEfforts[this.effortSelectedIndex].label}`;
|
|
@@ -325,15 +382,89 @@ export class ChatView implements TuiView {
|
|
|
325
382
|
return true;
|
|
326
383
|
}
|
|
327
384
|
|
|
328
|
-
// 3.
|
|
385
|
+
// 3. Chat Mode Selection Modal Active
|
|
386
|
+
if (this.isModeModalOpen) {
|
|
387
|
+
if (key.name === "hover" && key.mouse) {
|
|
388
|
+
const { row } = key.mouse;
|
|
389
|
+
const startRow = 11;
|
|
390
|
+
if (row >= startRow && row < startRow + this.availableModes.length) {
|
|
391
|
+
const hoverIdx = row - startRow;
|
|
392
|
+
if (this.modeSelectedIndex !== hoverIdx) {
|
|
393
|
+
this.modeSelectedIndex = hoverIdx;
|
|
394
|
+
this.onNeedsRender?.();
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (key.name === "click" && key.mouse) {
|
|
400
|
+
const { row } = key.mouse;
|
|
401
|
+
const startRow = 11;
|
|
402
|
+
if (row >= startRow && row < startRow + this.availableModes.length) {
|
|
403
|
+
this.selectedChatMode = this.availableModes[row - startRow].id;
|
|
404
|
+
this.isModeModalOpen = false;
|
|
405
|
+
setRuntimeChatMode(this.selectedChatMode);
|
|
406
|
+
saveTuiSettings({
|
|
407
|
+
chat: {
|
|
408
|
+
model: this.availableModels[this.selectedModelIndex],
|
|
409
|
+
effort: this.selectedEffort,
|
|
410
|
+
mode: this.selectedChatMode,
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
this.statusNote = theme.green(`✓ Modo global da API alterado para: ${this.selectedChatMode}`);
|
|
414
|
+
this.onNeedsRender?.();
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
this.isModeModalOpen = false;
|
|
418
|
+
this.onNeedsRender?.();
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
if (key.name === "up" || key.name === "wheelup" || (key.name === "k" && !key.ctrl)) {
|
|
422
|
+
this.modeSelectedIndex = Math.max(0, this.modeSelectedIndex - 1);
|
|
423
|
+
this.onNeedsRender?.();
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
if (key.name === "down" || key.name === "wheeldown" || (key.name === "j" && !key.ctrl)) {
|
|
427
|
+
this.modeSelectedIndex = Math.min(
|
|
428
|
+
this.availableModes.length - 1,
|
|
429
|
+
this.modeSelectedIndex + 1,
|
|
430
|
+
);
|
|
431
|
+
this.onNeedsRender?.();
|
|
432
|
+
return true;
|
|
433
|
+
}
|
|
434
|
+
if (key.name === "return") {
|
|
435
|
+
this.selectedChatMode = this.availableModes[this.modeSelectedIndex].id;
|
|
436
|
+
this.isModeModalOpen = false;
|
|
437
|
+
setRuntimeChatMode(this.selectedChatMode);
|
|
438
|
+
saveTuiSettings({
|
|
439
|
+
chat: {
|
|
440
|
+
model: this.availableModels[this.selectedModelIndex],
|
|
441
|
+
effort: this.selectedEffort,
|
|
442
|
+
mode: this.selectedChatMode,
|
|
443
|
+
},
|
|
444
|
+
});
|
|
445
|
+
this.statusNote = theme.green(`✓ Modo global da API alterado para: ${this.selectedChatMode}`);
|
|
446
|
+
this.onNeedsRender?.();
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
if (key.name === "escape") {
|
|
450
|
+
this.isModeModalOpen = false;
|
|
451
|
+
this.onNeedsRender?.();
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// 4. Header button hover (rows 4 to 6: Model, Effort, Mode)
|
|
329
458
|
if (key.name === "hover" && key.mouse) {
|
|
330
459
|
const { row, col } = key.mouse;
|
|
331
|
-
if (row >= 4 && row <= 6 && !this.isModelModalOpen && !this.isEffortModalOpen) {
|
|
332
|
-
let target: "model" | "effort" | null = null;
|
|
460
|
+
if (row >= 4 && row <= 6 && !this.isModelModalOpen && !this.isEffortModalOpen && !this.isModeModalOpen) {
|
|
461
|
+
let target: "model" | "effort" | "mode" | null = null;
|
|
333
462
|
if (this.modelBtnStartCol > 0 && col >= this.modelBtnStartCol - 1 && col <= this.modelBtnEndCol + 1) {
|
|
334
463
|
target = "model";
|
|
335
464
|
} else if (this.effortBtnStartCol > 0 && col >= this.effortBtnStartCol - 1 && col <= this.effortBtnEndCol + 1) {
|
|
336
465
|
target = "effort";
|
|
466
|
+
} else if (this.modeBtnStartCol > 0 && col >= this.modeBtnStartCol - 1 && col <= this.modeBtnEndCol + 1) {
|
|
467
|
+
target = "mode";
|
|
337
468
|
}
|
|
338
469
|
if (this.hoveredHeaderBtn !== target) {
|
|
339
470
|
this.hoveredHeaderBtn = target;
|
|
@@ -347,14 +478,15 @@ export class ChatView implements TuiView {
|
|
|
347
478
|
}
|
|
348
479
|
}
|
|
349
480
|
|
|
350
|
-
//
|
|
481
|
+
// 5. Header button click (rows 4 to 6: Model, Effort, Mode)
|
|
351
482
|
if (
|
|
352
483
|
key.name === "click" &&
|
|
353
484
|
key.mouse &&
|
|
354
485
|
key.mouse.row >= 4 &&
|
|
355
486
|
key.mouse.row <= 6 &&
|
|
356
487
|
!this.isModelModalOpen &&
|
|
357
|
-
!this.isEffortModalOpen
|
|
488
|
+
!this.isEffortModalOpen &&
|
|
489
|
+
!this.isModeModalOpen
|
|
358
490
|
) {
|
|
359
491
|
const col = key.mouse.col;
|
|
360
492
|
if (this.modelBtnStartCol > 0 && col >= this.modelBtnStartCol - 1 && col <= this.modelBtnEndCol + 1) {
|
|
@@ -372,6 +504,14 @@ export class ChatView implements TuiView {
|
|
|
372
504
|
this.onNeedsRender?.();
|
|
373
505
|
return true;
|
|
374
506
|
}
|
|
507
|
+
if (this.modeBtnStartCol > 0 && col >= this.modeBtnStartCol - 1 && col <= this.modeBtnEndCol + 1) {
|
|
508
|
+
this.isModeModalOpen = true;
|
|
509
|
+
const idx = this.availableModes.findIndex((m) => m.id === this.selectedChatMode);
|
|
510
|
+
this.modeSelectedIndex = idx !== -1 ? idx : 0;
|
|
511
|
+
this.hoveredHeaderBtn = null;
|
|
512
|
+
this.onNeedsRender?.();
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
375
515
|
}
|
|
376
516
|
|
|
377
517
|
// Keyboard shortcuts to open modals
|
|
@@ -399,6 +539,14 @@ export class ChatView implements TuiView {
|
|
|
399
539
|
}
|
|
400
540
|
}
|
|
401
541
|
|
|
542
|
+
if (key.name === "f4") {
|
|
543
|
+
this.isModeModalOpen = true;
|
|
544
|
+
const idx = this.availableModes.findIndex((m) => m.id === this.selectedChatMode);
|
|
545
|
+
this.modeSelectedIndex = idx !== -1 ? idx : 0;
|
|
546
|
+
this.onNeedsRender?.();
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
|
|
402
550
|
// 5. Scrollbar hover, click & drag
|
|
403
551
|
const isMouseOnScrollbar = (col: number, row: number) => {
|
|
404
552
|
return (
|
|
@@ -666,6 +814,7 @@ export class ChatView implements TuiView {
|
|
|
666
814
|
const result = await streamChatCompletions({
|
|
667
815
|
model,
|
|
668
816
|
reasoning_effort: isReasoning ? this.selectedEffort : undefined,
|
|
817
|
+
chatMode: this.selectedChatMode,
|
|
669
818
|
messages: conversationPayload,
|
|
670
819
|
signal: this.currentAbortController.signal,
|
|
671
820
|
onReasoning: (chunk) => {
|
|
@@ -747,10 +896,10 @@ export class ChatView implements TuiView {
|
|
|
747
896
|
|
|
748
897
|
const effortLabel = isReasoning
|
|
749
898
|
? this.selectedEffort === "high"
|
|
750
|
-
? "[ Effort: High
|
|
899
|
+
? "[ Effort: High ]"
|
|
751
900
|
: this.selectedEffort === "medium"
|
|
752
|
-
? "[ Effort:
|
|
753
|
-
: "[ Effort: Low
|
|
901
|
+
? "[ Effort: Med ]"
|
|
902
|
+
: "[ Effort: Low ]"
|
|
754
903
|
: "";
|
|
755
904
|
|
|
756
905
|
let styledEffort = "";
|
|
@@ -764,7 +913,18 @@ export class ChatView implements TuiView {
|
|
|
764
913
|
: theme.cyan(effortLabel);
|
|
765
914
|
}
|
|
766
915
|
|
|
767
|
-
const
|
|
916
|
+
const modeLabel = `[ Modo: ${this.selectedChatMode} ]`;
|
|
917
|
+
const styledMode = this.hoveredHeaderBtn === "mode"
|
|
918
|
+
? theme.bgHover(` ${theme.bold(theme.white(modeLabel))} `)
|
|
919
|
+
: this.selectedChatMode === "thread"
|
|
920
|
+
? theme.cyan(modeLabel)
|
|
921
|
+
: this.selectedChatMode === "thread-temp"
|
|
922
|
+
? theme.green(modeLabel)
|
|
923
|
+
: this.selectedChatMode === "stateless-temp"
|
|
924
|
+
? theme.yellow(modeLabel)
|
|
925
|
+
: theme.lavender(modeLabel);
|
|
926
|
+
|
|
927
|
+
const shortcutsLabel = `[ F2: Modelo${isReasoning ? " | F3: Effort" : ""} | F4: Modo ]`;
|
|
768
928
|
const styledShortcuts = theme.yellow(shortcutsLabel);
|
|
769
929
|
|
|
770
930
|
// Non-text models show their category badge ([Imagem] / [Vídeo]), while text models omit [Texto]
|
|
@@ -772,7 +932,7 @@ export class ChatView implements TuiView {
|
|
|
772
932
|
const nonTextCategory = !isReasoning ? theme.muted(`• ${currentInfo.category}`) : "";
|
|
773
933
|
|
|
774
934
|
const headerLine = hasAccounts
|
|
775
|
-
? ` ${theme.bold("Modelo:")} ${styledModel} ${nonTextBadge}${isReasoning ? styledEffort : nonTextCategory} ${styledShortcuts}`
|
|
935
|
+
? ` ${theme.bold("Modelo:")} ${styledModel} ${nonTextBadge}${isReasoning ? styledEffort + " " : nonTextCategory + " "}${styledMode} ${styledShortcuts}`
|
|
776
936
|
: ` ${theme.bold("Modelo:")} ${styledModel} ${theme.yellow("[ [!] Sem Contas: Adicione em [5] Contas ]")}`;
|
|
777
937
|
|
|
778
938
|
// Compute dynamic interactive column bounds:
|
|
@@ -781,16 +941,25 @@ export class ChatView implements TuiView {
|
|
|
781
941
|
this.modelBtnStartCol = modelStart;
|
|
782
942
|
this.modelBtnEndCol = modelEnd;
|
|
783
943
|
|
|
944
|
+
let nextStart = modelEnd + 3;
|
|
784
945
|
if (isReasoning) {
|
|
785
|
-
const effortStart =
|
|
946
|
+
const effortStart = nextStart;
|
|
786
947
|
const effortEnd = effortStart + stringWidth(effortLabel) - 1;
|
|
787
948
|
this.effortBtnStartCol = effortStart;
|
|
788
949
|
this.effortBtnEndCol = effortEnd;
|
|
950
|
+
nextStart = effortEnd + 3;
|
|
789
951
|
} else {
|
|
790
952
|
this.effortBtnStartCol = 0;
|
|
791
953
|
this.effortBtnEndCol = 0;
|
|
954
|
+
if (nonTextCategory) {
|
|
955
|
+
nextStart = modelEnd + stringWidth(` ${nonTextBadge}${nonTextCategory} `);
|
|
956
|
+
}
|
|
792
957
|
}
|
|
793
958
|
|
|
959
|
+
const modeStart = nextStart;
|
|
960
|
+
const modeEnd = modeStart + stringWidth(modeLabel) - 1;
|
|
961
|
+
this.modeBtnStartCol = modeStart;
|
|
962
|
+
this.modeBtnEndCol = modeEnd;
|
|
794
963
|
const headerBox = drawBox({
|
|
795
964
|
title: "Chat Tester",
|
|
796
965
|
width,
|
|
@@ -853,6 +1022,33 @@ export class ChatView implements TuiView {
|
|
|
853
1022
|
content: modalLines,
|
|
854
1023
|
});
|
|
855
1024
|
totalLines.push(...modalBox);
|
|
1025
|
+
} else if (this.isModeModalOpen) {
|
|
1026
|
+
const modalLines: string[] = [
|
|
1027
|
+
"",
|
|
1028
|
+
` ${theme.bold("Modo de Conversa:")} ${theme.cyan(this.selectedChatMode)}`,
|
|
1029
|
+
` ${theme.dim("Escolha como o histórico é transmitido e persistido no Qwen:")}`,
|
|
1030
|
+
"",
|
|
1031
|
+
];
|
|
1032
|
+
for (let i = 0; i < this.availableModes.length; i++) {
|
|
1033
|
+
const m = this.availableModes[i];
|
|
1034
|
+
const isSel = i === this.modeSelectedIndex;
|
|
1035
|
+
const isCurrent = m.id === this.selectedChatMode;
|
|
1036
|
+
const pointer = isSel ? theme.cyan("▸ ") : " ";
|
|
1037
|
+
const radio = isCurrent ? theme.green(glyphs.radioOn) : theme.muted(glyphs.radioOff);
|
|
1038
|
+
const line = `${pointer}${radio} ${pad(m.badge, 17)} ${pad(m.label, 17)} • ${m.desc}`;
|
|
1039
|
+
modalLines.push(isSel ? theme.bgSelected(line) : line);
|
|
1040
|
+
}
|
|
1041
|
+
modalLines.push("");
|
|
1042
|
+
|
|
1043
|
+
const modalBox = drawBox({
|
|
1044
|
+
title: "Selecionar Modo de Conversa [ Enter: Confirmar • Esc: Manter ]",
|
|
1045
|
+
width,
|
|
1046
|
+
height: chatHeight,
|
|
1047
|
+
borderColor: theme.borderActive,
|
|
1048
|
+
titleColor: theme.cyan,
|
|
1049
|
+
content: modalLines,
|
|
1050
|
+
});
|
|
1051
|
+
totalLines.push(...modalBox);
|
|
856
1052
|
} else {
|
|
857
1053
|
const chatContent: string[] = [];
|
|
858
1054
|
|
|
@@ -887,7 +1083,6 @@ export class ChatView implements TuiView {
|
|
|
887
1083
|
if (msg.reasoning && msg.reasoning.trim().length > 0) {
|
|
888
1084
|
const thinkWidth = Math.max(20, innerChatW - 4);
|
|
889
1085
|
let thinkLines: string[];
|
|
890
|
-
|
|
891
1086
|
if (msg.cachedWidth === innerChatW && msg.cachedReasoningBox) {
|
|
892
1087
|
thinkLines = msg.cachedReasoningBox;
|
|
893
1088
|
} else {
|
|
@@ -7,6 +7,8 @@ import type { KeyEvent } from "../screen.ts";
|
|
|
7
7
|
import { theme, glyphs, drawBox, pad, truncate } from "../theme.ts";
|
|
8
8
|
import { fetchProxyStatus, resetAllCooldowns, formatUptime } from "../proxy-client.ts";
|
|
9
9
|
import { ServerManager } from "../server-manager.ts";
|
|
10
|
+
import { getRuntimeChatMode, cycleNextChatMode } from "../../core/config.ts";
|
|
11
|
+
import { saveTuiSettings } from "../settings.ts";
|
|
10
12
|
|
|
11
13
|
export function renderProgressBar(
|
|
12
14
|
pct: number,
|
|
@@ -62,6 +64,7 @@ export class StatusView implements TuiView {
|
|
|
62
64
|
private lastLeftW = 38;
|
|
63
65
|
private lastActionRecarregarRow = 20;
|
|
64
66
|
private lastActionZerarRow = 21;
|
|
67
|
+
private lastActionModoRow = 22;
|
|
65
68
|
constructor() {
|
|
66
69
|
this.refresh();
|
|
67
70
|
}
|
|
@@ -86,6 +89,7 @@ export class StatusView implements TuiView {
|
|
|
86
89
|
return [
|
|
87
90
|
{ key: "r", label: "Recarregar" },
|
|
88
91
|
{ key: "z", label: "Zerar Cooldowns" },
|
|
92
|
+
{ key: "m", label: "Alternar Modo" },
|
|
89
93
|
];
|
|
90
94
|
}
|
|
91
95
|
|
|
@@ -105,7 +109,9 @@ export class StatusView implements TuiView {
|
|
|
105
109
|
if (
|
|
106
110
|
col >= 2 &&
|
|
107
111
|
col <= leftW - 1 &&
|
|
108
|
-
(row === this.lastActionRecarregarRow ||
|
|
112
|
+
(row === this.lastActionRecarregarRow ||
|
|
113
|
+
row === this.lastActionZerarRow ||
|
|
114
|
+
row === this.lastActionModoRow)
|
|
109
115
|
) {
|
|
110
116
|
if (this.hoveredActionRow !== row) {
|
|
111
117
|
this.hoveredActionRow = row;
|
|
@@ -133,9 +139,15 @@ export class StatusView implements TuiView {
|
|
|
133
139
|
this.setMessage(theme.green(`✓ Cooldowns zerados: ${cleared} conta(s) liberada(s)`));
|
|
134
140
|
return true;
|
|
135
141
|
}
|
|
142
|
+
if (row === this.lastActionModoRow) {
|
|
143
|
+
const nextMode = cycleNextChatMode();
|
|
144
|
+
saveTuiSettings({ chat: { mode: nextMode } });
|
|
145
|
+
await this.refresh();
|
|
146
|
+
this.setMessage(theme.green(`✓ Modo global da API: ${nextMode}`));
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
136
149
|
}
|
|
137
150
|
}
|
|
138
|
-
|
|
139
151
|
if ((key.name === "r" || key.name === "R") && !key.ctrl) {
|
|
140
152
|
await this.refresh();
|
|
141
153
|
this.setMessage(theme.green("✓ Status atualizado"));
|
|
@@ -148,6 +160,14 @@ export class StatusView implements TuiView {
|
|
|
148
160
|
this.setMessage(theme.green(`✓ Cooldowns zerados: ${cleared} conta(s) liberada(s)`));
|
|
149
161
|
return true;
|
|
150
162
|
}
|
|
163
|
+
|
|
164
|
+
if ((key.name === "m" || key.name === "M") && !key.ctrl) {
|
|
165
|
+
const nextMode = cycleNextChatMode();
|
|
166
|
+
saveTuiSettings({ chat: { mode: nextMode } });
|
|
167
|
+
await this.refresh();
|
|
168
|
+
this.setMessage(theme.green(`✓ Modo global da API: ${nextMode}`));
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
151
171
|
}
|
|
152
172
|
|
|
153
173
|
public render(width: number, height: number, snapshot?: ProxyStatusSnapshot | null): string[] {
|
|
@@ -214,8 +234,16 @@ export class StatusView implements TuiView {
|
|
|
214
234
|
const leftContent: string[] = [
|
|
215
235
|
` ${theme.bold(lbl("Status:"))} ${onlineBadge}`,
|
|
216
236
|
` ${theme.bold(lbl("Base URL:"))} ${theme.cyan(baseUrl)}`,
|
|
237
|
+
` ${theme.bold(lbl("Modo API:"))} ${
|
|
238
|
+
getRuntimeChatMode() === "thread"
|
|
239
|
+
? theme.cyan("[thread]")
|
|
240
|
+
: getRuntimeChatMode() === "thread-temp"
|
|
241
|
+
? theme.green("[thread-temp]")
|
|
242
|
+
: getRuntimeChatMode() === "stateless-temp"
|
|
243
|
+
? theme.yellow("[stateless-temp]")
|
|
244
|
+
: theme.lavender("[stateless]")
|
|
245
|
+
} ${theme.dim("('M' alternar)")}`,
|
|
217
246
|
` ${theme.bold(lbl("Uptime:"))} ${theme.cyan(uptimeStr)}`,
|
|
218
|
-
` ${theme.bold(lbl("Memória:"))} ${theme.cyan(ramStr)}`,
|
|
219
247
|
` ${theme.bold(lbl("Conexões:"))} ${connsStr}`,
|
|
220
248
|
` ${theme.dim("───────────────────────────────────────")}`,
|
|
221
249
|
` ${theme.bold("Tráfego & Performance:")}`,
|
|
@@ -235,10 +263,13 @@ export class StatusView implements TuiView {
|
|
|
235
263
|
const zerarIdx = leftContent.length + 1;
|
|
236
264
|
this.lastActionRecarregarRow = 5 + recarregarIdx;
|
|
237
265
|
this.lastActionZerarRow = 5 + zerarIdx;
|
|
266
|
+
const modoIdx = leftContent.length + 2;
|
|
267
|
+
this.lastActionModoRow = 5 + modoIdx;
|
|
238
268
|
|
|
239
269
|
leftContent.push(
|
|
240
270
|
` ${this.hoveredActionRow === this.lastActionRecarregarRow ? theme.bgHover(` ${theme.cyan("[ R ] Recarregar")} `) : ` ${theme.cyan("[ R ]")} Recarregar`}`,
|
|
241
271
|
` ${this.hoveredActionRow === this.lastActionZerarRow ? theme.bgHover(` ${theme.yellow("[ Z ] Zerar Cooldowns")} `) : ` ${theme.yellow("[ Z ]")} Zerar Cooldowns`}`,
|
|
272
|
+
` ${this.hoveredActionRow === this.lastActionModoRow ? theme.bgHover(` ${theme.lavender("[ M ] Alternar Modo")} `) : ` ${theme.lavender("[ M ]")} Alternar Modo`}`,
|
|
242
273
|
);
|
|
243
274
|
|
|
244
275
|
const boxHeight = Math.max(contentH, leftContent.length + 2);
|