primocode 8.32.0 → 8.33.1
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/app/app.css +175 -106
- package/app/app.js +171 -4
- package/app/index.html +125 -48
- package/app/primo-marca.js +113 -0
- package/bin/primocode.js +57 -15
- package/lib/api.js +67 -3
- package/lib/app.js +39 -1
- package/lib/boas-vindas.js +161 -0
- package/lib/modo.js +43 -3
- package/package.json +2 -2
package/app/app.css
CHANGED
|
@@ -27,7 +27,34 @@
|
|
|
27
27
|
--mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Mono", Menlo,
|
|
28
28
|
Consolas, "Liberation Mono", monospace;
|
|
29
29
|
--sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
|
|
30
|
+
|
|
31
|
+
/* A barra lateral e o diálogo de configurações. Nomes novos, mesma
|
|
32
|
+
família de cores — nada aqui inventa cor: tudo sai das de cima. */
|
|
33
|
+
--barra: #0b1119;
|
|
34
|
+
--alto: #16202c;
|
|
35
|
+
--largura-lado: 262px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Tema claro. Só os TOKENS mudam; nenhuma regra abaixo sabe que ele existe,
|
|
39
|
+
e é isso que faz o tema inteiro caber em vinte linhas. */
|
|
40
|
+
html[data-tema="claro"] {
|
|
41
|
+
--azul: #0e7490;
|
|
42
|
+
--azul-fundo: #0891b2;
|
|
43
|
+
--texto: #0f1922;
|
|
44
|
+
--fraco: #46596b;
|
|
45
|
+
--apagado: #7b8fa1;
|
|
46
|
+
--fundo: #f4f7f9;
|
|
47
|
+
--painel: #ffffff;
|
|
48
|
+
--borda: #d5e0e7;
|
|
49
|
+
--borda-viva: #b7c8d3;
|
|
50
|
+
--barra: #eef3f6;
|
|
51
|
+
--alto: #e3ebf0;
|
|
30
52
|
}
|
|
53
|
+
/* A cor de acento escolhida nas configurações. */
|
|
54
|
+
html[data-cor="verde"] { --azul:#34d399; --azul-fundo:#10b981; }
|
|
55
|
+
html[data-cor="roxo"] { --azul:#a78bfa; --azul-fundo:#8b5cf6; }
|
|
56
|
+
html[data-cor="ambar"] { --azul:#fbbf24; --azul-fundo:#d97706; }
|
|
57
|
+
html[data-cor="rosa"] { --azul:#fb7185; --azul-fundo:#e11d48; }
|
|
31
58
|
|
|
32
59
|
* { box-sizing: border-box; }
|
|
33
60
|
|
|
@@ -48,70 +75,170 @@ html, body {
|
|
|
48
75
|
}
|
|
49
76
|
|
|
50
77
|
body {
|
|
51
|
-
display: grid;
|
|
52
|
-
grid-template-rows: auto 1fr auto;
|
|
53
78
|
/* O brilho azul atrás de tudo. Um só, bem aberto e bem fraco: dá
|
|
54
79
|
profundidade sem virar papel de parede. */
|
|
55
80
|
background-image:
|
|
56
81
|
radial-gradient(120% 80% at 50% -20%, rgba(34, 211, 238, .08), transparent 62%);
|
|
57
82
|
}
|
|
58
83
|
|
|
59
|
-
/* ──
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
84
|
+
/* ── a mesma planta da página /code do Conecta ──────────────────────────
|
|
85
|
+
Barra lateral fixa à esquerda, conversa ocupando o resto. As duas telas
|
|
86
|
+
são o mesmo produto e têm de ser reconhecíveis uma na outra no primeiro
|
|
87
|
+
olhar — quem sai do terminal para a janela ou do celular para a janela
|
|
88
|
+
não pode ter de reaprender onde ficam as coisas. */
|
|
89
|
+
.tela { display: flex; height: 100%; }
|
|
90
|
+
.lado {
|
|
91
|
+
width: var(--largura-lado); flex: 0 0 var(--largura-lado);
|
|
92
|
+
background: var(--barra); border-right: 1px solid var(--borda);
|
|
93
|
+
display: flex; flex-direction: column; min-height: 0;
|
|
69
94
|
-webkit-app-region: drag;
|
|
70
|
-
user-select: none;
|
|
71
95
|
}
|
|
72
|
-
|
|
96
|
+
.lado button, .lado .chip { -webkit-app-region: no-drag; }
|
|
97
|
+
.lado-topo { padding: 14px 12px 10px; display: flex; flex-direction: column; gap: 12px; }
|
|
98
|
+
.marca-linha { display: flex; align-items: center; gap: 9px; padding: 0 4px; }
|
|
99
|
+
.marca-linha .nome { font-weight: 620; letter-spacing: -.01em; }
|
|
100
|
+
.marca-linha .versao { margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--apagado); }
|
|
73
101
|
|
|
74
|
-
.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
102
|
+
.novo {
|
|
103
|
+
display: flex; align-items: center; gap: 9px; width: 100%;
|
|
104
|
+
padding: 9px 12px; border-radius: 9px; cursor: pointer;
|
|
105
|
+
background: var(--alto); border: 1px solid var(--borda-viva);
|
|
106
|
+
color: inherit; font: inherit; font-size: 13.5px; font-weight: 500;
|
|
107
|
+
transition: border-color .15s;
|
|
108
|
+
}
|
|
109
|
+
.novo:hover { border-color: var(--azul); }
|
|
78
110
|
|
|
79
|
-
.
|
|
111
|
+
.rotulo { padding: 14px 16px 6px; font-size: 11px; letter-spacing: .08em;
|
|
112
|
+
text-transform: uppercase; color: var(--apagado); }
|
|
113
|
+
.recentes { flex: 1 1 0; min-height: 0; overflow-y: auto; padding: 0 8px 8px; }
|
|
114
|
+
.item {
|
|
115
|
+
display: block; width: 100%; text-align: left; padding: 8px 10px; cursor: pointer;
|
|
116
|
+
border: 0; background: none; color: var(--fraco); font: inherit; font-size: 13px;
|
|
117
|
+
border-radius: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
118
|
+
}
|
|
119
|
+
.item:hover { background: var(--alto); color: var(--texto); }
|
|
120
|
+
.item[aria-current="true"] { background: var(--alto); color: var(--texto); font-weight: 500; }
|
|
121
|
+
.item .quando { display: block; font-size: 11px; color: var(--apagado); font-family: var(--mono); }
|
|
122
|
+
.vazio-lista { padding: 6px 12px; font-size: 12.5px; color: var(--apagado); line-height: 1.5; }
|
|
123
|
+
.vazio-lista code { font-family: var(--mono); color: var(--azul); }
|
|
124
|
+
|
|
125
|
+
.lado-pe { border-top: 1px solid var(--borda); padding: 8px; display: flex; flex-direction: column; gap: 6px; }
|
|
126
|
+
.pe-btn {
|
|
127
|
+
display: flex; align-items: center; gap: 9px; padding: 8px 10px; cursor: pointer;
|
|
128
|
+
border: 0; background: none; color: var(--fraco); font: inherit; font-size: 13.5px;
|
|
129
|
+
border-radius: 8px; width: 100%; text-align: left;
|
|
130
|
+
}
|
|
131
|
+
.pe-btn:hover { background: var(--alto); color: var(--texto); }
|
|
132
|
+
.chips { display: flex; flex-wrap: wrap; gap: 5px; padding: 0 4px 2px; }
|
|
80
133
|
.chip {
|
|
81
|
-
font-family: var(--mono); font-size:
|
|
82
|
-
|
|
83
|
-
border:
|
|
84
|
-
|
|
85
|
-
max-width: 300px; overflow: hidden; text-overflow: ellipsis;
|
|
134
|
+
font-family: var(--mono); font-size: 11px; color: var(--fraco);
|
|
135
|
+
background: var(--alto); border: 1px solid var(--borda);
|
|
136
|
+
border-radius: 6px; padding: 2px 7px;
|
|
137
|
+
max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
86
138
|
}
|
|
87
139
|
.chip:empty { display: none; }
|
|
88
|
-
#modo { color: var(--azul); border-color: var(--borda-viva); }
|
|
89
140
|
|
|
90
|
-
|
|
141
|
+
.conversa { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
|
|
142
|
+
.rolo { flex: 1 1 0; overflow-y: auto; scroll-behavior: smooth; }
|
|
143
|
+
.rolo::-webkit-scrollbar { width: 10px; }
|
|
144
|
+
.rolo::-webkit-scrollbar-thumb { background: #1c2836; border-radius: 8px; border: 3px solid var(--fundo); }
|
|
145
|
+
.tira { max-width: 860px; margin: 0 auto; padding: 26px 22px 40px; }
|
|
91
146
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.boas-vindas { padding: 12vh 0 0; text-align: center; }
|
|
99
|
-
.boas-vindas h1 {
|
|
100
|
-
font-size: 27px; font-weight: 620; letter-spacing: -.025em; margin: 0 0 10px;
|
|
101
|
-
}
|
|
102
|
-
.boas-vindas p {
|
|
103
|
-
color: var(--fraco); max-width: 460px; margin: 0 auto 26px; line-height: 1.6;
|
|
104
|
-
}
|
|
105
|
-
.atalhos { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
|
|
106
|
-
.atalhos button {
|
|
147
|
+
.vazio { display: flex; flex-direction: column; align-items: center; gap: 14px;
|
|
148
|
+
padding: 9vh 22px 0; text-align: center; }
|
|
149
|
+
.vazio h1 { font-size: 26px; font-weight: 620; letter-spacing: -.025em; margin: 4px 0 0; }
|
|
150
|
+
.vazio p { color: var(--fraco); max-width: 460px; margin: 0; line-height: 1.6; }
|
|
151
|
+
.sugestoes { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 6px; }
|
|
152
|
+
.sugestao {
|
|
107
153
|
font: inherit; font-size: 13px; color: var(--fraco); cursor: pointer;
|
|
108
154
|
background: var(--painel); border: 1px solid var(--borda);
|
|
109
155
|
border-radius: 999px; padding: 7px 15px;
|
|
110
156
|
transition: border-color .16s, color .16s, transform .16s;
|
|
111
157
|
}
|
|
112
|
-
.
|
|
113
|
-
|
|
158
|
+
.sugestao:hover { color: var(--texto); border-color: var(--azul); transform: translateY(-1px); }
|
|
159
|
+
|
|
160
|
+
/* "trabalhando", com o ponto que pulsa. Fica ACIMA do campo, para não
|
|
161
|
+
empurrar o texto que a pessoa está digitando. */
|
|
162
|
+
#pensando {
|
|
163
|
+
max-width: 860px; margin: 0 auto 9px; display: flex; align-items: center; gap: 8px;
|
|
164
|
+
font-family: var(--mono); font-size: 12px; color: var(--apagado);
|
|
165
|
+
}
|
|
166
|
+
.pulso {
|
|
167
|
+
width: 8px; height: 8px; border-radius: 50%; background: var(--azul);
|
|
168
|
+
box-shadow: 0 0 0 0 rgba(34, 211, 238, .55);
|
|
169
|
+
}
|
|
170
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
171
|
+
.pulso { animation: bater 1.4s ease-out infinite; }
|
|
172
|
+
@keyframes bater {
|
|
173
|
+
to { box-shadow: 0 0 0 9px rgba(34, 211, 238, 0); }
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
.dica {
|
|
177
|
+
max-width: 860px; margin: 8px auto 0;
|
|
178
|
+
color: var(--apagado); font-size: 11.5px; text-align: center;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.rodape { border-top: 1px solid var(--borda); background: rgba(10, 15, 22, .82);
|
|
182
|
+
backdrop-filter: blur(14px); padding: 12px 22px 14px; }
|
|
183
|
+
html[data-tema="claro"] .rodape { background: rgba(255, 255, 255, .85); }
|
|
184
|
+
.caixa { max-width: 860px; margin: 0 auto; display: flex; align-items: flex-end; gap: 10px; }
|
|
185
|
+
.campo-volta {
|
|
186
|
+
flex: 1 1 auto; display: flex; align-items: flex-end; gap: 8px;
|
|
187
|
+
background: var(--painel); border: 1px solid var(--borda-viva); border-radius: 12px;
|
|
188
|
+
padding: 9px 12px; transition: border-color .15s;
|
|
189
|
+
}
|
|
190
|
+
.campo-volta:focus-within { border-color: var(--azul); }
|
|
191
|
+
.chevron { font-family: var(--mono); color: var(--azul); font-weight: 600; padding-bottom: 1px; }
|
|
192
|
+
/* O textarea entrega a moldura para a `.campo-volta`. Sem isto ele desenha a
|
|
193
|
+
caixa DELE dentro da caixa de fora — duas bordas, uma dentro da outra. */
|
|
194
|
+
#campo {
|
|
195
|
+
flex: 1 1 auto; background: none; border: 0; outline: none; color: inherit;
|
|
196
|
+
resize: none; font-family: var(--mono); font-size: 13.5px; line-height: 1.6;
|
|
197
|
+
max-height: 190px; padding: 0;
|
|
198
|
+
}
|
|
199
|
+
#campo::placeholder { color: var(--apagado); }
|
|
200
|
+
.enviar {
|
|
201
|
+
flex: 0 0 auto; width: 34px; height: 34px; border-radius: 10px; cursor: pointer;
|
|
202
|
+
display: grid; place-items: center; border: 0;
|
|
203
|
+
background: var(--azul); color: #04252c; transition: background .16s, opacity .16s;
|
|
204
|
+
}
|
|
205
|
+
html[data-tema="claro"] .enviar { color: #fff; }
|
|
206
|
+
.enviar:disabled { background: #16323d; color: #3f6472; cursor: default; }
|
|
207
|
+
.enviar.parar { background: rgba(251, 113, 133, .14); color: var(--falha); }
|
|
208
|
+
|
|
209
|
+
/* ── configurações ──────────────────────────────────────────────────── */
|
|
210
|
+
dialog {
|
|
211
|
+
border: 1px solid var(--borda-viva); border-radius: 14px; padding: 0;
|
|
212
|
+
background: var(--painel); color: var(--texto); max-width: 440px; width: calc(100% - 32px);
|
|
213
|
+
}
|
|
214
|
+
dialog::backdrop { background: rgba(4, 8, 12, .62); }
|
|
215
|
+
.dlg-topo { display: flex; align-items: center; justify-content: space-between; gap: 12px;
|
|
216
|
+
padding: 14px 17px; border-bottom: 1px solid var(--borda); }
|
|
217
|
+
.dlg-topo h2 { margin: 0; font-size: 15.5px; font-weight: 620; }
|
|
218
|
+
.dlg-corpo { padding: 15px 17px 19px; display: flex; flex-direction: column; gap: 19px; }
|
|
219
|
+
.grupo > h3 { margin: 0 0 9px; font-size: 11px; letter-spacing: .08em;
|
|
220
|
+
text-transform: uppercase; color: var(--apagado); font-weight: 500; }
|
|
221
|
+
.opcoes { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
222
|
+
.opcao, .copiar {
|
|
223
|
+
padding: 7px 14px; border-radius: 8px; cursor: pointer; font: inherit; font-size: 13px;
|
|
224
|
+
border: 1px solid var(--borda-viva); background: none; color: var(--fraco);
|
|
114
225
|
}
|
|
226
|
+
.opcao[aria-pressed="true"] { border-color: var(--azul); color: var(--texto); }
|
|
227
|
+
.copiar { padding: 4px 10px; font-size: 12px; }
|
|
228
|
+
.cores { display: flex; gap: 10px; }
|
|
229
|
+
.cor { width: 30px; height: 30px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; }
|
|
230
|
+
.cor[aria-pressed="true"] { border-color: var(--texto); }
|
|
231
|
+
.linha-info { display: flex; justify-content: space-between; gap: 12px; font-size: 13.5px; color: var(--fraco); }
|
|
232
|
+
.linha-info b { color: var(--texto); font-weight: 500; }
|
|
233
|
+
.dlg-corpo code { font-family: var(--mono); color: var(--azul); }
|
|
234
|
+
|
|
235
|
+
/* ── conversa ───────────────────────────────────────────────────────────── */
|
|
236
|
+
|
|
237
|
+
#rolo { overflow-y: auto; scroll-behavior: smooth; }
|
|
238
|
+
#rolo::-webkit-scrollbar { width: 10px; }
|
|
239
|
+
#rolo::-webkit-scrollbar-thumb { background: #1c2836; border-radius: 8px; border: 3px solid var(--fundo); }
|
|
240
|
+
|
|
241
|
+
#conversa { max-width: 860px; margin: 0 auto; padding: 26px 22px 40px; }
|
|
115
242
|
|
|
116
243
|
/* O que o usuário disse: bolha à direita, curta, com a marca de lado. */
|
|
117
244
|
.voce {
|
|
@@ -150,66 +277,6 @@ body {
|
|
|
150
277
|
border: 1px solid rgba(251, 113, 133, .26); border-radius: 10px;
|
|
151
278
|
}
|
|
152
279
|
|
|
153
|
-
/* ── barra de baixo ─────────────────────────────────────────────────────── */
|
|
154
|
-
|
|
155
|
-
#baixo {
|
|
156
|
-
border-top: 1px solid var(--borda);
|
|
157
|
-
background: rgba(10, 15, 22, .82);
|
|
158
|
-
backdrop-filter: blur(14px);
|
|
159
|
-
padding: 12px 22px 14px;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
#pensando {
|
|
163
|
-
max-width: 860px; margin: 0 auto 9px;
|
|
164
|
-
display: flex; align-items: center; gap: 9px;
|
|
165
|
-
color: var(--azul); font-family: var(--mono); font-size: 12.5px;
|
|
166
|
-
}
|
|
167
|
-
.pulso {
|
|
168
|
-
width: 7px; height: 7px; border-radius: 50%; background: var(--azul);
|
|
169
|
-
animation: bater 1.15s ease-in-out infinite;
|
|
170
|
-
}
|
|
171
|
-
@keyframes bater {
|
|
172
|
-
0%, 100% { opacity: .28; transform: scale(.72); }
|
|
173
|
-
50% { opacity: 1; transform: scale(1); }
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
#forma {
|
|
177
|
-
max-width: 860px; margin: 0 auto;
|
|
178
|
-
display: flex; align-items: flex-end; gap: 9px;
|
|
179
|
-
background: var(--painel);
|
|
180
|
-
border: 1px solid var(--borda);
|
|
181
|
-
border-radius: 15px; padding: 8px 8px 8px 15px;
|
|
182
|
-
transition: border-color .16s, box-shadow .16s;
|
|
183
|
-
}
|
|
184
|
-
#forma:focus-within {
|
|
185
|
-
border-color: rgba(34, 211, 238, .45);
|
|
186
|
-
box-shadow: 0 0 0 3px rgba(34, 211, 238, .09);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
#campo {
|
|
190
|
-
flex: 1; font: inherit; font-size: 14.5px; line-height: 1.55;
|
|
191
|
-
color: var(--texto); background: none; border: 0; outline: 0;
|
|
192
|
-
resize: none; max-height: 44vh; padding: 6px 0;
|
|
193
|
-
}
|
|
194
|
-
#campo::placeholder { color: var(--apagado); }
|
|
195
|
-
|
|
196
|
-
#forma button {
|
|
197
|
-
flex: 0 0 auto; width: 34px; height: 34px; border-radius: 10px;
|
|
198
|
-
display: grid; place-items: center; cursor: pointer; border: 0;
|
|
199
|
-
transition: background .16s, color .16s, opacity .16s;
|
|
200
|
-
}
|
|
201
|
-
#forma button svg { width: 18px; height: 18px; }
|
|
202
|
-
#mandar { background: var(--azul); color: #04252c; }
|
|
203
|
-
#mandar:hover { background: #67e8f9; }
|
|
204
|
-
#mandar:disabled { background: #16323d; color: #3f6472; cursor: default; }
|
|
205
|
-
#parar { background: rgba(251, 113, 133, .14); color: var(--falha); }
|
|
206
|
-
#parar:hover { background: rgba(251, 113, 133, .24); }
|
|
207
|
-
|
|
208
|
-
.dica {
|
|
209
|
-
max-width: 860px; margin: 8px auto 0;
|
|
210
|
-
color: var(--apagado); font-size: 11.5px; text-align: center;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
280
|
/* ── arrastar arquivo ───────────────────────────────────────────────────── */
|
|
214
281
|
|
|
215
282
|
#alvo-solto {
|
|
@@ -224,7 +291,9 @@ body.soltando #alvo-solto { display: grid; }
|
|
|
224
291
|
}
|
|
225
292
|
#alvo-solto span { color: var(--fraco); font-size: 13px; }
|
|
226
293
|
|
|
227
|
-
@media (max-width:
|
|
228
|
-
.
|
|
229
|
-
|
|
294
|
+
@media (max-width: 760px) {
|
|
295
|
+
/* Janela estreita: a barra lateral sai da frente. O botão de novo chat e o
|
|
296
|
+
histórico continuam alcançáveis pelo terminal (/rc de novo, /conversas). */
|
|
297
|
+
.lado { display: none; }
|
|
298
|
+
.tira, .caixa { max-width: none; }
|
|
230
299
|
}
|
package/app/app.js
CHANGED
|
@@ -51,8 +51,20 @@
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
function limparBoasVindas() {
|
|
54
|
-
var b =
|
|
55
|
-
if (b) b.
|
|
54
|
+
var b = document.getElementById("vazio");
|
|
55
|
+
if (b) b.hidden = true;
|
|
56
|
+
// A tira das mensagens nasce escondida para a tela vazia poder ocupar o
|
|
57
|
+
// meio. Esconder uma sem mostrar a outra deixaria a janela em branco na
|
|
58
|
+
// primeira mensagem — e nada na tela é o pior jeito de responder.
|
|
59
|
+
var t = document.getElementById("conversa");
|
|
60
|
+
if (t) t.hidden = false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function voltarAoVazio() {
|
|
64
|
+
var b = document.getElementById("vazio");
|
|
65
|
+
var t = document.getElementById("conversa");
|
|
66
|
+
if (t) { t.innerHTML = ""; t.hidden = true; }
|
|
67
|
+
if (b) b.hidden = false;
|
|
56
68
|
}
|
|
57
69
|
|
|
58
70
|
/* ── ANSI → HTML ───────────────────────────────────────────────────────
|
|
@@ -162,7 +174,10 @@
|
|
|
162
174
|
mandar.hidden = ocupado;
|
|
163
175
|
campo.placeholder = ocupado
|
|
164
176
|
? "ele está trabalhando — pode ir escrevendo o próximo"
|
|
165
|
-
|
|
177
|
+
// O "Enter manda, Shift+Enter quebra linha" saiu daqui: ele já está
|
|
178
|
+
// escrito na dica logo abaixo do campo, e dizer a mesma coisa duas
|
|
179
|
+
// vezes na mesma tela faz a pessoa ler nenhuma das duas.
|
|
180
|
+
: "Peça alguma coisa…";
|
|
166
181
|
}
|
|
167
182
|
|
|
168
183
|
/* ── o fluxo ───────────────────────────────────────────────────────────
|
|
@@ -218,7 +233,7 @@
|
|
|
218
233
|
}
|
|
219
234
|
});
|
|
220
235
|
|
|
221
|
-
document.querySelectorAll(".
|
|
236
|
+
document.querySelectorAll(".sugestao").forEach(function (b) {
|
|
222
237
|
b.addEventListener("click", function () { enviar(b.dataset.manda); });
|
|
223
238
|
});
|
|
224
239
|
|
|
@@ -266,6 +281,158 @@
|
|
|
266
281
|
ajustarAltura();
|
|
267
282
|
});
|
|
268
283
|
|
|
284
|
+
/* ── a barra lateral ─────────────────────────────────────────────────
|
|
285
|
+
Mascote, novo chat, histórico e configurações — a mesma coisa da página
|
|
286
|
+
/code do Conecta, e de propósito: quem sai de uma tela para a outra não
|
|
287
|
+
pode ter de reaprender onde ficam as coisas. */
|
|
288
|
+
|
|
289
|
+
var CHAVE_TEMA = "primocode_tema";
|
|
290
|
+
var CHAVE_COR = "primocode_cor";
|
|
291
|
+
var pego = function (k, p) { try { return localStorage.getItem(k) || p; } catch (e) { return p; } };
|
|
292
|
+
var guardo = function (k, v) { try { localStorage.setItem(k, v); } catch (e) {} };
|
|
293
|
+
|
|
294
|
+
function marcarEscolha(caixaId, atributo, valor) {
|
|
295
|
+
var caixa = document.getElementById(caixaId);
|
|
296
|
+
if (!caixa) return;
|
|
297
|
+
Array.prototype.forEach.call(caixa.children, function (b) {
|
|
298
|
+
b.setAttribute("aria-pressed", b.dataset[atributo] === valor ? "true" : "false");
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
function aplicarTema(qual) {
|
|
302
|
+
// "sistema" não é um terceiro tema: é a ausência de escolha. Guardar
|
|
303
|
+
// "escuro" quando a pessoa quer seguir o aparelho faria a janela ignorar
|
|
304
|
+
// o modo noturno para sempre, sem ela ter como saber por quê.
|
|
305
|
+
var raiz = document.documentElement;
|
|
306
|
+
if (qual === "sistema") {
|
|
307
|
+
raiz.setAttribute("data-tema",
|
|
308
|
+
window.matchMedia("(prefers-color-scheme: dark)").matches ? "escuro" : "claro");
|
|
309
|
+
} else {
|
|
310
|
+
raiz.setAttribute("data-tema", qual);
|
|
311
|
+
}
|
|
312
|
+
marcarEscolha("temas", "tema", qual);
|
|
313
|
+
}
|
|
314
|
+
function aplicarCor(cor) {
|
|
315
|
+
document.documentElement.setAttribute("data-cor", cor);
|
|
316
|
+
marcarEscolha("cores", "cor", cor);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function desenharMarca() {
|
|
320
|
+
if (!window.PrimoMarca) return;
|
|
321
|
+
var t = document.getElementById("marcaTopo");
|
|
322
|
+
var g = document.getElementById("marcaGrande");
|
|
323
|
+
if (t) t.innerHTML = window.PrimoMarca.svg({ tamanho: 22 });
|
|
324
|
+
if (g) g.innerHTML = window.PrimoMarca.svg({ tamanho: 82, rotulo: "PrimoCode" });
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function quando(iso) {
|
|
328
|
+
if (!iso) return "";
|
|
329
|
+
var d = new Date(iso);
|
|
330
|
+
if (isNaN(d)) return "";
|
|
331
|
+
var dias = Math.floor((Date.now() - d.getTime()) / 86400000);
|
|
332
|
+
if (dias <= 0) return d.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" });
|
|
333
|
+
if (dias === 1) return "ontem";
|
|
334
|
+
if (dias < 7) return dias + " dias";
|
|
335
|
+
return d.toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit" });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
var conversaAberta = null;
|
|
339
|
+
|
|
340
|
+
function carregarConversas() {
|
|
341
|
+
var caixa = document.getElementById("recentes");
|
|
342
|
+
if (!caixa) return;
|
|
343
|
+
pedir("/api/conversas").then(function (r) { return r.json(); }).then(function (d) {
|
|
344
|
+
if (!d || !d.ok || !d.conversas || !d.conversas.length) return; // deixa o texto de vazio
|
|
345
|
+
caixa.innerHTML = "";
|
|
346
|
+
d.conversas.forEach(function (c) {
|
|
347
|
+
var b = document.createElement("button");
|
|
348
|
+
b.className = "item";
|
|
349
|
+
b.setAttribute("aria-current", c.id === conversaAberta ? "true" : "false");
|
|
350
|
+
var t = document.createElement("span");
|
|
351
|
+
// textContent, nunca innerHTML: o título vem cifrado do banco e foi
|
|
352
|
+
// escrito por gente — um "<img onerror>" ali viraria script na janela.
|
|
353
|
+
t.textContent = c.titulo || "Conversa";
|
|
354
|
+
var q = document.createElement("span");
|
|
355
|
+
q.className = "quando";
|
|
356
|
+
q.textContent = quando(c.atualizada) + (c.mensagens ? " · " + c.mensagens + " msg" : "");
|
|
357
|
+
b.appendChild(t); b.appendChild(q);
|
|
358
|
+
b.onclick = function () { abrirConversa(c.id); };
|
|
359
|
+
caixa.appendChild(b);
|
|
360
|
+
});
|
|
361
|
+
}).catch(function () { /* sem conta: fica o texto de vazio */ });
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function abrirConversa(id) {
|
|
365
|
+
conversaAberta = id;
|
|
366
|
+
pedir("/api/conversa/" + encodeURIComponent(id)).then(function (r) { return r.json(); })
|
|
367
|
+
.then(function (d) {
|
|
368
|
+
if (!d || !d.ok) return;
|
|
369
|
+
voltarAoVazio();
|
|
370
|
+
limparBoasVindas();
|
|
371
|
+
var tira = document.getElementById("conversa");
|
|
372
|
+
(d.mensagens || []).forEach(function (m) {
|
|
373
|
+
var linha = document.createElement("div");
|
|
374
|
+
if (m.papel === "user") {
|
|
375
|
+
linha.className = "voce";
|
|
376
|
+
var dentro = document.createElement("div");
|
|
377
|
+
dentro.textContent = m.texto;
|
|
378
|
+
linha.appendChild(dentro);
|
|
379
|
+
} else {
|
|
380
|
+
linha.className = "saida";
|
|
381
|
+
linha.textContent = m.texto;
|
|
382
|
+
}
|
|
383
|
+
tira.appendChild(linha);
|
|
384
|
+
});
|
|
385
|
+
rolo.scrollTop = rolo.scrollHeight;
|
|
386
|
+
carregarConversas();
|
|
387
|
+
}).catch(function () {});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function ligarLado() {
|
|
391
|
+
var novo = document.getElementById("btnNovo");
|
|
392
|
+
if (novo) novo.onclick = function () {
|
|
393
|
+
conversaAberta = null;
|
|
394
|
+
voltarAoVazio();
|
|
395
|
+
carregarConversas();
|
|
396
|
+
campo.focus();
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
var dlg = document.getElementById("dlgConfig");
|
|
400
|
+
var abrir = document.getElementById("btnConfig");
|
|
401
|
+
var fechar = document.getElementById("fecharConfig");
|
|
402
|
+
if (abrir && dlg) abrir.onclick = function () {
|
|
403
|
+
// Os três valores vêm do estado que o terminal já manda; repetir a
|
|
404
|
+
// consulta aqui só para abrir um diálogo seria uma ida à toa.
|
|
405
|
+
["pasta", "modo", "esforco"].forEach(function (k) {
|
|
406
|
+
var de = document.getElementById(k);
|
|
407
|
+
var para = document.getElementById(k + "Config");
|
|
408
|
+
if (de && para) para.textContent = de.textContent || "—";
|
|
409
|
+
});
|
|
410
|
+
dlg.showModal();
|
|
411
|
+
};
|
|
412
|
+
if (fechar && dlg) fechar.onclick = function () { dlg.close(); };
|
|
413
|
+
|
|
414
|
+
var temas = document.getElementById("temas");
|
|
415
|
+
if (temas) temas.onclick = function (ev) {
|
|
416
|
+
var b = ev.target.closest("[data-tema]"); if (!b) return;
|
|
417
|
+
guardo(CHAVE_TEMA, b.dataset.tema); aplicarTema(b.dataset.tema);
|
|
418
|
+
};
|
|
419
|
+
var cores = document.getElementById("cores");
|
|
420
|
+
if (cores) cores.onclick = function (ev) {
|
|
421
|
+
var b = ev.target.closest("[data-cor]"); if (!b) return;
|
|
422
|
+
guardo(CHAVE_COR, b.dataset.cor); aplicarCor(b.dataset.cor);
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", function () {
|
|
426
|
+
if (pego(CHAVE_TEMA, "escuro") === "sistema") aplicarTema("sistema");
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
desenharMarca();
|
|
431
|
+
aplicarTema(pego(CHAVE_TEMA, "escuro"));
|
|
432
|
+
aplicarCor(pego(CHAVE_COR, "ciano"));
|
|
433
|
+
ligarLado();
|
|
434
|
+
carregarConversas();
|
|
435
|
+
|
|
269
436
|
pedir("/api/estado").then(function (r) { return r.json(); }).then(pintarEstado)
|
|
270
437
|
.catch(function () {});
|
|
271
438
|
ligar();
|