primocode 8.9.0 → 8.10.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/bin/primocode.js +10 -0
- package/lib/referencia.js +29 -5
- package/package.json +2 -2
- package/studio/arquivos/corte-estatico-corte-a5bab.json +150 -0
- package/studio/arquivos/corte-founders-ai-teste-do-player-2de1d.json +65 -0
- package/studio/arquivos/corte-minimo-2a829.json +24 -0
- package/studio/arquivos/corte-ok-00a3b.json +18 -0
- package/studio/arquivos/corte-sweep-corte-cfb29.json +147 -0
- package/studio/arquivos/corte-visual-completo-78f50.json +35 -0
- package/studio/arquivos/grade-sweep-grade-0c2f1.json +119 -0
- package/studio/arquivos/prisma-estatico-prisma-d8740.json +182 -0
- package/studio/arquivos/prisma-sweep-prisma-4c58c.json +166 -0
- package/studio/arquivos/prisma-y-b0785.json +52 -0
- package/studio/arquivos/prosa-sweep-prosa-f29cf.json +80 -0
- package/studio/arquivos/tela-estatico-tela-c7daf.json +90 -0
- package/studio/arquivos/tela-sweep-tela-0d2bf.json +84 -0
- package/studio/arquivos/traco-estatico-traco-25872.json +113 -0
- package/studio/arquivos/traco-sweep-traco-0bcf4.json +102 -0
- package/studio/ferramentas/corte.md +62 -57
- package/studio/server.py +46 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "traco-estatico-traco-25872",
|
|
3
|
+
"ferramenta": "traco",
|
|
4
|
+
"titulo": "estatico-traco",
|
|
5
|
+
"doc": {
|
|
6
|
+
"tema": "meia-noite",
|
|
7
|
+
"largura": 1920,
|
|
8
|
+
"altura": 1080,
|
|
9
|
+
"cenas": [
|
|
10
|
+
{
|
|
11
|
+
"layout": "livre",
|
|
12
|
+
"fundo": "pontos",
|
|
13
|
+
"elementos": [
|
|
14
|
+
{
|
|
15
|
+
"id": "ped",
|
|
16
|
+
"tipo": "no",
|
|
17
|
+
"texto": "Pedido aberto",
|
|
18
|
+
"x": 120,
|
|
19
|
+
"y": 480,
|
|
20
|
+
"l": 320,
|
|
21
|
+
"a": 140,
|
|
22
|
+
"destaque": true,
|
|
23
|
+
"anim": "nenhuma"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "val",
|
|
27
|
+
"tipo": "no",
|
|
28
|
+
"texto": "Validação\nfiscal",
|
|
29
|
+
"x": 620,
|
|
30
|
+
"y": 480,
|
|
31
|
+
"l": 320,
|
|
32
|
+
"a": 140,
|
|
33
|
+
"anim": "nenhuma"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "ger",
|
|
37
|
+
"tipo": "no",
|
|
38
|
+
"texto": "Aprovação\ngerência",
|
|
39
|
+
"x": 1120,
|
|
40
|
+
"y": 300,
|
|
41
|
+
"l": 320,
|
|
42
|
+
"a": 140,
|
|
43
|
+
"anim": "nenhuma"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "dir",
|
|
47
|
+
"tipo": "no",
|
|
48
|
+
"texto": "Aprovação\ndiretoria",
|
|
49
|
+
"x": 1120,
|
|
50
|
+
"y": 660,
|
|
51
|
+
"l": 320,
|
|
52
|
+
"a": 140,
|
|
53
|
+
"anim": "nenhuma"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "ok",
|
|
57
|
+
"tipo": "no",
|
|
58
|
+
"texto": "Compra\nemitida",
|
|
59
|
+
"x": 1560,
|
|
60
|
+
"y": 480,
|
|
61
|
+
"l": 280,
|
|
62
|
+
"a": 140,
|
|
63
|
+
"destaque": true,
|
|
64
|
+
"anim": "nenhuma"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"tipo": "conector",
|
|
68
|
+
"de": "ped",
|
|
69
|
+
"para": "val",
|
|
70
|
+
"anim": "nenhuma"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"tipo": "conector",
|
|
74
|
+
"de": "val",
|
|
75
|
+
"para": "ger",
|
|
76
|
+
"texto": "até R$ 20 mil",
|
|
77
|
+
"anim": "nenhuma"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"tipo": "conector",
|
|
81
|
+
"de": "val",
|
|
82
|
+
"para": "dir",
|
|
83
|
+
"texto": "acima de R$ 20 mil",
|
|
84
|
+
"anim": "nenhuma"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"tipo": "conector",
|
|
88
|
+
"de": "ger",
|
|
89
|
+
"para": "ok",
|
|
90
|
+
"anim": "nenhuma"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"tipo": "conector",
|
|
94
|
+
"de": "dir",
|
|
95
|
+
"para": "ok",
|
|
96
|
+
"anim": "nenhuma"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"tipo": "texto",
|
|
100
|
+
"x": 120,
|
|
101
|
+
"y": 120,
|
|
102
|
+
"l": 900,
|
|
103
|
+
"a": 120,
|
|
104
|
+
"texto": "Compras · fluxo vigente",
|
|
105
|
+
"papel": "chapeu",
|
|
106
|
+
"anim": "nenhuma"
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
"atualizado": "2026-08-16T15:08:28+00:00"
|
|
113
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "traco-sweep-traco-0bcf4",
|
|
3
|
+
"ferramenta": "traco",
|
|
4
|
+
"titulo": "sweep-traco",
|
|
5
|
+
"doc": {
|
|
6
|
+
"tema": "meia-noite",
|
|
7
|
+
"largura": 1920,
|
|
8
|
+
"altura": 1080,
|
|
9
|
+
"cenas": [
|
|
10
|
+
{
|
|
11
|
+
"layout": "livre",
|
|
12
|
+
"fundo": "pontos",
|
|
13
|
+
"elementos": [
|
|
14
|
+
{
|
|
15
|
+
"id": "ped",
|
|
16
|
+
"tipo": "no",
|
|
17
|
+
"texto": "Pedido aberto",
|
|
18
|
+
"x": 120,
|
|
19
|
+
"y": 480,
|
|
20
|
+
"l": 320,
|
|
21
|
+
"a": 140,
|
|
22
|
+
"destaque": true
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "val",
|
|
26
|
+
"tipo": "no",
|
|
27
|
+
"texto": "Validação\nfiscal",
|
|
28
|
+
"x": 620,
|
|
29
|
+
"y": 480,
|
|
30
|
+
"l": 320,
|
|
31
|
+
"a": 140
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "ger",
|
|
35
|
+
"tipo": "no",
|
|
36
|
+
"texto": "Aprovação\ngerência",
|
|
37
|
+
"x": 1120,
|
|
38
|
+
"y": 300,
|
|
39
|
+
"l": 320,
|
|
40
|
+
"a": 140
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "dir",
|
|
44
|
+
"tipo": "no",
|
|
45
|
+
"texto": "Aprovação\ndiretoria",
|
|
46
|
+
"x": 1120,
|
|
47
|
+
"y": 660,
|
|
48
|
+
"l": 320,
|
|
49
|
+
"a": 140
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "ok",
|
|
53
|
+
"tipo": "no",
|
|
54
|
+
"texto": "Compra\nemitida",
|
|
55
|
+
"x": 1560,
|
|
56
|
+
"y": 480,
|
|
57
|
+
"l": 280,
|
|
58
|
+
"a": 140,
|
|
59
|
+
"destaque": true
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"tipo": "conector",
|
|
63
|
+
"de": "ped",
|
|
64
|
+
"para": "val"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"tipo": "conector",
|
|
68
|
+
"de": "val",
|
|
69
|
+
"para": "ger",
|
|
70
|
+
"texto": "até R$ 20 mil"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"tipo": "conector",
|
|
74
|
+
"de": "val",
|
|
75
|
+
"para": "dir",
|
|
76
|
+
"texto": "acima de R$ 20 mil"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"tipo": "conector",
|
|
80
|
+
"de": "ger",
|
|
81
|
+
"para": "ok"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"tipo": "conector",
|
|
85
|
+
"de": "dir",
|
|
86
|
+
"para": "ok"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"tipo": "texto",
|
|
90
|
+
"x": 120,
|
|
91
|
+
"y": 120,
|
|
92
|
+
"l": 900,
|
|
93
|
+
"a": 120,
|
|
94
|
+
"texto": "Compras · fluxo vigente",
|
|
95
|
+
"papel": "chapeu"
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
"atualizado": "2026-08-16T15:07:33+00:00"
|
|
102
|
+
}
|
|
@@ -3,6 +3,68 @@
|
|
|
3
3
|
No lugar de CapCut, Premiere e After Effects. Modelo `cena`, com `duracao` em cada
|
|
4
4
|
cena, trilha sonora, transições e gravação em `.webm`.
|
|
5
5
|
|
|
6
|
+
<!-- O EXEMPLO FICA NO COMEÇO DE PROPÓSITO: quando o plano de tokens é
|
|
7
|
+
apertado, o agente recebe só os primeiros ~3500 caracteres desta spec. Um
|
|
8
|
+
vídeo inteiro saiu PRETO porque o formato do documento estava depois do
|
|
9
|
+
corte e o agente inventou a estrutura. Formato primeiro; detalhe depois. -->
|
|
10
|
+
|
|
11
|
+
## Exemplo completo
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"ferramenta": "corte",
|
|
16
|
+
"titulo": "Reels — por que sua conta de luz subiu",
|
|
17
|
+
"doc": {
|
|
18
|
+
"tema": "brasa",
|
|
19
|
+
"largura": 1080, "altura": 1920,
|
|
20
|
+
"acabamento": "grao",
|
|
21
|
+
"trilha": { "tipo": "gerada", "estilo": "pulso", "volume": 0.7 },
|
|
22
|
+
"cenas": [
|
|
23
|
+
{ "layout": "centro", "duracao": 3, "fundo": "aurora", "transicao": "flash",
|
|
24
|
+
"elementos": [
|
|
25
|
+
{ "tipo": "texto", "papel": "titulo", "texto": "Sua conta subiu\nde novo.",
|
|
26
|
+
"tamanho": 124, "estilo": "gradiente", "anim": "desfoque" }]},
|
|
27
|
+
|
|
28
|
+
{ "layout": "centro", "duracao": 3, "fundo": "vinheta", "transicao": "limpar",
|
|
29
|
+
"elementos": [
|
|
30
|
+
{ "tipo": "texto", "papel": "titulo", "texto": "E não foi o seu chuveiro.",
|
|
31
|
+
"tamanho": 78, "estilo": "marcador", "anim": "palavra" }]},
|
|
32
|
+
|
|
33
|
+
{ "layout": "centro", "duracao": 4, "transicao": "zoom", "acabamento": "vinheta",
|
|
34
|
+
"fundo": { "imagem": "/midia/torre.jpg", "escurecer": 0.5 },
|
|
35
|
+
"elementos": [
|
|
36
|
+
{ "tipo": "numeros", "contar": true, "anim": "saltar", "tamanho": 180,
|
|
37
|
+
"itens": ["+38% | bandeira tarifária em 12 meses"] }]},
|
|
38
|
+
|
|
39
|
+
{ "layout": "livre", "duracao": 4, "transicao": "deslizar",
|
|
40
|
+
"elementos": [
|
|
41
|
+
{ "tipo": "imagem", "src": "/midia/campo.jpg", "x": 0, "y": 0,
|
|
42
|
+
"l": 1080, "a": 1920, "raio": 0, "movimento": "aproximar", "filtro": "contraste" },
|
|
43
|
+
{ "tipo": "forma", "forma": "retangulo", "x": 0, "y": 1180, "l": 1080, "a": 740,
|
|
44
|
+
"preenchimento": "rgba(10,5,6,.72)" },
|
|
45
|
+
{ "tipo": "texto", "x": 90, "y": 1320, "l": 900, "a": 420,
|
|
46
|
+
"texto": "O custo está\nna transmissão", "tamanho": 104, "peso": 750,
|
|
47
|
+
"estilo": "sombra", "anim": "cortina" }]},
|
|
48
|
+
|
|
49
|
+
{ "layout": "centro", "duracao": 5, "fundo": "raios", "transicao": "subir",
|
|
50
|
+
"elementos": [
|
|
51
|
+
{ "tipo": "lista", "revelar": true, "tamanho": 52, "intervalo": 0.9,
|
|
52
|
+
"itens": ["Linhas saturadas no Nordeste",
|
|
53
|
+
"Usina gerando sem escoar",
|
|
54
|
+
"A conta chega em quem consome"] }]},
|
|
55
|
+
|
|
56
|
+
{ "layout": "centro", "duracao": 3, "fundo": "brilho", "transicao": "fade",
|
|
57
|
+
"acabamento": "luz",
|
|
58
|
+
"elementos": [
|
|
59
|
+
{ "tipo": "texto", "papel": "titulo", "texto": "Segue para\nentender a próxima.",
|
|
60
|
+
"tamanho": 96, "estilo": "contorno-tom", "anim": "elastico" }]}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
6
68
|
## Como pensar um vídeo curto
|
|
7
69
|
|
|
8
70
|
Um Reels de 30 s são 7 a 9 cenas de 3 a 5 segundos. A regra é dura:
|
|
@@ -152,63 +214,6 @@ Em `lista`, `"revelar": true` distribui os itens dentro da duração da cena;
|
|
|
152
214
|
|
|
153
215
|
---
|
|
154
216
|
|
|
155
|
-
## Exemplo completo
|
|
156
|
-
|
|
157
|
-
```json
|
|
158
|
-
{
|
|
159
|
-
"ferramenta": "corte",
|
|
160
|
-
"titulo": "Reels — por que sua conta de luz subiu",
|
|
161
|
-
"doc": {
|
|
162
|
-
"tema": "brasa",
|
|
163
|
-
"largura": 1080, "altura": 1920,
|
|
164
|
-
"acabamento": "grao",
|
|
165
|
-
"trilha": { "tipo": "gerada", "estilo": "pulso", "volume": 0.7 },
|
|
166
|
-
"cenas": [
|
|
167
|
-
{ "layout": "centro", "duracao": 3, "fundo": "aurora", "transicao": "flash",
|
|
168
|
-
"elementos": [
|
|
169
|
-
{ "tipo": "texto", "papel": "titulo", "texto": "Sua conta subiu\nde novo.",
|
|
170
|
-
"tamanho": 124, "estilo": "gradiente", "anim": "desfoque" }]},
|
|
171
|
-
|
|
172
|
-
{ "layout": "centro", "duracao": 3, "fundo": "vinheta", "transicao": "limpar",
|
|
173
|
-
"elementos": [
|
|
174
|
-
{ "tipo": "texto", "papel": "titulo", "texto": "E não foi o seu chuveiro.",
|
|
175
|
-
"tamanho": 78, "estilo": "marcador", "anim": "palavra" }]},
|
|
176
|
-
|
|
177
|
-
{ "layout": "centro", "duracao": 4, "transicao": "zoom", "acabamento": "vinheta",
|
|
178
|
-
"fundo": { "imagem": "/midia/torre.jpg", "escurecer": 0.5 },
|
|
179
|
-
"elementos": [
|
|
180
|
-
{ "tipo": "numeros", "contar": true, "anim": "saltar", "tamanho": 180,
|
|
181
|
-
"itens": ["+38% | bandeira tarifária em 12 meses"] }]},
|
|
182
|
-
|
|
183
|
-
{ "layout": "livre", "duracao": 4, "transicao": "deslizar",
|
|
184
|
-
"elementos": [
|
|
185
|
-
{ "tipo": "imagem", "src": "/midia/campo.jpg", "x": 0, "y": 0,
|
|
186
|
-
"l": 1080, "a": 1920, "raio": 0, "movimento": "aproximar", "filtro": "contraste" },
|
|
187
|
-
{ "tipo": "forma", "forma": "retangulo", "x": 0, "y": 1180, "l": 1080, "a": 740,
|
|
188
|
-
"preenchimento": "rgba(10,5,6,.72)" },
|
|
189
|
-
{ "tipo": "texto", "x": 90, "y": 1320, "l": 900, "a": 420,
|
|
190
|
-
"texto": "O custo está\nna transmissão", "tamanho": 104, "peso": 750,
|
|
191
|
-
"estilo": "sombra", "anim": "cortina" }]},
|
|
192
|
-
|
|
193
|
-
{ "layout": "centro", "duracao": 5, "fundo": "raios", "transicao": "subir",
|
|
194
|
-
"elementos": [
|
|
195
|
-
{ "tipo": "lista", "revelar": true, "tamanho": 52, "intervalo": 0.9,
|
|
196
|
-
"itens": ["Linhas saturadas no Nordeste",
|
|
197
|
-
"Usina gerando sem escoar",
|
|
198
|
-
"A conta chega em quem consome"] }]},
|
|
199
|
-
|
|
200
|
-
{ "layout": "centro", "duracao": 3, "fundo": "brilho", "transicao": "fade",
|
|
201
|
-
"acabamento": "luz",
|
|
202
|
-
"elementos": [
|
|
203
|
-
{ "tipo": "texto", "papel": "titulo", "texto": "Segue para\nentender a próxima.",
|
|
204
|
-
"tamanho": 96, "estilo": "contorno-tom", "anim": "elastico" }]}
|
|
205
|
-
]
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
---
|
|
211
|
-
|
|
212
217
|
## No modo Abrir
|
|
213
218
|
|
|
214
219
|
Toca sozinho ao carregar, com barra de transporte embaixo:
|
package/studio/server.py
CHANGED
|
@@ -244,6 +244,17 @@ def criar(dados):
|
|
|
244
244
|
if doc is not None and not isinstance(doc, dict):
|
|
245
245
|
raise ValueError("doc precisa ser um objeto")
|
|
246
246
|
|
|
247
|
+
# ── O doc torto não pode virar arquivo ───────────────────────────────
|
|
248
|
+
# Quem cria é um agente de IA, e quando ele não viu a spec ele INVENTA a
|
|
249
|
+
# estrutura. Antes o arquivo era gravado assim mesmo: o player não achava
|
|
250
|
+
# cena nenhuma e o "vídeo" saía uma tela preta — o usuário viu exatamente
|
|
251
|
+
# isso. Recusar com o formato certo na mensagem transforma o erro mudo em
|
|
252
|
+
# correção na rodada seguinte.
|
|
253
|
+
if doc:
|
|
254
|
+
problema = validar_doc(POR_SLUG[ferramenta]["modelo"], doc)
|
|
255
|
+
if problema:
|
|
256
|
+
raise ValueError(problema)
|
|
257
|
+
|
|
247
258
|
arq_id = ferramenta + "-" + slugify(titulo) + "-" + uuid.uuid4().hex[:5]
|
|
248
259
|
return gravar({
|
|
249
260
|
"id": arq_id,
|
|
@@ -253,6 +264,41 @@ def criar(dados):
|
|
|
253
264
|
})
|
|
254
265
|
|
|
255
266
|
|
|
267
|
+
def validar_doc(modelo, doc):
|
|
268
|
+
"""None quando o doc serve; senão, a frase que ENSINA o formato certo.
|
|
269
|
+
|
|
270
|
+
A mensagem repete a estrutura mínima de propósito: ela volta para o agente
|
|
271
|
+
como resultado da tool, e é a única chance de ele se corrigir sem o
|
|
272
|
+
usuário ver um vídeo preto."""
|
|
273
|
+
if modelo == "cena":
|
|
274
|
+
cenas = doc.get("cenas")
|
|
275
|
+
if not isinstance(cenas, list) or not cenas:
|
|
276
|
+
return ('doc sem "cenas". O formato é: {"tema":"...", "cenas":[{"layout":"centro",'
|
|
277
|
+
'"duracao":3,"elementos":[{"tipo":"texto","texto":"..."}]}]}. '
|
|
278
|
+
'Leia studio_spec da ferramenta e refaça.')
|
|
279
|
+
for i, c in enumerate(cenas):
|
|
280
|
+
if not isinstance(c, dict):
|
|
281
|
+
return "cena %d não é um objeto" % (i + 1)
|
|
282
|
+
els = c.get("elementos")
|
|
283
|
+
if not isinstance(els, list) or not els:
|
|
284
|
+
return ('cena %d sem "elementos". Cada cena precisa de elementos:[...] — '
|
|
285
|
+
'ex.: {"tipo":"texto","texto":"..."}. Leia studio_spec e refaça.' % (i + 1))
|
|
286
|
+
for j, e in enumerate(els):
|
|
287
|
+
if not isinstance(e, dict) or not e.get("tipo"):
|
|
288
|
+
return 'elemento %d da cena %d sem "tipo" (texto, imagem, lista, numeros, forma...)' % (j + 1, i + 1)
|
|
289
|
+
elif modelo == "documento":
|
|
290
|
+
blocos = doc.get("blocos")
|
|
291
|
+
if not isinstance(blocos, list) or not blocos:
|
|
292
|
+
return ('doc sem "blocos". O formato é: {"tema":"papel","blocos":[{"tipo":"titulo",'
|
|
293
|
+
'"texto":"..."},{"tipo":"paragrafo","texto":"..."}]}. Leia studio_spec e refaça.')
|
|
294
|
+
elif modelo == "planilha":
|
|
295
|
+
abas = doc.get("abas")
|
|
296
|
+
if not isinstance(abas, list) or not abas:
|
|
297
|
+
return ('doc sem "abas". O formato é: {"abas":[{"nome":"...","colunas":[...],'
|
|
298
|
+
'"linhas":[[...]]}]}. Leia studio_spec e refaça.')
|
|
299
|
+
return None
|
|
300
|
+
|
|
301
|
+
|
|
256
302
|
def exemplo(ferramenta):
|
|
257
303
|
"""Documento inicial de cada ferramenta — o mínimo que já fica bonito."""
|
|
258
304
|
if ferramenta == "prosa":
|