primocode 8.40.0 → 8.42.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.md +34 -5
- package/bin/primocode.js +90 -0
- package/lib/acervo.js +391 -0
- package/lib/act.js +96 -0
- package/lib/audio-livre.js +6 -1
- package/lib/boas-vindas.js +80 -21
- package/lib/catalogo.js +24 -0
- package/lib/conta.js +24 -3
- package/lib/diretor.js +309 -0
- package/lib/tools.js +43 -0
- package/lib/ui.js +3 -2
- package/package.json +2 -2
- package/studio/__pycache__/edge.cpython-311.pyc +0 -0
- package/studio/__pycache__/elevenlabs.cpython-311.pyc +0 -0
- package/studio/__pycache__/voz.cpython-311.pyc +0 -0
- package/studio/arquivos/corte-teste-de-camera-fb823.json +57 -0
- package/studio/ferramentas/corte.md +42 -0
- package/studio/studio.log +39 -10
- package/studio/web/core.css +178 -0
- package/studio/web/home.html +91 -24
- package/studio/web/js/editor.js +17 -0
- package/studio/web/js/render.js +155 -15
- package/studio/web/render.css +18 -0
- package/studio/arquivos/corte-estatico-corte-a5bab.json +0 -150
- package/studio/arquivos/corte-founders-ai-teste-do-player-2de1d.json +0 -65
- package/studio/arquivos/corte-legado-teste.json +0 -1
- package/studio/arquivos/corte-legado2.json +0 -1
- package/studio/arquivos/corte-minimo-2a829.json +0 -24
- package/studio/arquivos/corte-ok-00a3b.json +0 -18
- package/studio/arquivos/corte-ok-1270e.json +0 -22
- package/studio/arquivos/corte-sweep-corte-cfb29.json +0 -147
- package/studio/arquivos/corte-visual-completo-78f50.json +0 -35
- package/studio/arquivos/grade-sweep-grade-0c2f1.json +0 -119
- package/studio/arquivos/prisma-estatico-prisma-d8740.json +0 -182
- package/studio/arquivos/prisma-sweep-prisma-4c58c.json +0 -166
- package/studio/arquivos/prisma-y-b0785.json +0 -52
- package/studio/arquivos/prosa-sweep-prosa-f29cf.json +0 -80
- package/studio/arquivos/tela-estatico-tela-c7daf.json +0 -90
- package/studio/arquivos/tela-motion-3ecaa.json +0 -135
- package/studio/arquivos/tela-motion-d6150.json +0 -135
- package/studio/arquivos/tela-sweep-tela-0d2bf.json +0 -84
- package/studio/arquivos/traco-estatico-traco-25872.json +0 -113
- package/studio/arquivos/traco-sweep-traco-0bcf4.json +0 -102
package/lib/ui.js
CHANGED
|
@@ -160,8 +160,9 @@ const TIPS = [
|
|
|
160
160
|
];
|
|
161
161
|
|
|
162
162
|
const NEWS = [
|
|
163
|
-
'
|
|
164
|
-
'
|
|
163
|
+
'Vídeo com DIRETOR: roteiro, narração, voz e movimento antes de produzir',
|
|
164
|
+
'Ele busca e baixa imagem, logo e música — com a licença junto',
|
|
165
|
+
'A escolha da IA na abertura agora anda nas setinhas',
|
|
165
166
|
'Esc Esc a qualquer momento para o agente no meio do trabalho',
|
|
166
167
|
];
|
|
167
168
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primocode",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.42.0",
|
|
4
4
|
"description": "PrimoCode — agente de engenharia com IA e cursor próprio. Requer conta Conecta Primo AI (Premium ou Super). 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": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node bin/primocode.js",
|
|
11
|
-
"test": "node test/tools.test.js && node test/ui.test.js && node test/modo.test.js && node test/pasta.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/effort.test.js && node test/claude-engine.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/studio-spec.test.js && node test/studio-versoes.test.js && node test/voz.test.js && node test/editor.test.js && node test/estudio-projeto.test.js && node test/vocabulario.test.js && node test/video-export.test.js && node test/monotonia.test.js && node test/clipe.test.js && node test/midia-envio.test.js && node test/pausas.test.js && node test/fala-repetida.test.js && node test/trilha.test.js && node test/anuncio.test.js && node test/pipeline.test.js && node test/app.test.js && node test/elementos.test.js && node test/remotion.test.js && node test/aplicativo.test.js && node test/conta.test.mjs && node test/nuvem.test.js && node test/codex.test.js && node test/chaves.test.js && node test/prazo.test.js && node test/primeira-vez.test.js && node test/janela.test.js && node test/pareamento.test.js && node test/fala.test.js && node test/especialistas.test.js && node test/icone.test.js && node test/exportar.test.js && node test/ate-o-fim.test.js && node test/motion.test.js && node test/audio-marca.test.js"
|
|
11
|
+
"test": "node test/tools.test.js && node test/ui.test.js && node test/modo.test.js && node test/pasta.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/effort.test.js && node test/claude-engine.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/studio-spec.test.js && node test/studio-versoes.test.js && node test/voz.test.js && node test/editor.test.js && node test/estudio-projeto.test.js && node test/vocabulario.test.js && node test/video-export.test.js && node test/monotonia.test.js && node test/clipe.test.js && node test/midia-envio.test.js && node test/pausas.test.js && node test/fala-repetida.test.js && node test/trilha.test.js && node test/anuncio.test.js && node test/pipeline.test.js && node test/app.test.js && node test/elementos.test.js && node test/remotion.test.js && node test/aplicativo.test.js && node test/conta.test.mjs && node test/nuvem.test.js && node test/codex.test.js && node test/chaves.test.js && node test/prazo.test.js && node test/primeira-vez.test.js && node test/janela.test.js && node test/pareamento.test.js && node test/fala.test.js && node test/especialistas.test.js && node test/icone.test.js && node test/exportar.test.js && node test/ate-o-fim.test.js && node test/motion.test.js && node test/audio-marca.test.js && node test/diretor.test.js && node test/camera.test.js"
|
|
12
12
|
},
|
|
13
13
|
"engines": {
|
|
14
14
|
"node": ">=18.17.0"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "corte-teste-de-camera-fb823",
|
|
3
|
+
"ferramenta": "corte",
|
|
4
|
+
"titulo": "Teste de câmera",
|
|
5
|
+
"doc": {
|
|
6
|
+
"tema": "meia-noite",
|
|
7
|
+
"largura": 1920,
|
|
8
|
+
"altura": 1080,
|
|
9
|
+
"acabamento": "grao",
|
|
10
|
+
"cenas": [
|
|
11
|
+
{
|
|
12
|
+
"layout": "capa",
|
|
13
|
+
"duracao": 4,
|
|
14
|
+
"fundo": "aurora",
|
|
15
|
+
"transicao": "fade",
|
|
16
|
+
"camera": "empurrar",
|
|
17
|
+
"elementos": [
|
|
18
|
+
{
|
|
19
|
+
"tipo": "texto",
|
|
20
|
+
"papel": "chapeu",
|
|
21
|
+
"texto": "Câmera",
|
|
22
|
+
"paralaxe": 1.3
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"tipo": "texto",
|
|
26
|
+
"papel": "titulo",
|
|
27
|
+
"texto": "O quadro se move"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"tipo": "texto",
|
|
31
|
+
"papel": "apoio",
|
|
32
|
+
"texto": "e as camadas andam em velocidades diferentes.",
|
|
33
|
+
"paralaxe": 0.5
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"layout": "numeros",
|
|
39
|
+
"duracao": 3.5,
|
|
40
|
+
"fundo": "malha",
|
|
41
|
+
"transicao": "zoom",
|
|
42
|
+
"camera": "aproximar",
|
|
43
|
+
"elementos": [
|
|
44
|
+
{
|
|
45
|
+
"tipo": "numeros",
|
|
46
|
+
"itens": [
|
|
47
|
+
"+38% | em 12 meses"
|
|
48
|
+
],
|
|
49
|
+
"contar": true,
|
|
50
|
+
"som": "ding"
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"atualizado": "2026-09-11T18:39:36+00:00"
|
|
57
|
+
}
|
|
@@ -796,6 +796,48 @@ Duas chaves já bastam. Quem tem `chaves` ignora `anim` — as duas disputariam
|
|
|
796
796
|
mesmo movimento, e a chave é mais específica. Use em **um ou dois** elementos
|
|
797
797
|
por vídeo: tudo se mexendo ao mesmo tempo cansa e esconde a mensagem.
|
|
798
798
|
|
|
799
|
+
## Câmera da cena — o quadro também se move
|
|
800
|
+
|
|
801
|
+
`chaves` move um elemento. `camera` move o **quadro inteiro**: é o que separa
|
|
802
|
+
motion de slide animado. Na cena:
|
|
803
|
+
|
|
804
|
+
```json
|
|
805
|
+
{ "layout": "capa", "duracao": 4, "camera": "aproximar" }
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
Movimentos com nome: `aproximar` · `afastar` · `pan-direita` · `pan-esquerda` ·
|
|
809
|
+
`subir` · `descer` · `empurrar` (zoom com um grau de giro — a mão humana) ·
|
|
810
|
+
`balanco`.
|
|
811
|
+
|
|
812
|
+
Para um movimento seu, as mesmas chaves dos elementos:
|
|
813
|
+
|
|
814
|
+
```json
|
|
815
|
+
"camera": { "chaves": [ { "t": 0, "escala": 1.15, "x": -40 },
|
|
816
|
+
{ "t": 1, "escala": 1, "x": 0 } ], "suavizar": "saida" }
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
O **fundo não anda junto** — é assim de propósito: o quadro passa sobre o
|
|
820
|
+
fundo, e é daí que vem a sensação de profundidade sem custo nenhum.
|
|
821
|
+
|
|
822
|
+
### Paralaxe — as camadas andam em velocidades diferentes
|
|
823
|
+
|
|
824
|
+
No elemento, `paralaxe` diz o quanto ele acompanha a câmera: `1` é o padrão
|
|
825
|
+
(anda com o quadro), acima de 1 vem à frente e anda **mais**, abaixo de 1 fica
|
|
826
|
+
ao fundo e anda **menos**.
|
|
827
|
+
|
|
828
|
+
```json
|
|
829
|
+
{ "tipo": "imagem", "src": "/midia/cidade.jpg", "paralaxe": 0.4 }
|
|
830
|
+
{ "tipo": "texto", "papel": "titulo", "texto": "São Paulo", "paralaxe": 1.25 }
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
Duas ou três camadas bastam: fundo em 0,3–0,5, conteúdo em 1, o que está na
|
|
834
|
+
frente em 1,2–1,4. Um elemento que já tem `chaves` próprias e está no fluxo
|
|
835
|
+
ignora a paralaxe — a animação dele é mais específica, e embrulhar mudaria o
|
|
836
|
+
espaçamento da cena.
|
|
837
|
+
|
|
838
|
+
**Um movimento de câmera por cena, e não em todas.** Câmera em tudo embrulha o
|
|
839
|
+
olho; o que dá ritmo é alternar cena parada e cena com movimento.
|
|
840
|
+
|
|
799
841
|
## Acabamento
|
|
800
842
|
|
|
801
843
|
Textura por cima da cena inteira — é o que tira a cara de template:
|
package/studio/studio.log
CHANGED
|
@@ -1,28 +1,57 @@
|
|
|
1
|
-
[studio] 2026-09-
|
|
1
|
+
[studio] 2026-09-11T18:38:32+00:00 — iniciando na porta 7777
|
|
2
2
|
|
|
3
3
|
██ PrimoCode Studio
|
|
4
4
|
────────────────────────────────────────────────────────
|
|
5
|
-
Estúdio http://localhost:
|
|
6
|
-
Manual da IA http://localhost:
|
|
5
|
+
Estúdio http://localhost:7777
|
|
6
|
+
Manual da IA http://localhost:7777/api/manual
|
|
7
7
|
Ferramentas Prisma, Tela, Prosa, Grade, Corte, Traço
|
|
8
8
|
────────────────────────────────────────────────────────
|
|
9
9
|
Ctrl+C para parar
|
|
10
10
|
|
|
11
|
+
GET /api/ferramentas
|
|
12
|
+
GET /api/ferramentas
|
|
13
|
+
GET /api/arquivos
|
|
14
|
+
GET /api/ferramentas
|
|
15
|
+
GET /api/arquivos
|
|
16
|
+
GET /api/ferramentas
|
|
11
17
|
GET /api/arquivos
|
|
12
18
|
POST /api/arquivos
|
|
13
|
-
GET /api/arquivos/
|
|
19
|
+
GET /api/arquivos/corte-teste-de-camera-fb823
|
|
20
|
+
GET /api/voz/status
|
|
21
|
+
GET /api/voz/status
|
|
22
|
+
GET /api/arquivos/corte-teste-de-camera-fb823
|
|
23
|
+
GET /api/voz/status
|
|
24
|
+
GET /api/arquivos/corte-teste-de-camera-fb823
|
|
14
25
|
GET /api/voz/status
|
|
15
|
-
[studio] 2026-09-
|
|
26
|
+
[studio] 2026-09-11T18:40:34+00:00 — iniciando na porta 7777
|
|
16
27
|
|
|
17
28
|
██ PrimoCode Studio
|
|
18
29
|
────────────────────────────────────────────────────────
|
|
19
|
-
Estúdio http://localhost:
|
|
20
|
-
Manual da IA http://localhost:
|
|
30
|
+
Estúdio http://localhost:7777
|
|
31
|
+
Manual da IA http://localhost:7777/api/manual
|
|
21
32
|
Ferramentas Prisma, Tela, Prosa, Grade, Corte, Traço
|
|
22
33
|
────────────────────────────────────────────────────────
|
|
23
34
|
Ctrl+C para parar
|
|
24
35
|
|
|
25
|
-
GET /api/arquivos
|
|
26
|
-
|
|
27
|
-
GET /api/
|
|
36
|
+
GET /api/arquivos/corte-teste-de-camera-fb823
|
|
37
|
+
GET /api/voz/status
|
|
38
|
+
GET /api/voz/status
|
|
39
|
+
GET /api/voz/status
|
|
40
|
+
GET /api/voz/status
|
|
41
|
+
GET /api/arquivos/corte-teste-de-camera-fb823
|
|
42
|
+
GET /api/voz/status
|
|
43
|
+
GET /api/voz/status
|
|
44
|
+
GET /api/arquivos/corte-teste-de-camera-fb823
|
|
45
|
+
GET /api/voz/status
|
|
46
|
+
GET /api/voz/status
|
|
47
|
+
GET /api/arquivos/corte-teste-de-camera-fb823
|
|
48
|
+
GET /api/voz/status
|
|
49
|
+
GET /api/voz/status
|
|
50
|
+
GET /api/arquivos/corte-teste-de-camera-fb823
|
|
51
|
+
GET /api/voz/status
|
|
52
|
+
GET /api/voz/status
|
|
53
|
+
GET /api/arquivos/corte-teste-de-camera-fb823
|
|
28
54
|
GET /api/voz/status
|
|
55
|
+
GET /api/ferramentas
|
|
56
|
+
GET /api/versao
|
|
57
|
+
POST /api/sair
|
package/studio/web/core.css
CHANGED
|
@@ -987,3 +987,181 @@ body {
|
|
|
987
987
|
font: 400 11.5px/1.2 var(--sans);
|
|
988
988
|
color: var(--fraco);
|
|
989
989
|
}
|
|
990
|
+
|
|
991
|
+
/* ==========================================================================
|
|
992
|
+
REDESENHO DA CAPA — o estúdio se apresentando
|
|
993
|
+
--------------------------------------------------------------------------
|
|
994
|
+
O que estava aqui era uma página de lista: título, cartões, lista de
|
|
995
|
+
arquivos. Funcionava e não dizia nada — quem abria pela primeira vez não
|
|
996
|
+
descobria que o vídeo sai com narração, câmera e keyframes, nem que dá para
|
|
997
|
+
pedir tudo por texto ao agente.
|
|
998
|
+
|
|
999
|
+
Este bloco vem DEPOIS do núcleo de propósito: ele acrescenta a capa, os
|
|
1000
|
+
poderes e o acabamento dos cartões sem reescrever a primitiva (.btn, .campo,
|
|
1001
|
+
.topo) que o editor usa — mudar aquilo mexeria em cinco telas de uma vez.
|
|
1002
|
+
========================================================================== */
|
|
1003
|
+
|
|
1004
|
+
.home { max-width: 1140px; padding-top: 34px; }
|
|
1005
|
+
|
|
1006
|
+
.busca-topo { width: 220px; background: var(--sup2); }
|
|
1007
|
+
.busca-topo::placeholder { color: var(--fraco2); }
|
|
1008
|
+
@media (max-width: 720px) { .busca-topo { display: none; } }
|
|
1009
|
+
|
|
1010
|
+
/* ------------------------------------------------------------------ capa -- */
|
|
1011
|
+
|
|
1012
|
+
.capa {
|
|
1013
|
+
position: relative;
|
|
1014
|
+
display: grid;
|
|
1015
|
+
grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
|
|
1016
|
+
gap: 30px;
|
|
1017
|
+
align-items: center;
|
|
1018
|
+
padding: 46px 40px;
|
|
1019
|
+
margin-bottom: 54px;
|
|
1020
|
+
border: 1px solid var(--linha);
|
|
1021
|
+
border-radius: 22px;
|
|
1022
|
+
background: linear-gradient(180deg, var(--sup) 0%, #101014 100%);
|
|
1023
|
+
overflow: hidden;
|
|
1024
|
+
}
|
|
1025
|
+
/* A luz é a única coisa que se move na capa, e devagar: movimento na página
|
|
1026
|
+
inteira briga com o vídeo que a pessoa veio fazer. */
|
|
1027
|
+
.capa-luz {
|
|
1028
|
+
position: absolute;
|
|
1029
|
+
inset: -40% -10% auto -20%;
|
|
1030
|
+
height: 520px;
|
|
1031
|
+
background:
|
|
1032
|
+
radial-gradient(420px 240px at 20% 40%, rgba(255, 90, 54, .22), transparent 70%),
|
|
1033
|
+
radial-gradient(360px 220px at 70% 30%, rgba(139, 92, 246, .18), transparent 72%);
|
|
1034
|
+
filter: blur(6px);
|
|
1035
|
+
animation: capaLuz 18s ease-in-out infinite alternate;
|
|
1036
|
+
pointer-events: none;
|
|
1037
|
+
}
|
|
1038
|
+
@keyframes capaLuz { from { transform: translate3d(-3%, 0, 0); } to { transform: translate3d(6%, 3%, 0); } }
|
|
1039
|
+
|
|
1040
|
+
.capa-texto { position: relative; z-index: 1; }
|
|
1041
|
+
.capa h1 {
|
|
1042
|
+
font-size: 40px;
|
|
1043
|
+
line-height: 1.12;
|
|
1044
|
+
font-weight: 680;
|
|
1045
|
+
letter-spacing: -.04em;
|
|
1046
|
+
margin: 14px 0 12px;
|
|
1047
|
+
}
|
|
1048
|
+
.capa h1 .grad {
|
|
1049
|
+
background: linear-gradient(92deg, var(--primo), #ff9f7a 55%, #8b5cf6);
|
|
1050
|
+
-webkit-background-clip: text;
|
|
1051
|
+
background-clip: text;
|
|
1052
|
+
color: transparent;
|
|
1053
|
+
}
|
|
1054
|
+
.capa p { color: var(--fraco); font-size: 14.5px; line-height: 1.7; margin: 0 0 22px; max-width: 560px; }
|
|
1055
|
+
.capa-acoes { display: flex; gap: 10px; flex-wrap: wrap; }
|
|
1056
|
+
.btn.grande { height: 40px; padding: 0 18px; font-size: 13.5px; border-radius: 12px; }
|
|
1057
|
+
|
|
1058
|
+
.selo-vivo { color: var(--fraco); }
|
|
1059
|
+
.selo-vivo i {
|
|
1060
|
+
width: 7px; height: 7px; border-radius: 999px; background: #4ade80;
|
|
1061
|
+
box-shadow: 0 0 0 3px rgba(74, 222, 128, .16);
|
|
1062
|
+
animation: pulso 2.4s ease-in-out infinite;
|
|
1063
|
+
}
|
|
1064
|
+
@keyframes pulso { 50% { box-shadow: 0 0 0 6px rgba(74, 222, 128, 0); } }
|
|
1065
|
+
|
|
1066
|
+
/* A arte da direita é o produto em miniatura: três quadros de cena, com a
|
|
1067
|
+
mesma linguagem do render. Nada de screenshot — captura envelhece e mente. */
|
|
1068
|
+
.capa-arte { position: relative; height: 250px; }
|
|
1069
|
+
.capa-arte .quadro {
|
|
1070
|
+
position: absolute;
|
|
1071
|
+
border: 1px solid var(--linha2);
|
|
1072
|
+
border-radius: 14px;
|
|
1073
|
+
background: linear-gradient(160deg, #16161d, #0c0c11);
|
|
1074
|
+
box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
|
|
1075
|
+
animation: flutua 9s ease-in-out infinite alternate;
|
|
1076
|
+
}
|
|
1077
|
+
.capa-arte .q1 {
|
|
1078
|
+
left: 0; top: 16px; width: 62%; height: 132px; padding: 16px;
|
|
1079
|
+
display: flex; flex-direction: column; gap: 9px; justify-content: center;
|
|
1080
|
+
}
|
|
1081
|
+
.capa-arte .q1 span { height: 9px; border-radius: 5px; background: rgba(255, 255, 255, .16); }
|
|
1082
|
+
.capa-arte .q1 span:first-child { width: 42%; background: var(--primo); height: 11px; }
|
|
1083
|
+
.capa-arte .q1 span:last-child { width: 68%; }
|
|
1084
|
+
.capa-arte .q2 {
|
|
1085
|
+
right: 6px; top: 0; width: 42%; height: 104px;
|
|
1086
|
+
display: grid; place-content: center; text-align: center;
|
|
1087
|
+
animation-delay: -3s;
|
|
1088
|
+
}
|
|
1089
|
+
.capa-arte .q2 b { font-size: 26px; font-weight: 680; letter-spacing: -.03em; display: block; }
|
|
1090
|
+
.capa-arte .q2 i { font-size: 11px; color: var(--fraco2); font-style: normal; }
|
|
1091
|
+
.capa-arte .q3 {
|
|
1092
|
+
right: 22px; bottom: 4px; width: 54%; height: 96px; padding: 14px;
|
|
1093
|
+
display: flex; align-items: flex-end; gap: 8px;
|
|
1094
|
+
animation-delay: -6s;
|
|
1095
|
+
}
|
|
1096
|
+
.capa-arte .q3 u { flex: 1; border-radius: 4px 4px 0 0; background: linear-gradient(180deg, var(--primo), rgba(255, 90, 54, .25)); }
|
|
1097
|
+
.capa-arte .q3 u:nth-child(1) { height: 38%; }
|
|
1098
|
+
.capa-arte .q3 u:nth-child(2) { height: 72%; }
|
|
1099
|
+
.capa-arte .q3 u:nth-child(3) { height: 54%; }
|
|
1100
|
+
@keyframes flutua { from { transform: translateY(-5px); } to { transform: translateY(7px); } }
|
|
1101
|
+
|
|
1102
|
+
@media (max-width: 880px) {
|
|
1103
|
+
.capa { grid-template-columns: 1fr; padding: 34px 24px; }
|
|
1104
|
+
.capa-arte { display: none; }
|
|
1105
|
+
.capa h1 { font-size: 31px; }
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/* ------------------------------------------------------- cartões e listas -- */
|
|
1109
|
+
|
|
1110
|
+
/* Seis ferramentas em três colunas fecham duas fileiras cheias; em quatro
|
|
1111
|
+
colunas sobra uma fileira pela metade, e a página parece interrompida. */
|
|
1112
|
+
.grade-ferr {
|
|
1113
|
+
gap: 16px;
|
|
1114
|
+
margin-bottom: 52px;
|
|
1115
|
+
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
.cartao-ferr { padding: 22px; background: linear-gradient(180deg, var(--sup), #101015); }
|
|
1119
|
+
.cartao-ferr .brilho {
|
|
1120
|
+
position: absolute; inset: -1px -1px auto -1px; height: 2px;
|
|
1121
|
+
background: linear-gradient(90deg, transparent, var(--cor, var(--primo)), transparent);
|
|
1122
|
+
opacity: 0; transition: opacity .18s;
|
|
1123
|
+
}
|
|
1124
|
+
.cartao-ferr:hover .brilho, .cartao-ferr:focus-visible .brilho { opacity: .9; }
|
|
1125
|
+
.cartao-ferr:focus-visible { outline: 2px solid var(--cor, var(--primo)); outline-offset: 2px; }
|
|
1126
|
+
.cartao-ferr .icone {
|
|
1127
|
+
width: 38px; height: 38px; border-radius: 11px;
|
|
1128
|
+
background: color-mix(in srgb, var(--cor, var(--primo)) 16%, transparent);
|
|
1129
|
+
color: var(--cor, var(--primo));
|
|
1130
|
+
border: 1px solid color-mix(in srgb, var(--cor, var(--primo)) 28%, transparent);
|
|
1131
|
+
}
|
|
1132
|
+
.cartao-ferr .icone svg { width: 19px; height: 19px; }
|
|
1133
|
+
.cartao-ferr:hover { transform: translateY(-3px); box-shadow: 0 18px 40px rgba(0, 0, 0, .35); }
|
|
1134
|
+
|
|
1135
|
+
.item-arq { border-radius: 12px; }
|
|
1136
|
+
.item-arq .tag {
|
|
1137
|
+
background: color-mix(in srgb, var(--cor, var(--primo)) 16%, transparent);
|
|
1138
|
+
color: var(--cor, var(--primo));
|
|
1139
|
+
}
|
|
1140
|
+
.item-arq:hover { transform: translateX(2px); }
|
|
1141
|
+
|
|
1142
|
+
/* ------------------------------------------------------------- poderes --- */
|
|
1143
|
+
|
|
1144
|
+
.poderes { margin-bottom: 50px; }
|
|
1145
|
+
.grade-poder {
|
|
1146
|
+
display: grid;
|
|
1147
|
+
grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
|
|
1148
|
+
gap: 12px;
|
|
1149
|
+
}
|
|
1150
|
+
.grade-poder article {
|
|
1151
|
+
border: 1px solid var(--linha);
|
|
1152
|
+
border-radius: var(--r2);
|
|
1153
|
+
background: var(--sup);
|
|
1154
|
+
padding: 16px 18px;
|
|
1155
|
+
}
|
|
1156
|
+
.grade-poder b { font-size: 13.5px; font-weight: 620; display: block; margin-bottom: 5px; }
|
|
1157
|
+
.grade-poder p { margin: 0; font-size: 12.5px; line-height: 1.6; color: var(--fraco); }
|
|
1158
|
+
|
|
1159
|
+
.copiado { font-size: 12px; color: #4ade80; opacity: 0; transition: opacity .2s; }
|
|
1160
|
+
.copiado.on { opacity: 1; }
|
|
1161
|
+
|
|
1162
|
+
/* Quem pediu menos movimento recebe menos movimento — a capa continua
|
|
1163
|
+
inteira, só para de respirar. */
|
|
1164
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1165
|
+
.capa-luz, .capa-arte .quadro, .selo-vivo i { animation: none; }
|
|
1166
|
+
.cartao-ferr:hover, .item-arq:hover { transform: none; }
|
|
1167
|
+
}
|
package/studio/web/home.html
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
6
|
<title>PrimoCode Studio</title>
|
|
7
7
|
<link rel="stylesheet" href="/core.css">
|
|
8
|
+
<link rel="stylesheet" href="/fontes.css">
|
|
8
9
|
</head>
|
|
9
10
|
<body>
|
|
10
11
|
|
|
@@ -27,22 +28,48 @@
|
|
|
27
28
|
<header class="topo">
|
|
28
29
|
<a class="marca" href="/"><span class="cubo">P</span>PrimoCode <em>Studio</em></a>
|
|
29
30
|
<div class="cresce"></div>
|
|
31
|
+
<input class="campo busca-topo" id="busca" placeholder="Buscar no estúdio /" autocomplete="off">
|
|
30
32
|
<button class="btn vazio" id="btn-manual">Manual da IA</button>
|
|
31
33
|
<a class="btn vazio" href="/api/manual" target="_blank">API</a>
|
|
32
34
|
</header>
|
|
33
35
|
|
|
34
36
|
<div class="home">
|
|
35
37
|
|
|
36
|
-
<section class="
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
<section class="capa">
|
|
39
|
+
<div class="capa-luz" aria-hidden="true"></div>
|
|
40
|
+
<div class="capa-texto">
|
|
41
|
+
<span class="pilula selo-vivo"><i></i> o estúdio está no ar em localhost:7777</span>
|
|
42
|
+
<h1>Seis ferramentas.<br>Um documento só.<br><span class="grad">A IA opera tudo.</span></h1>
|
|
43
|
+
<p>Apresentação, arte, texto, planilha, vídeo e diagrama nascem do mesmo modelo —
|
|
44
|
+
a IA escreve o documento, o estúdio desenha, você refina à mão. Vídeo sai com
|
|
45
|
+
narração neural, trilha, efeito sonoro, câmera e keyframes em 3D, e baixa em MP4.</p>
|
|
46
|
+
<div class="capa-acoes">
|
|
47
|
+
<button class="btn primo grande" id="ir-corte">Criar um vídeo</button>
|
|
48
|
+
<button class="btn grande" id="ir-prisma">Criar uma apresentação</button>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="capa-arte" aria-hidden="true">
|
|
52
|
+
<div class="quadro q1"><span></span><span></span><span></span></div>
|
|
53
|
+
<div class="quadro q2"><b>+38%</b><i>em 12 meses</i></div>
|
|
54
|
+
<div class="quadro q3"><u></u><u></u><u></u></div>
|
|
55
|
+
</div>
|
|
42
56
|
</section>
|
|
43
57
|
|
|
58
|
+
<h2 class="secao-tit" id="tit-ferramentas">As ferramentas</h2>
|
|
44
59
|
<section class="grade-ferr" id="ferramentas"></section>
|
|
45
60
|
|
|
61
|
+
<section class="poderes">
|
|
62
|
+
<h2 class="secao-tit">O que a IA consegue fazer aqui</h2>
|
|
63
|
+
<div class="grade-poder">
|
|
64
|
+
<article><b>Diretor antes da produção</b><p>Roteiro, narração falada, voz, câmera e a mídia a buscar — decidido antes de a primeira cena existir.</p></article>
|
|
65
|
+
<article><b>Câmera e paralaxe</b><p>O quadro se aproxima, atravessa e balança; as camadas andam em velocidades diferentes.</p></article>
|
|
66
|
+
<article><b>Keyframes em 3D</b><p>Giro em profundidade, curvas com nome (elástico, salto) e som no instante em que o elemento entra.</p></article>
|
|
67
|
+
<article><b>Mídia da internet</b><p>Foto, logo em SVG da marca e música com licença aberta — com o crédito guardado junto do arquivo.</p></article>
|
|
68
|
+
<article><b>Narração neural</b><p>Voz sintetizada em pt-BR, sincronia automática com a cena e a trilha abaixando sozinha enquanto a voz fala.</p></article>
|
|
69
|
+
<article><b>MP4 sem clique</b><p>Exporta o arquivo direto, ou vira projeto Remotion quando o vídeo é longo ou precisa de canal alfa.</p></article>
|
|
70
|
+
</div>
|
|
71
|
+
</section>
|
|
72
|
+
|
|
46
73
|
<section class="faixa-ia">
|
|
47
74
|
<h2>Como pedir</h2>
|
|
48
75
|
<p>Fale com seu agente em português normal. Ele lê o manual, escolhe a ferramenta e devolve o link.</p>
|
|
@@ -51,7 +78,7 @@ usando o Prisma. Me devolva a URL "ver" da resposta.</pre>
|
|
|
51
78
|
<div class="acoes">
|
|
52
79
|
<button class="btn primo" id="copiar">Copiar</button>
|
|
53
80
|
<a class="btn" href="/api/manual" target="_blank">Ler o manual</a>
|
|
54
|
-
<span id="copiado"
|
|
81
|
+
<span id="copiado" class="copiado">copiado</span>
|
|
55
82
|
</div>
|
|
56
83
|
</section>
|
|
57
84
|
|
|
@@ -70,15 +97,16 @@ usando o Prisma. Me devolva a URL "ver" da resposta.</pre>
|
|
|
70
97
|
(function () {
|
|
71
98
|
var $ = function (s) { return document.querySelector(s); };
|
|
72
99
|
var FERRS = [];
|
|
100
|
+
var ARQUIVOS = [];
|
|
73
101
|
|
|
74
102
|
function fugir(s) { return String(s).replace(/&/g, "&").replace(/</g, "<"); }
|
|
75
103
|
|
|
76
104
|
fetch("/api/ferramentas").then(function (r) { return r.json(); }).then(function (lista) {
|
|
77
105
|
FERRS = lista;
|
|
78
106
|
$("#ferramentas").innerHTML = lista.map(function (f) {
|
|
79
|
-
return '<div class="cartao-ferr" data-slug="' + f.slug + '">' +
|
|
80
|
-
'<div class="
|
|
81
|
-
svgIcone(iconeDe(f.slug)) + "</div>" +
|
|
107
|
+
return '<div class="cartao-ferr" data-slug="' + f.slug + '" style="--cor:' + f.cor + '" tabindex="0" role="button">' +
|
|
108
|
+
'<div class="brilho" aria-hidden="true"></div>' +
|
|
109
|
+
'<div class="icone">' + svgIcone(iconeDe(f.slug)) + "</div>" +
|
|
82
110
|
"<b>" + fugir(f.nome) + "</b>" +
|
|
83
111
|
'<div class="papel">' + fugir(f.papel) + "</div>" +
|
|
84
112
|
"<p>" + fugir(f.resumo) + "</p>" +
|
|
@@ -87,6 +115,11 @@ usando o Prisma. Me devolva a URL "ver" da resposta.</pre>
|
|
|
87
115
|
|
|
88
116
|
Array.prototype.forEach.call(document.querySelectorAll(".cartao-ferr"), function (c) {
|
|
89
117
|
c.onclick = function () { criar(c.dataset.slug); };
|
|
118
|
+
// Teclado abre igual ao mouse: o cartão é um botão, e um botão que só
|
|
119
|
+
// responde a clique deixa de fora quem navega por Tab.
|
|
120
|
+
c.onkeydown = function (ev) {
|
|
121
|
+
if (ev.key === "Enter" || ev.key === " ") { ev.preventDefault(); criar(c.dataset.slug); }
|
|
122
|
+
};
|
|
90
123
|
});
|
|
91
124
|
carregarArquivos();
|
|
92
125
|
});
|
|
@@ -110,28 +143,59 @@ usando o Prisma. Me devolva a URL "ver" da resposta.</pre>
|
|
|
110
143
|
});
|
|
111
144
|
}
|
|
112
145
|
|
|
146
|
+
$("#ir-corte").onclick = function () { criar("corte"); };
|
|
147
|
+
$("#ir-prisma").onclick = function () { criar("prisma"); };
|
|
148
|
+
|
|
113
149
|
function carregarArquivos() {
|
|
114
150
|
fetch("/api/arquivos").then(function (r) { return r.json(); }).then(function (lista) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
var cor = corDe(a.ferramenta);
|
|
118
|
-
var quando = a.atualizado ? a.atualizado.slice(0, 10).split("-").reverse().join("/") : "";
|
|
119
|
-
return '<div class="item-arq">' +
|
|
120
|
-
'<div class="tag" style="background:' + cor + '22;color:' + cor + '">' + svgIcone(iconeDe(a.ferramenta)) + "</div>" +
|
|
121
|
-
'<div class="cresce"><b>' + fugir(a.titulo) + "</b>" +
|
|
122
|
-
'<div class="meta">' + fugir(a.ferramenta) + " · " + a.pecas +
|
|
123
|
-
(a.pecas === 1 ? " peça · " : " peças · ") + quando + "</div></div>" +
|
|
124
|
-
'<a class="btn" href="/t/' + a.ferramenta + "/" + a.id + '">Editar</a>' +
|
|
125
|
-
'<a class="btn primo" href="/v/' + a.id + '" target="_blank">Abrir</a></div>';
|
|
126
|
-
}).join("");
|
|
151
|
+
ARQUIVOS = lista;
|
|
152
|
+
desenharArquivos(lista);
|
|
127
153
|
});
|
|
128
154
|
}
|
|
129
155
|
|
|
156
|
+
function desenharArquivos(lista) {
|
|
157
|
+
if (!lista.length) {
|
|
158
|
+
$("#arquivos").innerHTML = "";
|
|
159
|
+
$("#vazio").style.display = "block";
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
$("#vazio").style.display = "none";
|
|
163
|
+
$("#arquivos").innerHTML = lista.map(function (a) {
|
|
164
|
+
var cor = corDe(a.ferramenta);
|
|
165
|
+
var quando = a.atualizado ? a.atualizado.slice(0, 10).split("-").reverse().join("/") : "";
|
|
166
|
+
return '<div class="item-arq" style="--cor:' + cor + '">' +
|
|
167
|
+
'<div class="tag">' + svgIcone(iconeDe(a.ferramenta)) + "</div>" +
|
|
168
|
+
'<div class="cresce"><b>' + fugir(a.titulo) + "</b>" +
|
|
169
|
+
'<div class="meta">' + fugir(a.ferramenta) + " · " + a.pecas +
|
|
170
|
+
(a.pecas === 1 ? " peça · " : " peças · ") + quando + "</div></div>" +
|
|
171
|
+
'<a class="btn" href="/t/' + a.ferramenta + "/" + a.id + '">Editar</a>' +
|
|
172
|
+
'<a class="btn primo" href="/v/' + a.id + '" target="_blank">Abrir</a></div>';
|
|
173
|
+
}).join("");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* A BUSCA filtra o que já está na tela — ferramentas e arquivos.
|
|
177
|
+
Sem ida ao servidor: a lista inteira já veio, e um fetch por tecla
|
|
178
|
+
digitada seria trabalho para devolver o que está na memória. */
|
|
179
|
+
var busca = $("#busca");
|
|
180
|
+
busca.oninput = function () {
|
|
181
|
+
var q = busca.value.trim().toLowerCase();
|
|
182
|
+
Array.prototype.forEach.call(document.querySelectorAll(".cartao-ferr"), function (c) {
|
|
183
|
+
c.style.display = !q || c.textContent.toLowerCase().indexOf(q) >= 0 ? "" : "none";
|
|
184
|
+
});
|
|
185
|
+
desenharArquivos(!q ? ARQUIVOS : ARQUIVOS.filter(function (a) {
|
|
186
|
+
return (a.titulo + " " + a.ferramenta).toLowerCase().indexOf(q) >= 0;
|
|
187
|
+
}));
|
|
188
|
+
};
|
|
189
|
+
document.addEventListener("keydown", function (ev) {
|
|
190
|
+
if (ev.key === "/" && document.activeElement !== busca) { ev.preventDefault(); busca.focus(); }
|
|
191
|
+
if (ev.key === "Escape" && document.activeElement === busca) { busca.value = ""; busca.oninput(); busca.blur(); }
|
|
192
|
+
});
|
|
193
|
+
|
|
130
194
|
$("#copiar").onclick = function () {
|
|
131
195
|
navigator.clipboard.writeText($("#frase").textContent).then(function () {
|
|
132
196
|
var c = $("#copiado");
|
|
133
|
-
c.
|
|
134
|
-
setTimeout(function () { c.
|
|
197
|
+
c.classList.add("on");
|
|
198
|
+
setTimeout(function () { c.classList.remove("on"); }, 1500);
|
|
135
199
|
});
|
|
136
200
|
};
|
|
137
201
|
|
|
@@ -144,6 +208,9 @@ usando o Prisma. Me devolva a URL "ver" da resposta.</pre>
|
|
|
144
208
|
}
|
|
145
209
|
};
|
|
146
210
|
folha.onclick = function (e) { if (e.target === folha) folha.classList.remove("on"); };
|
|
211
|
+
document.addEventListener("keydown", function (e) {
|
|
212
|
+
if (e.key === "Escape") folha.classList.remove("on");
|
|
213
|
+
});
|
|
147
214
|
})();
|
|
148
215
|
</script>
|
|
149
216
|
</body>
|
package/studio/web/js/editor.js
CHANGED
|
@@ -24,6 +24,12 @@
|
|
|
24
24
|
Os valores NÃO mudam — são os mesmos que o render.css entende. O que
|
|
25
25
|
muda é só o que a pessoa lê. */
|
|
26
26
|
var OPC = {
|
|
27
|
+
// A câmera da cena: o quadro que se move. Os valores são os mesmos que o
|
|
28
|
+
// render.js conhece (CAMERAS); aqui vão só os nomes que se leem.
|
|
29
|
+
camera: [["", "Parada"], ["aproximar", "Aproximar"], ["afastar", "Afastar"],
|
|
30
|
+
["pan-direita", "Atravessar para a direita"], ["pan-esquerda", "Atravessar para a esquerda"],
|
|
31
|
+
["subir", "Subir"], ["descer", "Descer"], ["empurrar", "Empurrão (zoom com giro)"],
|
|
32
|
+
["balanco", "Balanço"]],
|
|
27
33
|
transicao: [["", "Nenhuma (corte seco)"], ["fade", "Suave"], ["deslizar", "Deslizar para o lado"],
|
|
28
34
|
["subir", "Subir"], ["zoom", "Aproximar"], ["limpar", "Varrer"],
|
|
29
35
|
["persiana", "Persiana"], ["flash", "Flash"]],
|
|
@@ -1221,6 +1227,12 @@
|
|
|
1221
1227
|
g0.appendChild(linha("Duração (s)", inp(c.duracao || 4, function (v) { mudar(function () { c.duracao = v || 4; }); }, "number")));
|
|
1222
1228
|
g0.appendChild(linha("Como entra", menu(OPC.transicao, c.transicao || "",
|
|
1223
1229
|
function (v) { mudar(function () { if (v) c.transicao = v; else delete c.transicao; }); })));
|
|
1230
|
+
/* A câmera só aparece onde há tempo (vídeo): num slide parado ela
|
|
1231
|
+
não teria o que animar, e uma caixa que não faz nada é pior que
|
|
1232
|
+
caixa nenhuma. Guardada como string — é o formato que a spec
|
|
1233
|
+
documenta e o que o render.js aceita direto. */
|
|
1234
|
+
g0.appendChild(linha("Câmera", menu(OPC.camera, typeof c.camera === "string" ? c.camera : "",
|
|
1235
|
+
function (v) { mudar(function () { if (v) c.camera = v; else delete c.camera; }); })));
|
|
1224
1236
|
}
|
|
1225
1237
|
if (SLUG === "traco") {
|
|
1226
1238
|
g0.appendChild(linha("Arranjo", selecao(["", "horizontal", "vertical"], c.auto || "",
|
|
@@ -1292,6 +1304,11 @@
|
|
|
1292
1304
|
gp.appendChild(linha("Opacidade", inp(e.opacidade != null ? e.opacidade : 1, function (v) { edita("opacidade", v); }, "number")));
|
|
1293
1305
|
gp.appendChild(linha("Como entra", menu(OPC.animacao, e.anim || "",
|
|
1294
1306
|
function (v) { edita("anim", v); })));
|
|
1307
|
+
/* Profundidade: o quanto este elemento acompanha a câmera. 1 anda com o
|
|
1308
|
+
quadro, acima vem à frente, abaixo fica ao fundo. Sem câmera na cena
|
|
1309
|
+
não faz nada — e é por isso que mora aqui embaixo, e não em destaque. */
|
|
1310
|
+
gp.appendChild(linha("Profundidade", inp(e.paralaxe != null ? e.paralaxe : 1,
|
|
1311
|
+
function (v) { edita("paralaxe", v === 1 || v === "" ? "" : v); }, "number")));
|
|
1295
1312
|
if (e.tipo === "numeros") {
|
|
1296
1313
|
gp.appendChild(linha("Contar", selecao(["não", "sim"], e.contar ? "sim" : "não",
|
|
1297
1314
|
function (v) { edita("contar", v === "sim" ? true : ""); })));
|