primocode 9.8.0-beta.6 → 9.8.0-beta.8
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.md +5 -5
- package/app/app.css +482 -281
- package/app/app.js +888 -315
- package/app/index.html +176 -101
- package/app/primo-marca.js +58 -1
- package/bin/primocode.js +77 -26
- package/lib/act.js +71 -7
- package/lib/app.js +297 -89
- package/lib/browser.js +65 -2
- package/lib/fala.js +95 -1
- package/lib/{ide.js → painel.js} +41 -87
- package/lib/pre-leitura.js +118 -0
- package/lib/repeticao.js +4 -2
- package/lib/tempos.js +62 -0
- package/package.json +2 -2
- package/app/ide.css +0 -345
- package/app/ide.html +0 -183
- package/app/ide.js +0 -639
package/app/index.html
CHANGED
|
@@ -8,138 +8,213 @@
|
|
|
8
8
|
<link rel="icon" href="/icone.svg">
|
|
9
9
|
</head>
|
|
10
10
|
<!--
|
|
11
|
-
A janela do PrimoCode
|
|
11
|
+
A janela do PrimoCode — /rc, /ide e `primo ide` abrem esta mesma página.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
O layout é o de um app de mesa de agente: barra lateral com novo chat,
|
|
14
|
+
projetos e recentes; a pergunta no meio da tela vazia; o compositor embaixo
|
|
15
|
+
com projeto, anexo, permissão, esforço e voz; e um painel à direita com a
|
|
16
|
+
prévia do site e os arquivos (a IDE antiga, sem o editor).
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
Os ícones são SVG em linha, de propósito: sem fonte de ícone, sem CDN, sem
|
|
19
|
+
pedir nada fora da máquina (regra do AGENTS.md). Cada um é um <symbol> aqui
|
|
20
|
+
em cima e um <use> onde aparece — o mesmo desenho não é copiado dez vezes.
|
|
21
21
|
-->
|
|
22
22
|
<body>
|
|
23
23
|
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
|
|
25
|
+
<defs>
|
|
26
|
+
<symbol id="i-lado" viewBox="0 0 24 24"><rect x="3.5" y="4.5" width="17" height="15" rx="3"/><path d="M9.5 4.5v15"/></symbol>
|
|
27
|
+
<symbol id="i-painel" viewBox="0 0 24 24"><rect x="3.5" y="4.5" width="17" height="15" rx="3"/><path d="M14.5 4.5v15"/></symbol>
|
|
28
|
+
<symbol id="i-chev" viewBox="0 0 24 24"><path d="m7 10 5 5 5-5"/></symbol>
|
|
29
|
+
<symbol id="i-busca" viewBox="0 0 24 24"><circle cx="11" cy="11" r="6"/><path d="m20 20-4.6-4.6"/></symbol>
|
|
30
|
+
<symbol id="i-sino" viewBox="0 0 24 24"><path d="M6.5 16.5V11a5.5 5.5 0 0 1 11 0v5.5l1.5 1.5H5z"/><path d="M10 20.5a2 2 0 0 0 4 0"/></symbol>
|
|
31
|
+
<symbol id="i-editar" viewBox="0 0 24 24"><path d="M11 4.5H7a2.5 2.5 0 0 0-2.5 2.5v10A2.5 2.5 0 0 0 7 19.5h10a2.5 2.5 0 0 0 2.5-2.5v-4"/><path d="M17.8 3.9a1.8 1.8 0 0 1 2.5 2.5L12 14.7l-3.3.8.8-3.3z"/></symbol>
|
|
32
|
+
<symbol id="i-mais-circ" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8"/><path d="M12 8.5v7M8.5 12h7"/></symbol>
|
|
33
|
+
<symbol id="i-terminal" viewBox="0 0 24 24"><rect x="3.5" y="4.5" width="17" height="15" rx="3"/><path d="m7.5 10 2.5 2-2.5 2M12.5 14.5h4"/></symbol>
|
|
34
|
+
<symbol id="i-olho" viewBox="0 0 24 24"><path d="M2.8 12S6 5.8 12 5.8 21.2 12 21.2 12 18 18.2 12 18.2 2.8 12 2.8 12z"/><circle cx="12" cy="12" r="2.6"/></symbol>
|
|
35
|
+
<symbol id="i-barra" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="m14 7.5-4 9"/></symbol>
|
|
36
|
+
<symbol id="i-ajuda" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M9.7 9.6a2.4 2.4 0 1 1 3.3 2.2c-.6.3-1 .8-1 1.5v.4"/><circle cx="12" cy="16.6" r=".4"/></symbol>
|
|
37
|
+
<symbol id="i-pasta" viewBox="0 0 24 24"><path d="M3.5 7.5A2 2 0 0 1 5.5 5.5h3.8l2 2h7.2a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-13a2 2 0 0 1-2-2z"/></symbol>
|
|
38
|
+
<symbol id="i-mais" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></symbol>
|
|
39
|
+
<symbol id="i-perigo" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M12 7.8v5.2"/><circle cx="12" cy="16.3" r=".5"/></symbol>
|
|
40
|
+
<symbol id="i-escudo" viewBox="0 0 24 24"><path d="M12 3.5 5.5 6v5.2c0 4.3 2.8 7.6 6.5 9.3 3.7-1.7 6.5-5 6.5-9.3V6z"/><path d="m9.2 12 2 2 3.8-4"/></symbol>
|
|
41
|
+
<symbol id="i-mao" viewBox="0 0 24 24"><path d="M12 3.5 5.5 6v5.2c0 4.3 2.8 7.6 6.5 9.3 3.7-1.7 6.5-5 6.5-9.3V6z"/><path d="M12 8.5v4"/><circle cx="12" cy="15.6" r=".5"/></symbol>
|
|
42
|
+
<symbol id="i-mic" viewBox="0 0 24 24"><rect x="9" y="3.5" width="6" height="11" rx="3"/><path d="M5.8 11.5a6.2 6.2 0 0 0 12.4 0M12 17.7v2.8"/></symbol>
|
|
43
|
+
<symbol id="i-seta" viewBox="0 0 24 24"><path d="M12 19V5.5M6.5 11 12 5.5l5.5 5.5"/></symbol>
|
|
44
|
+
<symbol id="i-fechar" viewBox="0 0 24 24"><path d="m6.5 6.5 11 11M17.5 6.5l-11 11"/></symbol>
|
|
45
|
+
<symbol id="i-recarregar" viewBox="0 0 24 24"><path d="M19 12a7 7 0 1 1-2.1-5"/><path d="M19.2 4.5v4h-4"/></symbol>
|
|
46
|
+
<symbol id="i-arquivo" viewBox="0 0 24 24"><path d="M7 3.5h6.5l4.5 4.5v11a1.5 1.5 0 0 1-1.5 1.5h-9.5A1.5 1.5 0 0 1 5.5 19V5A1.5 1.5 0 0 1 7 3.5z"/><path d="M13.5 3.5V8H18"/></symbol>
|
|
47
|
+
<symbol id="i-ler" viewBox="0 0 24 24"><path d="M7 3.5h6.5l4.5 4.5v11a1.5 1.5 0 0 1-1.5 1.5h-9.5A1.5 1.5 0 0 1 5.5 19V5A1.5 1.5 0 0 1 7 3.5z"/><path d="M8.5 12.5h7M8.5 16h5"/></symbol>
|
|
48
|
+
<symbol id="i-rodar" viewBox="0 0 24 24"><path d="m6 8.5 4 3.5-4 3.5M12 16h6"/></symbol>
|
|
49
|
+
<symbol id="i-web" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M3.5 12h17M12 3.5c2.4 2.4 3.4 5.3 3.4 8.5s-1 6.1-3.4 8.5c-2.4-2.4-3.4-5.3-3.4-8.5s1-6.1 3.4-8.5z"/></symbol>
|
|
50
|
+
<symbol id="i-computador" viewBox="0 0 24 24"><rect x="3.5" y="4.5" width="17" height="11.5" rx="2"/><path d="M9 20h6M12 16v4"/></symbol>
|
|
51
|
+
<symbol id="i-plano" viewBox="0 0 24 24"><path d="M9.5 7h10M9.5 12h10M9.5 17h10"/><path d="m4 7 1.2 1.2L7 6.3M4 12l1.2 1.2L7 11.3"/><circle cx="5.5" cy="17" r=".8"/></symbol>
|
|
52
|
+
<symbol id="i-agente" viewBox="0 0 24 24"><rect x="5" y="7.5" width="14" height="11" rx="3"/><path d="M12 7.5V4.5"/><circle cx="9.5" cy="12.5" r=".9"/><circle cx="14.5" cy="12.5" r=".9"/></symbol>
|
|
53
|
+
<symbol id="i-pare" viewBox="0 0 24 24"><rect x="7.5" y="7.5" width="9" height="9" rx="1.6" fill="currentColor" stroke="none"/></symbol>
|
|
54
|
+
</defs>
|
|
55
|
+
</svg>
|
|
56
|
+
|
|
57
|
+
<div class="app" id="app">
|
|
58
|
+
<aside class="lado" id="lado" aria-label="Conversas e projetos">
|
|
59
|
+
<div class="lado-barra">
|
|
60
|
+
<button class="icone-btn" id="btnLado" type="button" aria-label="Recolher a barra lateral" title="Barra lateral (Ctrl+B)">
|
|
61
|
+
<svg class="ic"><use href="#i-lado"/></svg>
|
|
37
62
|
</button>
|
|
38
63
|
</div>
|
|
39
64
|
|
|
40
|
-
<div class="
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
65
|
+
<div class="lado-marca">
|
|
66
|
+
<button class="marca-btn" id="btnMarca" type="button" aria-haspopup="menu">
|
|
67
|
+
<span>PrimoCode</span><svg class="ic ic-p"><use href="#i-chev"/></svg>
|
|
68
|
+
</button>
|
|
69
|
+
<div class="lado-acoes">
|
|
70
|
+
<button class="icone-btn" id="btnBusca" type="button" aria-label="Buscar nos recentes" title="Buscar"><svg class="ic"><use href="#i-busca"/></svg></button>
|
|
71
|
+
<button class="icone-btn" id="btnAvisos" type="button" aria-pressed="false" aria-label="Avisar quando terminar" title="Avisar quando o Primo terminar"><svg class="ic"><use href="#i-sino"/></svg></button>
|
|
44
72
|
</div>
|
|
45
73
|
</div>
|
|
46
74
|
|
|
47
|
-
<div class="
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
</button>
|
|
56
|
-
<div class="chips">
|
|
57
|
-
<span class="chip" id="pasta" title="pasta de trabalho"></span>
|
|
58
|
-
<span class="chip" id="modo"></span>
|
|
59
|
-
<span class="chip" id="esforco"></span>
|
|
75
|
+
<div class="busca" id="busca" hidden>
|
|
76
|
+
<input id="campoBusca" type="search" placeholder="Buscar nos recentes" autocomplete="off">
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<nav class="nav" aria-label="Principal">
|
|
80
|
+
<div class="nav-linha">
|
|
81
|
+
<button class="nav-item ativo" id="btnNovo" type="button"><svg class="ic"><use href="#i-editar"/></svg>Novo chat</button>
|
|
82
|
+
<button class="icone-btn" id="btnNovoProjeto" type="button" aria-label="Novo projeto" title="Novo projeto em ~/primocode"><svg class="ic"><use href="#i-mais-circ"/></svg></button>
|
|
60
83
|
</div>
|
|
84
|
+
<button class="nav-item" id="navTerminal" type="button" aria-pressed="false"><svg class="ic"><use href="#i-terminal"/></svg>Terminal</button>
|
|
85
|
+
<button class="nav-item" id="navPrevia" type="button"><svg class="ic"><use href="#i-olho"/></svg>Prévia do site</button>
|
|
86
|
+
<button class="nav-item" id="navComandos" type="button"><svg class="ic"><use href="#i-barra"/></svg>Comandos</button>
|
|
87
|
+
</nav>
|
|
88
|
+
|
|
89
|
+
<div class="rolagem-lado">
|
|
90
|
+
<div class="secao" id="secaoProjetos">Projetos</div>
|
|
91
|
+
<div class="lista" id="listaProjetos"><div class="vazio-lista">Nenhum projeto</div></div>
|
|
92
|
+
|
|
93
|
+
<div class="secao" id="secaoRecentes">Recentes</div>
|
|
94
|
+
<div class="lista" id="listaRecentes"><div class="vazio-lista">Nenhuma conversa ainda</div></div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div class="lado-pe">
|
|
98
|
+
<div class="usuario"><span class="avatar" id="avatar">PC</span><span class="nome-usuario" id="nomeUsuario">Você</span></div>
|
|
99
|
+
<button class="icone-btn" id="btnAjuda" type="button" aria-label="Atalhos e comandos" title="Atalhos e comandos"><svg class="ic"><use href="#i-ajuda"/></svg></button>
|
|
61
100
|
</div>
|
|
62
101
|
</aside>
|
|
63
102
|
|
|
64
|
-
<main class="
|
|
103
|
+
<main class="centro">
|
|
104
|
+
<header class="topo">
|
|
105
|
+
<button class="icone-btn" id="btnLadoAbrir" type="button" aria-label="Abrir a barra lateral" title="Barra lateral (Ctrl+B)"><svg class="ic"><use href="#i-lado"/></svg></button>
|
|
106
|
+
<div class="topo-titulo" id="topoTitulo"></div>
|
|
107
|
+
<button class="icone-btn" id="btnPainel" type="button" aria-pressed="false" aria-label="Abrir a prévia e os arquivos" title="Prévia e arquivos"><svg class="ic"><use href="#i-painel"/></svg></button>
|
|
108
|
+
</header>
|
|
109
|
+
|
|
65
110
|
<div class="rolo" id="rolo">
|
|
66
|
-
<
|
|
67
|
-
<span id="
|
|
68
|
-
<h1>O que
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<div class="sugestoes">
|
|
72
|
-
<button class="sugestao" data-manda="/all">o que ele sabe fazer</button>
|
|
73
|
-
<button class="sugestao" data-manda="/project">meus projetos</button>
|
|
74
|
-
<button class="sugestao" data-manda="/effort">esforço</button>
|
|
75
|
-
<button class="sugestao" data-manda="/help">comandos</button>
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
<div id="conversa" class="tira" hidden></div>
|
|
111
|
+
<section class="vazio" id="vazio">
|
|
112
|
+
<span class="mascote" id="mascote"></span>
|
|
113
|
+
<h1>O que vamos criar?</h1>
|
|
114
|
+
</section>
|
|
115
|
+
<div class="chat" id="chat" hidden aria-live="polite"></div>
|
|
79
116
|
</div>
|
|
80
117
|
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
118
|
+
<section class="terminal" id="terminal" hidden aria-label="Terminal">
|
|
119
|
+
<header class="terminal-topo">
|
|
120
|
+
<span><b>Terminal</b> <small>o mesmo que aparece no CLI</small></span>
|
|
121
|
+
<button class="icone-btn" id="fecharTerminal" type="button" aria-label="Voltar para o chat"><svg class="ic"><use href="#i-fechar"/></svg></button>
|
|
122
|
+
</header>
|
|
123
|
+
<pre class="terminal-saida" id="terminalSaida"></pre>
|
|
124
|
+
</section>
|
|
125
|
+
|
|
126
|
+
<footer class="rodape">
|
|
127
|
+
<div class="compositor-zona" id="zona">
|
|
128
|
+
<div class="projeto-barra">
|
|
129
|
+
<button class="projeto-btn" id="btnProjeto" type="button" aria-haspopup="menu">
|
|
130
|
+
<svg class="ic"><use href="#i-pasta"/></svg><span id="projetoNome">Escolher projeto</span>
|
|
131
|
+
</button>
|
|
88
132
|
</div>
|
|
89
|
-
<
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
133
|
+
<form class="compositor" id="forma">
|
|
134
|
+
<div class="anexos" id="anexos" hidden></div>
|
|
135
|
+
<textarea id="campo" rows="1" placeholder="Peça qualquer coisa" autocomplete="off" spellcheck="false" aria-label="Pedido"></textarea>
|
|
136
|
+
<div class="controles">
|
|
137
|
+
<button class="icone-btn grande" id="btnAnexar" type="button" aria-label="Anexar arquivo" title="Anexar arquivo"><svg class="ic"><use href="#i-mais"/></svg></button>
|
|
138
|
+
<input id="arquivo" type="file" multiple hidden>
|
|
139
|
+
<button class="chip-perm" id="btnPermissao" type="button" aria-haspopup="menu" data-modo="auto">
|
|
140
|
+
<svg class="ic" id="permIcone"><use href="#i-escudo"/></svg><span id="permRotulo">Pergunta antes do perigoso</span>
|
|
141
|
+
</button>
|
|
142
|
+
<span class="espaco"></span>
|
|
143
|
+
<button class="seletor" id="btnEsforco" type="button" aria-haspopup="menu" title="Esforço do agente (/effort)">
|
|
144
|
+
<b id="esforcoRotulo">Equilíbrio</b><span id="esforcoNome">medium</span><svg class="ic ic-p"><use href="#i-chev"/></svg>
|
|
145
|
+
</button>
|
|
146
|
+
<button class="icone-btn" id="btnMic" type="button" aria-pressed="false" aria-label="Ligar o modo voz" title="Modo voz (/voice)"><svg class="ic"><use href="#i-mic"/></svg></button>
|
|
147
|
+
<button class="enviar" id="btnEnviar" type="submit" disabled aria-label="Enviar" title="Enviar (Enter)"><svg class="ic"><use href="#i-seta"/></svg></button>
|
|
148
|
+
</div>
|
|
149
|
+
</form>
|
|
150
|
+
</div>
|
|
151
|
+
</footer>
|
|
101
152
|
</main>
|
|
102
|
-
</div>
|
|
103
153
|
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
154
|
+
<aside class="painel" id="painel" hidden aria-label="Prévia e arquivos">
|
|
155
|
+
<header class="painel-topo">
|
|
156
|
+
<div class="abas" role="tablist">
|
|
157
|
+
<button class="aba" id="abaPrevia" type="button" role="tab" aria-selected="true">Prévia</button>
|
|
158
|
+
<button class="aba" id="abaArquivos" type="button" role="tab" aria-selected="false">Arquivos</button>
|
|
159
|
+
</div>
|
|
160
|
+
<span class="espaco"></span>
|
|
161
|
+
<button class="icone-btn" id="btnRecarregar" type="button" aria-label="Recarregar" title="Recarregar"><svg class="ic"><use href="#i-recarregar"/></svg></button>
|
|
162
|
+
<button class="icone-btn" id="fecharPainel" type="button" aria-label="Fechar o painel"><svg class="ic"><use href="#i-fechar"/></svg></button>
|
|
163
|
+
</header>
|
|
164
|
+
<div class="painel-corpo" id="corpoPrevia">
|
|
165
|
+
<iframe class="previa" id="previa" title="Prévia do site"
|
|
166
|
+
sandbox="allow-scripts allow-forms allow-modals allow-popups allow-pointer-lock"></iframe>
|
|
167
|
+
<div class="painel-vazio" id="previaVazia" hidden>
|
|
168
|
+
<span class="mascote pequeno" id="mascotePainel"></span>
|
|
169
|
+
<p>Ainda não tem site nesta pasta.</p>
|
|
170
|
+
<p class="fraco">Peça "crie uma página de…" e ele aparece aqui, atualizando sozinho.</p>
|
|
116
171
|
</div>
|
|
117
172
|
</div>
|
|
118
|
-
<div class="
|
|
119
|
-
<
|
|
120
|
-
<div class="
|
|
121
|
-
<
|
|
122
|
-
<
|
|
123
|
-
<button class="cor" data-cor="roxo" style="background:#a78bfa" aria-label="Roxo"></button>
|
|
124
|
-
<button class="cor" data-cor="ambar" style="background:#fbbf24" aria-label="Âmbar"></button>
|
|
125
|
-
<button class="cor" data-cor="rosa" style="background:#fb7185" aria-label="Rosa"></button>
|
|
173
|
+
<div class="painel-corpo arquivos" id="corpoArquivos" hidden>
|
|
174
|
+
<div class="arvore" id="arvore" role="tree"></div>
|
|
175
|
+
<div class="visor">
|
|
176
|
+
<div class="visor-topo" id="visorNome">Escolha um arquivo</div>
|
|
177
|
+
<pre class="visor-texto" id="visor"></pre>
|
|
126
178
|
</div>
|
|
127
179
|
</div>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
180
|
+
</aside>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div class="menu" id="menu" role="menu" hidden></div>
|
|
184
|
+
|
|
185
|
+
<dialog class="dialogo" id="dlgAjuda" aria-labelledby="tituloAjuda">
|
|
186
|
+
<header class="dlg-topo">
|
|
187
|
+
<h2 id="tituloAjuda">Atalhos e comandos</h2>
|
|
188
|
+
<button class="icone-btn" id="fecharAjuda" type="button" aria-label="Fechar"><svg class="ic"><use href="#i-fechar"/></svg></button>
|
|
189
|
+
</header>
|
|
190
|
+
<div class="dlg-corpo">
|
|
191
|
+
<h3>Teclado</h3>
|
|
192
|
+
<dl class="atalhos">
|
|
193
|
+
<dt><kbd>Enter</kbd></dt><dd>manda o pedido</dd>
|
|
194
|
+
<dt><kbd>Shift</kbd> <kbd>Enter</kbd></dt><dd>quebra a linha</dd>
|
|
195
|
+
<dt><kbd>Esc</kbd> <kbd>Esc</kbd></dt><dd>para o que o Primo está fazendo</dd>
|
|
196
|
+
<dt><kbd>Ctrl</kbd> <kbd>B</kbd></dt><dd>mostra ou esconde a barra lateral</dd>
|
|
197
|
+
<dt><kbd>Ctrl</kbd> <kbd>J</kbd></dt><dd>abre o terminal</dd>
|
|
198
|
+
</dl>
|
|
199
|
+
<h3>Comandos</h3>
|
|
200
|
+
<p class="fraco">Digite no campo, como no terminal. Clique para usar.</p>
|
|
201
|
+
<div class="comandos" id="listaComandos">
|
|
202
|
+
<button type="button" data-cmd="/help">/help <span>todos os comandos</span></button>
|
|
203
|
+
<button type="button" data-cmd="/novo ">/novo <span>projeto novo, do zero</span></button>
|
|
204
|
+
<button type="button" data-cmd="/abrir">/abrir <span>abre o site do projeto</span></button>
|
|
205
|
+
<button type="button" data-cmd="/desfazer">/desfazer <span>volta o que ele mexeu</span></button>
|
|
206
|
+
<button type="button" data-cmd="/status">/status <span>conta e servidor</span></button>
|
|
207
|
+
<button type="button" data-cmd="/mascot">/mascot <span>troca o mascote</span></button>
|
|
208
|
+
<button type="button" data-cmd="/login">/login <span>entra na sua conta</span></button>
|
|
209
|
+
<button type="button" data-cmd="/doctor">/doctor <span>confere o que costuma dar problema</span></button>
|
|
136
210
|
</div>
|
|
137
211
|
</div>
|
|
138
212
|
</dialog>
|
|
139
213
|
|
|
140
|
-
<div
|
|
214
|
+
<div class="alvo-solto" id="alvoSolto"><div><strong>Solte aqui</strong><span>o arquivo entra como anexo do pedido</span></div></div>
|
|
141
215
|
|
|
142
216
|
<script src="/primo-marca.js"></script>
|
|
217
|
+
<script src="/ansi.js"></script>
|
|
143
218
|
<script src="/app.js"></script>
|
|
144
219
|
</body>
|
|
145
220
|
</html>
|
package/app/primo-marca.js
CHANGED
|
@@ -131,5 +131,62 @@
|
|
|
131
131
|
return "data:image/svg+xml," + encodeURIComponent(logo({ tamanho: 32 }));
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
/* ── O MASCOTE EM TRAÇO ────────────────────────────────────────────────
|
|
135
|
+
A tela vazia da janela pede um ícone de LINHA, fino e neutro, no lugar
|
|
136
|
+
do bicho em pixel colorido: a janela é cinza e calma, e um mascote de
|
|
137
|
+
blocos cianos no meio dela gritava mais alto que a pergunta. Um traço
|
|
138
|
+
por mascote de lib/ui.js (MASCOTS), mesmo nome de id — quem escolheu a
|
|
139
|
+
coruja no /mascot vê a coruja aqui também. Os três de orelha (primo,
|
|
140
|
+
raposa, gato) têm o ❯_ do prompt no rosto: é a marca do PrimoCode.
|
|
141
|
+
|
|
142
|
+
Tudo em `currentColor`, então a cor vem do CSS e segue o tema. */
|
|
143
|
+
var CORPO_LINHA = "M14 16h20a5 5 0 0 1 5 5v12a5 5 0 0 1-5 5H14a5 5 0 0 1-5-5V21a5 5 0 0 1 5-5z";
|
|
144
|
+
var ROSTO_PROMPT = "M17 23.5l4 3.5-4 3.5M24 31h7";
|
|
145
|
+
var PES = "M17 38v4M31 38v4";
|
|
146
|
+
var LINHAS = {
|
|
147
|
+
primo: [CORPO_LINHA, ROSTO_PROMPT, PES, "M16 16v-6h4v6M28 16v-6h4v6", "M9 26H5M39 26h4"],
|
|
148
|
+
fox: [CORPO_LINHA, ROSTO_PROMPT, PES, "M12 17 9 6l9 10M36 17l3-11-9 10"],
|
|
149
|
+
cat: [CORPO_LINHA, ROSTO_PROMPT, PES, "M14 16l1-7 6 7M34 16l-1-7-6 7"],
|
|
150
|
+
robo: ["M13 14h22a4 4 0 0 1 4 4v14a4 4 0 0 1-4 4H13a4 4 0 0 1-4-4V18a4 4 0 0 1 4-4z",
|
|
151
|
+
"M24 14V9", "c24 6.5 2", "c18 24 2.5", "c30 24 2.5", "M19 31h10", "M16 36v5M32 36v5"],
|
|
152
|
+
coruja: ["M12 11l6 5h12l6-5v19a12 12 0 0 1-24 0z", "c19 24 4", "c29 24 4", "M24 29l-2 3h4z"],
|
|
153
|
+
dragao: [CORPO_LINHA, PES, "M13 16 9 8M35 16l4-8", "M9 22 3 18v10l6-2M39 22l6-4v10l-6-2",
|
|
154
|
+
"c18.5 25 1.5", "c29.5 25 1.5", "M21 31h6"],
|
|
155
|
+
fantasma: ["M12 40V22a12 12 0 0 1 24 0v18l-4-3-4 3-4-3-4 3-4-3z", "c20 23 2", "c28 23 2"],
|
|
156
|
+
capivara: ["M8 24c0-6 5-9 11-9h13c6 0 9 5 9 11v5a6 6 0 0 1-6 6H14a6 6 0 0 1-6-6z",
|
|
157
|
+
"M14 16l1-4 4 3", "c14.5 22.5 1.5", "M8 27h3", "M15 37v4M33 37v4"],
|
|
158
|
+
polvo: ["M14 27a10 10 0 1 1 20 0v1H14z", "c20 22 1.5", "c28 22 1.5",
|
|
159
|
+
"M15 28c-4 4-6 8-3 12M20 28c-1 5-1 9 1 12M28 28c1 5 1 9-1 12M33 28c4 4 6 8 3 12"],
|
|
160
|
+
pinguim: ["M24 7c-8 0-12 8-12 18s4 15 12 15 12-5 12-15S32 7 24 7z",
|
|
161
|
+
"M24 18c-4 0-6 5-6 11s2 8 6 8 6-2 6-8-2-11-6-11z", "c20.5 14 1.2", "c27.5 14 1.2",
|
|
162
|
+
"M22 17l2 2 2-2", "M18 41h5M25 41h5"],
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* O mascote em traço, para a tela vazia.
|
|
167
|
+
* @param {object} op
|
|
168
|
+
* @param {string} [op.mascote] id de lib/ui.js (primo, fox, cat, robo…); desconhecido = primo
|
|
169
|
+
* @param {number} [op.tamanho] lado em px
|
|
170
|
+
* @param {string} [op.rotulo] texto alternativo; vazio = decorativo
|
|
171
|
+
*/
|
|
172
|
+
function linha(op) {
|
|
173
|
+
op = op || {};
|
|
174
|
+
var lado = op.tamanho || 48;
|
|
175
|
+
var partes = LINHAS[op.mascote] || LINHAS.primo;
|
|
176
|
+
var acessivel = op.rotulo
|
|
177
|
+
? ' role="img" aria-label="' + String(op.rotulo).replace(/"/g, """) + '"'
|
|
178
|
+
: ' aria-hidden="true"';
|
|
179
|
+
var corpo = partes.map(function (d) {
|
|
180
|
+
if (d.charAt(0) === "c") { // "cX Y R" = círculo
|
|
181
|
+
var n = d.slice(1).split(" ");
|
|
182
|
+
return '<circle cx="' + n[0] + '" cy="' + n[1] + '" r="' + n[2] + '"/>';
|
|
183
|
+
}
|
|
184
|
+
return '<path d="' + d + '"/>';
|
|
185
|
+
}).join("");
|
|
186
|
+
return '<svg viewBox="0 0 48 48" width="' + lado + '" height="' + lado + '"' + acessivel
|
|
187
|
+
+ ' fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">'
|
|
188
|
+
+ corpo + "</svg>";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
raiz.PrimoMarca = { svg: svg, logo: logo, linha: linha, LINHAS: LINHAS, favicon: favicon, MATRIZ: MATRIZ, CORPO: CORPO, DETALHE: DETALHE };
|
|
135
192
|
})(typeof window !== "undefined" ? window : globalThis);
|
package/bin/primocode.js
CHANGED
|
@@ -53,6 +53,7 @@ extra.instalarContador(api, contadorDaSessao, () => {
|
|
|
53
53
|
const p = provedorLocal.atual();
|
|
54
54
|
return p ? p.nome : 'Servidor PrimoCode';
|
|
55
55
|
});
|
|
56
|
+
const tempos = require('../lib/tempos');
|
|
56
57
|
|
|
57
58
|
// Rede de segurança de último recurso: sem isto, uma exceção fora de um
|
|
58
59
|
// try/catch (ex.: dentro de um callback de socket, como o parser de SSE) não
|
|
@@ -107,10 +108,10 @@ const state = {
|
|
|
107
108
|
// existe para a frente em vez de subir outro servidor ao lado.
|
|
108
109
|
let janelaAberta = null;
|
|
109
110
|
|
|
110
|
-
/* O que
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
/* O que o /rc, o /ide e o `primo --app` passam ao lib/app.js. Uma função
|
|
112
|
+
só porque os três sobem a MESMA janela: se cada um montasse a sua, o /ide
|
|
113
|
+
aberto depois do /rc acharia um servidor sem a pasta do projeto — e o
|
|
114
|
+
painel da prévia nasceria vazio. */
|
|
114
115
|
function opcoesDaJanela(extra) {
|
|
115
116
|
return Object.assign({
|
|
116
117
|
versao: VERSION,
|
|
@@ -122,10 +123,37 @@ function opcoesDaJanela(extra) {
|
|
|
122
123
|
projeto: state.currentProject,
|
|
123
124
|
modo: state.mode,
|
|
124
125
|
esforco: state.config.effort,
|
|
126
|
+
permissoes: perigo.normalizarModo(state.config.permissoes),
|
|
127
|
+
voz: !!vozEmEspera,
|
|
128
|
+
usuario: state.config.userName || null,
|
|
129
|
+
mascote: state.config.mascot || 'primo',
|
|
130
|
+
anexos: state.attachedFiles.map((f) => f.path),
|
|
125
131
|
}),
|
|
126
132
|
// Lida na hora, não guardada: o /dir e o /project trocam a pasta
|
|
127
|
-
// com a
|
|
133
|
+
// com a janela aberta, e o painel tem de acompanhar.
|
|
128
134
|
pastaProjeto: () => projectDir(),
|
|
135
|
+
/* As ações da janela passam pelos MESMOS caminhos dos comandos — o
|
|
136
|
+
/projetos, o /clear, o /permissoes, o /effort, o /attach — para a
|
|
137
|
+
janela e o terminal nunca discordarem sobre onde se está. */
|
|
138
|
+
entrarProjeto: (dir) => {
|
|
139
|
+
entrarNoProjeto(dir, 'projeto');
|
|
140
|
+
console.log(c.ok(` Trabalhando em ${state.currentProject}`) + c.muted(` (${shortPath(dir)})`));
|
|
141
|
+
refreshPrompt();
|
|
142
|
+
},
|
|
143
|
+
novoChat: () => handleSlash('/clear'),
|
|
144
|
+
mudarPermissoes: (m) => handleSlash('/permissoes ' + m),
|
|
145
|
+
mudarEsforco: (n) => aplicarEffort(n),
|
|
146
|
+
anexar: (f) => {
|
|
147
|
+
state.attachedFiles.push(f);
|
|
148
|
+
console.log(c.ok(`Anexado: ${f.path} (${tools.fmtBytes(f.content.length)})`) + c.muted(' pela janela'));
|
|
149
|
+
refreshPrompt();
|
|
150
|
+
},
|
|
151
|
+
desanexar: (nome) => {
|
|
152
|
+
state.attachedFiles = nome ? state.attachedFiles.filter((f) => f.path !== nome) : [];
|
|
153
|
+
refreshPrompt();
|
|
154
|
+
},
|
|
155
|
+
voz: (ligar) => handleSlash(ligar ? '/voice' : '/voice parar'),
|
|
156
|
+
continuarConversa: (alvo) => abrirConversa(alvo),
|
|
129
157
|
}, extra || {});
|
|
130
158
|
}
|
|
131
159
|
|
|
@@ -152,7 +180,7 @@ ${c.bold('Comandos')}
|
|
|
152
180
|
${c.brand('/copiar')} copia a última resposta
|
|
153
181
|
${c.brand('/historico')} [termo] procura nos seus pedidos antigos
|
|
154
182
|
${c.brand('/rc')} abre a janela do PrimoCode (chat, sem terminal)
|
|
155
|
-
${c.brand('/ide')} abre a
|
|
183
|
+
${c.brand('/ide')} abre a janela com a prévia do site e os arquivos ao lado
|
|
156
184
|
${c.brand('/login')} entra na sua conta · ${c.brand('/account')} mostra o plano
|
|
157
185
|
${c.brand('/chats')} [n] suas conversas — as mesmas do app e do celular
|
|
158
186
|
${c.brand('/logout')} desconecta este computador da conta
|
|
@@ -209,7 +237,7 @@ const SLASH_COMANDOS = [
|
|
|
209
237
|
['/copiar', 'copia a última resposta'],
|
|
210
238
|
['/historico', 'procura nos seus pedidos antigos: /historico login'],
|
|
211
239
|
['/rc', 'abre a janela do PrimoCode'],
|
|
212
|
-
['/ide', 'abre a
|
|
240
|
+
['/ide', 'abre a janela com a prévia do site e os arquivos ao lado'],
|
|
213
241
|
['/login', 'entra na sua conta Conecta Primo'],
|
|
214
242
|
['/account', 'sua conta, plano e computadores conectados'],
|
|
215
243
|
['/chats', 'suas conversas salvas, as mesmas do app e do celular'],
|
|
@@ -1055,6 +1083,16 @@ async function sendPrompt(userText) {
|
|
|
1055
1083
|
return;
|
|
1056
1084
|
}
|
|
1057
1085
|
|
|
1086
|
+
// PRIMOCODE_TEMPOS=1: uma linha no fim com o custo de cada fase.
|
|
1087
|
+
tempos.comecar();
|
|
1088
|
+
try { await enviarPedido(userText); }
|
|
1089
|
+
finally {
|
|
1090
|
+
const linha = tempos.relatorio();
|
|
1091
|
+
if (linha) console.log(c.dim(` ⏱ ${linha}`));
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
async function enviarPedido(userText) {
|
|
1058
1096
|
let prompt = userText;
|
|
1059
1097
|
// "@index.html" no pedido é um /attach só deste pedido: vai junto dos
|
|
1060
1098
|
// anexos, mas não fica em state.attachedFiles para o próximo.
|
|
@@ -1091,15 +1129,22 @@ async function sendPrompt(userText) {
|
|
|
1091
1129
|
|
|
1092
1130
|
// O resumo da rodada anterior é feito em segundo plano logo depois
|
|
1093
1131
|
// dela; aqui só se espera ele terminar, para não sair com memória velha.
|
|
1094
|
-
|
|
1095
|
-
|
|
1132
|
+
//
|
|
1096
1133
|
// O pedido passa por uma cabeça antes de virar trabalho: conserta o
|
|
1097
1134
|
// que está torto, diz qual é o entregável e o que conta como pronto.
|
|
1098
1135
|
// UMA chamada faz as duas coisas: arruma o pedido torto e, se fizer
|
|
1099
1136
|
// sentido, divide em passos. Eram duas idas ao modelo por pedido, e
|
|
1100
1137
|
// num plano limitado por REQUISIÇÕES (o grátis da Groq são 30 por
|
|
1101
1138
|
// minuto) cada chamada extra custa tanto quanto token.
|
|
1102
|
-
|
|
1139
|
+
//
|
|
1140
|
+
// As duas esperas correm JUNTAS. Uma não lê o que a outra escreve: o
|
|
1141
|
+
// plano só olha o texto do pedido, e o resumo só a conversa de antes.
|
|
1142
|
+
// Em fila, o pedido de duas fases pagava o resumo e DEPOIS o plano —
|
|
1143
|
+
// duas idas ao modelo em série antes da primeira rodada.
|
|
1144
|
+
const [, passos] = await Promise.all([
|
|
1145
|
+
tempos.medir('espera do resumo', () => esperarResumo()),
|
|
1146
|
+
tempos.medir('plano', () => dividirTarefa(prompt)),
|
|
1147
|
+
]);
|
|
1103
1148
|
const instrucaoKit = await prepararKit(plan, userText);
|
|
1104
1149
|
|
|
1105
1150
|
let r = null;
|
|
@@ -1214,8 +1259,8 @@ async function sendPrompt(userText) {
|
|
|
1214
1259
|
state.gravador.turno(userText, r.assistantMessage.content,
|
|
1215
1260
|
{ modo: 'primocode', projeto: state.currentProject || null });
|
|
1216
1261
|
} else {
|
|
1217
|
-
await esperarResumo();
|
|
1218
|
-
await runChat(prompt, userText);
|
|
1262
|
+
await tempos.medir('espera do resumo', () => esperarResumo());
|
|
1263
|
+
await tempos.medir('chat', () => runChat(prompt, userText));
|
|
1219
1264
|
}
|
|
1220
1265
|
|
|
1221
1266
|
state.attachedFiles = [];
|
|
@@ -2456,26 +2501,26 @@ async function handleSlash(text) {
|
|
|
2456
2501
|
break;
|
|
2457
2502
|
}
|
|
2458
2503
|
|
|
2459
|
-
/*
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2504
|
+
/* O /ide virou a MESMA janela do /rc, abrindo com a prévia do site
|
|
2505
|
+
à direita. O editor de código saiu ("remove a IDE ou deixa
|
|
2506
|
+
idêntico ao Codex"): quem não programa precisa ver o site
|
|
2507
|
+
nascendo, não de um editor para quebrar o que o agente fez. O
|
|
2508
|
+
comando continua existindo para o dedo viciado não bater em nada. */
|
|
2463
2509
|
case 'ide': {
|
|
2464
|
-
if (janelaAberta
|
|
2465
|
-
await appLib.abrirJanela(janelaAberta.
|
|
2466
|
-
console.log(c.ok('
|
|
2467
|
-
console.log(c.dim(` ${janelaAberta.urlIde}`));
|
|
2510
|
+
if (janelaAberta) {
|
|
2511
|
+
await appLib.abrirJanela(janelaAberta.urlPainel || janelaAberta.url);
|
|
2512
|
+
console.log(c.ok(' Janela com a prévia aberta.') + c.muted(' (no mesmo servidor que já estava de pé)'));
|
|
2468
2513
|
break;
|
|
2469
2514
|
}
|
|
2470
|
-
const sp = new Spinner('abrindo a
|
|
2515
|
+
const sp = new Spinner('abrindo a janela').start();
|
|
2471
2516
|
try {
|
|
2472
2517
|
janelaAberta = await appLib.abrir(opcoesDaJanela({ abrirEm: 'ide' }));
|
|
2473
2518
|
sp.stop();
|
|
2474
2519
|
const j = janelaAberta.janela || {};
|
|
2475
|
-
console.log(c.ok('
|
|
2520
|
+
console.log(c.ok(' Janela aberta, com a prévia do site ao lado.')
|
|
2476
2521
|
+ c.muted(j.modo === 'janela' ? ` (${j.navegador})`
|
|
2477
2522
|
: j.modo === 'aba' ? ' (aba do navegador padrão)' : ''));
|
|
2478
|
-
console.log(c.dim(` ${janelaAberta.
|
|
2523
|
+
console.log(c.dim(` ${janelaAberta.urlPainel}`));
|
|
2479
2524
|
if (j.modo === 'nenhum') {
|
|
2480
2525
|
console.log(c.warn(' Não consegui abrir sozinho — copie o endereço acima no navegador.'));
|
|
2481
2526
|
}
|
|
@@ -2801,7 +2846,7 @@ async function main() {
|
|
|
2801
2846
|
return;
|
|
2802
2847
|
}
|
|
2803
2848
|
|
|
2804
|
-
/* `primo ide`: o terminal de sempre, com a
|
|
2849
|
+
/* `primo ide`: o terminal de sempre, com a janela (e a prévia) já aberta.
|
|
2805
2850
|
Só a palavra SOZINHA: `primo ide para gatos` continua sendo um pedido
|
|
2806
2851
|
ao agente (modo um-tiro), como sempre foi. */
|
|
2807
2852
|
const abrirIde = args.length === 1 && args[0].toLowerCase() === 'ide';
|
|
@@ -2921,7 +2966,13 @@ async function main() {
|
|
|
2921
2966
|
try {
|
|
2922
2967
|
if (ehBash(text)) await rodarBash(text);
|
|
2923
2968
|
else if (isSlash(text)) await handleSlash(text);
|
|
2924
|
-
else
|
|
2969
|
+
else {
|
|
2970
|
+
// Com a janela aberta, o pedido digitado AQUI também vira
|
|
2971
|
+
// balão lá — senão a janela mostraria a resposta de uma
|
|
2972
|
+
// pergunta que ninguém fez.
|
|
2973
|
+
if (janelaAberta && janelaAberta.pedidoDoTerminal) janelaAberta.pedidoDoTerminal(text);
|
|
2974
|
+
await sendPrompt(text);
|
|
2975
|
+
}
|
|
2925
2976
|
} catch (e) {
|
|
2926
2977
|
fala.problema(e);
|
|
2927
2978
|
}
|
|
@@ -2986,7 +3037,7 @@ async function main() {
|
|
|
2986
3037
|
// O que sobrou de adiantado não era resposta de nada: vira comando,
|
|
2987
3038
|
// na ordem em que foi digitado. Nada se perde, só esperou a vez.
|
|
2988
3039
|
segurando = false;
|
|
2989
|
-
// Depois da abertura (e do login dela): a
|
|
3040
|
+
// Depois da abertura (e do login dela): a janela sem conta abriria
|
|
2990
3041
|
// mandando pedidos que o servidor recusa.
|
|
2991
3042
|
if (abrirIde) queue.push('/ide');
|
|
2992
3043
|
while (adiantadas.length) queue.push(adiantadas.shift());
|