primocode 7.12.0 → 7.12.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/lib/streaming.js +65 -56
- package/package.json +1 -1
package/lib/streaming.js
CHANGED
|
@@ -188,65 +188,74 @@ const MASCOTE_SVG = `<svg viewBox="0 0 9 3.6" aria-hidden="true">
|
|
|
188
188
|
<g fill="#0b1220"><rect x="3" y=".35" width=".8" height=".5" rx=".1"/><rect x="5.2" y=".35" width=".8" height=".5" rx=".1"/><rect x="3.9" y="1.25" width="1.2" height=".28" rx=".14"/></g>
|
|
189
189
|
</svg>`;
|
|
190
190
|
|
|
191
|
+
// A página segue o desenho do dono, traço a traço: fundo grafite, mascote
|
|
192
|
+
// inclinado no canto, "Primocode" branco no alto; a janela de borda branca
|
|
193
|
+
// grossa e canto bem redondo; barra #232323 com as três bolinhas e as abas em
|
|
194
|
+
// texto branco centralizado, separadas por "|"; dentro, a tela preta com a
|
|
195
|
+
// imagem ao vivo. O histórico (miniaturas + passos) fica ABAIXO da janela,
|
|
196
|
+
// discreto — a moldura fica idêntica ao desenho.
|
|
191
197
|
const PAGINA = `<!doctype html>
|
|
192
198
|
<html lang="pt-BR"><head><meta charset="utf-8">
|
|
193
199
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
194
200
|
<title>PrimoCode · Ao vivo</title>
|
|
195
201
|
<style>
|
|
196
202
|
* { box-sizing: border-box; margin: 0; }
|
|
197
|
-
body { background: #
|
|
198
|
-
|
|
199
|
-
/* Cabeçalho: mascote
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
.dot { width: 9px; height: 9px; border-radius: 50%; background: #22d3ee; animation: pulsa 1.4s infinite; }
|
|
203
|
+
body { background: #262626; color: #fff; font-family: 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; min-height: 100vh; display: flex; flex-direction: column; align-items: center; padding: 0 16px 30px; }
|
|
204
|
+
|
|
205
|
+
/* Cabeçalho: mascote inclinado no canto, nome centrado */
|
|
206
|
+
#topo { position: relative; width: min(1100px, 100%); padding: 44px 0 30px; text-align: center; }
|
|
207
|
+
#topo .mascote { position: absolute; left: 6px; top: 30px; width: 96px; height: 40px; transform: rotate(-32deg); }
|
|
208
|
+
#topo .mascote svg { width: 100%; height: 100%; display: block; }
|
|
209
|
+
#topo h1 { font-size: clamp(34px, 6vw, 58px); font-weight: 600; letter-spacing: .5px; }
|
|
210
|
+
#topo .vivo { position: absolute; right: 6px; top: 46px; display: inline-flex; align-items: center; gap: 7px; color: #8a8a8a; font-size: 12px; }
|
|
211
|
+
.dot { width: 9px; height: 9px; border-radius: 50%; background: #46d160; animation: pulsa 1.4s infinite; }
|
|
207
212
|
@keyframes pulsa { 50% { opacity: .25 } }
|
|
208
213
|
|
|
209
|
-
/* A
|
|
210
|
-
#janela { width: min(
|
|
211
|
-
#barra { display: flex; align-items: center;
|
|
212
|
-
#barra .b {
|
|
213
|
-
#barra .b.r {
|
|
214
|
+
/* A janela — borda branca grossa, canto bem redondo */
|
|
215
|
+
#janela { width: min(1100px, 100%); background: #000; border: 10px solid #f5f5f5; border-radius: 46px; overflow: hidden; display: flex; flex-direction: column; }
|
|
216
|
+
#barra { position: relative; display: flex; align-items: center; background: #232323; padding: 14px 26px; min-height: 58px; }
|
|
217
|
+
#barra .b { flex: none; margin-right: 12px; }
|
|
218
|
+
#barra .b.r { width: 17px; height: 17px; border-radius: 50%; background: #d40000; }
|
|
219
|
+
#barra .b.y { width: 30px; height: 17px; border-radius: 50%; background: #ffdd00; }
|
|
220
|
+
#barra .b.g { width: 24px; height: 14px; border-radius: 50%; background: #33cc33; }
|
|
214
221
|
|
|
215
|
-
|
|
222
|
+
/* Abas: texto branco centralizado na barra, separadas por | */
|
|
223
|
+
#abas { position: absolute; left: 50%; transform: translateX(-50%); max-width: 62%; display: flex; align-items: center; gap: 12px; overflow-x: auto; white-space: nowrap; scrollbar-width: none; font-size: clamp(16px, 2.4vw, 24px); font-weight: 500; }
|
|
216
224
|
#abas::-webkit-scrollbar { display: none; }
|
|
217
|
-
.aba { border: 0;
|
|
218
|
-
.aba:hover { color:
|
|
219
|
-
.aba.ativa {
|
|
220
|
-
.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
#tela
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
#
|
|
230
|
-
|
|
225
|
+
.aba { border: 0; background: none; cursor: pointer; font: inherit; color: rgba(255,255,255,.5); padding: 2px 2px; }
|
|
226
|
+
.aba:hover { color: rgba(255,255,255,.8); }
|
|
227
|
+
.aba.ativa { color: #fff; }
|
|
228
|
+
.sep { color: rgba(255,255,255,.45); font-weight: 400; }
|
|
229
|
+
|
|
230
|
+
/* A tela */
|
|
231
|
+
#tela { position: relative; min-height: 470px; display: flex; align-items: center; justify-content: center; background: #000; }
|
|
232
|
+
#tela img { width: 100%; max-height: 64vh; object-fit: contain; display: none; }
|
|
233
|
+
#aviso { color: #fff; text-align: center; font-size: clamp(20px, 3.4vw, 34px); font-weight: 500; padding: 110px 20px; }
|
|
234
|
+
#carimbo { position: absolute; left: 16px; bottom: 12px; background: rgba(0,0,0,.75); border: 1px solid #2e2e2e; color: #bdbdbd; font-size: 12px; padding: 3px 10px; border-radius: 8px; display: none; max-width: 85%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
235
|
+
|
|
236
|
+
/* Histórico: fora da moldura, discreto, abaixo da janela */
|
|
237
|
+
#historico { width: min(1100px, 100%); padding-top: 14px; }
|
|
238
|
+
#filme { display: flex; gap: 6px; padding: 2px 2px 8px; overflow-x: auto; }
|
|
239
|
+
.foto { flex: none; border: 1px solid #3a3a3a; border-radius: 6px; background: #000; cursor: pointer; padding: 0; width: 84px; height: 50px; overflow: hidden; opacity: .7; }
|
|
231
240
|
.foto img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
232
|
-
.foto.ativa { border-color: #
|
|
233
|
-
#passos { max-height:
|
|
234
|
-
.item { padding: 3px
|
|
235
|
-
.item .n { color: #
|
|
236
|
-
.item .ok { color: #
|
|
237
|
-
.item .txt { flex: 1; word-break: break-word; color: #
|
|
238
|
-
.item .txt .erro { color: #
|
|
239
|
-
.item .hora { color: #
|
|
240
|
-
.item.temfoto { cursor: pointer; } .item.temfoto:hover .txt { color: #
|
|
241
|
-
|
|
242
|
-
footer { color: #
|
|
241
|
+
.foto.ativa { border-color: #fff; opacity: 1; }
|
|
242
|
+
#passos { max-height: 170px; overflow-y: auto; }
|
|
243
|
+
.item { padding: 3px 6px; display: flex; gap: 8px; font-size: 13px; }
|
|
244
|
+
.item .n { color: #6f6f6f; min-width: 3ch; text-align: right; }
|
|
245
|
+
.item .ok { color: #46d160; } .item .falha { color: #e5484d; }
|
|
246
|
+
.item .txt { flex: 1; word-break: break-word; color: #d6d6d6; }
|
|
247
|
+
.item .txt .erro { color: #e5484d; font-size: 12px; display: block; }
|
|
248
|
+
.item .hora { color: #6f6f6f; font-size: 11px; }
|
|
249
|
+
.item.temfoto { cursor: pointer; } .item.temfoto:hover .txt { color: #fff; }
|
|
250
|
+
|
|
251
|
+
footer { color: #6f6f6f; font-size: 12px; padding-top: 14px; }
|
|
243
252
|
</style></head>
|
|
244
253
|
<body>
|
|
245
|
-
<
|
|
254
|
+
<div id="topo">
|
|
246
255
|
<div class="mascote">${MASCOTE_SVG}</div>
|
|
247
|
-
<h1
|
|
256
|
+
<h1>Primocode</h1>
|
|
248
257
|
<span class="vivo"><span class="dot"></span><span id="st">conectando…</span></span>
|
|
249
|
-
</
|
|
258
|
+
</div>
|
|
250
259
|
|
|
251
260
|
<div id="janela">
|
|
252
261
|
<div id="barra">
|
|
@@ -255,13 +264,14 @@ const PAGINA = `<!doctype html>
|
|
|
255
264
|
</div>
|
|
256
265
|
<section id="tela">
|
|
257
266
|
<img id="frame" alt="tela que a IA está usando">
|
|
258
|
-
<div id="aviso">Tela que a IA
|
|
267
|
+
<div id="aviso">Tela que a IA está</div>
|
|
259
268
|
<div id="carimbo"></div>
|
|
260
269
|
</section>
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
<div id="historico">
|
|
273
|
+
<div id="filme"></div>
|
|
274
|
+
<div id="passos"></div>
|
|
265
275
|
</div>
|
|
266
276
|
<footer>só nesta máquina (127.0.0.1) · /streaming desliga</footer>
|
|
267
277
|
|
|
@@ -282,14 +292,12 @@ function logDaAba(id) {
|
|
|
282
292
|
}
|
|
283
293
|
|
|
284
294
|
function render() {
|
|
285
|
-
// Abas: "Ao vivo" + uma por app/janela
|
|
295
|
+
// Abas: "Ao vivo" + uma por app/janela, texto separado por |
|
|
286
296
|
const nav = document.getElementById('abas');
|
|
287
297
|
const abas = [{ id: VIVO, nome: 'Ao vivo' }, ...dados.abas];
|
|
288
298
|
nav.innerHTML = abas.map((a) =>
|
|
289
|
-
'<button class="aba' + (a.id === abaSel ? ' ativa' : '') + '" data-id="' + a.id + '">'
|
|
290
|
-
|
|
291
|
-
+ (a.passos ? '<span class="qtd">' + a.passos + '</span>' : '')
|
|
292
|
-
+ '</button>').join('');
|
|
299
|
+
'<button class="aba' + (a.id === abaSel ? ' ativa' : '') + '" data-id="' + a.id + '">' + esc(a.nome) + '</button>'
|
|
300
|
+
).join('<span class="sep">|</span>');
|
|
293
301
|
nav.querySelectorAll('.aba').forEach((b) => b.onclick = () => { abaSel = Number(b.dataset.id); fotoSel = null; render(); });
|
|
294
302
|
|
|
295
303
|
// Imagem: a fixada no filmstrip, senão a mais nova da aba
|
|
@@ -306,7 +314,7 @@ function render() {
|
|
|
306
314
|
carimbo.textContent = '#' + atual.id + ' · ' + atual.hora + ' · ' + atual.acao + (fotoSel != null ? ' (clique na miniatura de novo para voltar ao mais recente)' : '');
|
|
307
315
|
} else {
|
|
308
316
|
img.style.display = 'none'; aviso.style.display = 'block'; carimbo.style.display = 'none';
|
|
309
|
-
if (dados.erroFoto) aviso.
|
|
317
|
+
if (dados.erroFoto) aviso.textContent = 'Sem foto da tela: ' + dados.erroFoto;
|
|
310
318
|
}
|
|
311
319
|
|
|
312
320
|
// Filmstrip da aba (as últimas 14)
|
|
@@ -336,7 +344,7 @@ async function tick() {
|
|
|
336
344
|
try {
|
|
337
345
|
const s = await (await fetch('/state')).json();
|
|
338
346
|
dados = s;
|
|
339
|
-
document.getElementById('st').textContent = 'ao vivo
|
|
347
|
+
document.getElementById('st').textContent = 'ao vivo';
|
|
340
348
|
const assinatura = JSON.stringify([s.frames.length, s.log.length, s.abas.length, s.erroFoto]);
|
|
341
349
|
if (assinatura !== ultimoEstado) { ultimoEstado = assinatura; render(); }
|
|
342
350
|
} catch (e) {
|
|
@@ -348,6 +356,7 @@ tick();
|
|
|
348
356
|
</script>
|
|
349
357
|
</body></html>`;
|
|
350
358
|
|
|
359
|
+
|
|
351
360
|
function start() {
|
|
352
361
|
if (state.on) return Promise.resolve(state.url);
|
|
353
362
|
try { fs.mkdirSync(DIR, { recursive: true }); } catch {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primocode",
|
|
3
|
-
"version": "7.12.
|
|
3
|
+
"version": "7.12.1",
|
|
4
4
|
"description": "PrimoCode — agente de engenharia com IA e cursor próprio. Modelos grátis. Cria arquivos, roda comandos, controla navegador e desktop: abre apps, clica em botões e ícones pelo nome, digita e usa atalhos.",
|
|
5
5
|
"main": "bin/primocode.js",
|
|
6
6
|
"bin": {
|