# Catálogos de seletores

> Crie seletores do Nodaro em seu app com @nodaro/prompts, converta seleções em trechos de prompt, traduza rótulos, mostre imagens ou leia catálogos via API.

Source: https://nodaro.ai/pt-BR/docs/developers/picker-catalogs

Um **catálogo de seletor** é o conjunto de dados por trás de um dos seletores do Nodaro, os nós dos Controles criativos, como [**Clima** (Mood)](https://nodaro.ai/docs/nodes/creative-controls/mood), [**Lente** (Lens)](https://nodaro.ai/docs/nodes/creative-controls/lens), [**Enquadramento** (Framing)](https://nodaro.ai/docs/nodes/creative-controls/framing) e [**Pessoa** (Person)](https://nodaro.ai/docs/nodes/creative-controls/person). Ele contém as opções do seletor, o texto de prompt que cada opção adiciona, as categorias e as chaves das traduções. Os catálogos vêm como dados simples em pacotes npm, então você pode criar os mesmos seletores no seu próprio app, com o seu próprio estilo, e montar exatamente os prompts que o Nodaro monta.

Um seletor nunca chama um modelo. Ele adiciona um trecho descritivo ao prompt do nó que ele alimenta; veja [Controles criativos](https://nodaro.ai/docs/guides/creative-controls).

## Importar o pacote ou chamar a API
| O seu app | Use |
| --- | --- |
| Consegue incluir um pacote npm no bundle | Importe os catálogos de `@nodaro/prompts`. Eles são tipados, funcionam offline e não precisam de chamada à API. |
| Não consegue incluir um pacote, como um agente de IA via MCP | Leia os mesmos dados pela interface de descoberta: o endpoint REST `GET /v1/picker-catalogs`, `client.pickerCatalogs` no [SDK](https://nodaro.ai/docs/developers/sdk), `nodaro pickers` na [CLI](https://nodaro.ai/docs/developers/cli/commands#pickers) ou a ferramenta MCP `get_picker_catalog`. |

Prefira o pacote quando puder: ele evita uma ida e volta à rede por dados que só mudam a cada versão.

```bash
npm install @nodaro/prompts @nodaro/shared @nodaro/sdk
```

`@nodaro/prompts` contém os catálogos e as funções de prompt, `@nodaro/shared` contém as traduções, e `@nodaro/sdk` executa a geração.

## O registro
```ts

```

| Exportação | O que retorna |
| --- | --- |
| `PICKER_CATALOGS` | Todos os catálogos de seletores: 28 seletores de uma dimensão e 11 de várias dimensões. |
| `getPickerCatalog(nodeTypeOrCatalogId)` | Um catálogo, pelo tipo de nó (como `"mood"`) ou pelo id do catálogo. |
| `listPickerCatalogs()` | Todos eles. |

Os tipos do catálogo:

```ts
interface PickerOption {
id: string
label: string            // English display text; translate it with the catalogId (see below)
description?: string
category?: string        // group id, matching categoryOrder and categoryLabels
promptHint: string       // the clause this option adds ("" for a no-op option such as "auto")
term: string             // the short professional term Compact mode adds ("" for a no-op option)
icon?: string            // reserved; pictures are served separately (see "Pictures")
}

interface PickerDimension {  // multi-dimension pickers, and the secondary fields of a single-dimension picker
field: string              // for example "shotSize"
label: string
options: readonly PickerOption[]
}

interface PickerCatalog {
nodeType: string           // "mood", "framing", ...
label: string
catalogId: string          // the key of the translations
kind: "single" | "multi"
valueField?: string        // single: the field a selection writes
defaultValue?: string
categoryOrder?: readonly string[]
categoryLabels?: Readonly<Record<string, string>>
options?: readonly PickerOption[]        // single-dimension pickers
fields?: readonly string[]               // multi-dimension pickers: the dimension fields
dimensions?: readonly PickerDimension[]  // multi-dimension pickers, and secondary fields
}
```

Mostre o `label` e envie `term` ou `promptHint` ao modelo. Nunca derive um do outro.

## Criar um seletor de uma dimensão
Um seletor de uma dimensão, como o Clima, é uma escolha em uma lista simples, opcionalmente agrupada. `options` traz tudo o que você precisa para desenhar a grade.

```tsx

const client = createClient({
baseUrl: "https://app.nodaro.ai",
auth: new StaticTokenAuth(process.env.NODARO_ACCESS_TOKEN!),
})
const mood = getPickerCatalog("mood")! // { nodeType: "mood", valueField: "mood", options, categoryOrder, categoryLabels }

// 1. Render your own tile grid, grouped by category
function MoodPicker({ value, onChange }: { value?: string; onChange: (id: string) => void }) {
return (mood.categoryOrder ?? [undefined]).map((cat) => (
<section key={cat ?? "all"}>
{cat && <h4>{mood.categoryLabels?.[cat]}</h4>}
{mood.options!
.filter((o) => !cat || o.category === cat)
.map((o) => (
<button key={o.id} aria-pressed={value === o.id} title={o.description} onClick={() => onChange(o.id)}>
{o.label}
</button>
))}
</section>
))
}

// 2. Selection, then prompt clause, then generation
const selected = "serene"
const clause = getParameterPromptHint({ type: "mood", data: { mood: selected } })
// or: mood.options!.find((o) => o.id === selected)!.promptHint

const prompt = ["a portrait of a woman", clause].filter(Boolean).join(", ")
const result = await client.nodes.runAndWait("generate-image", { prompt })
console.log(result.imageUrl)
```

### Parâmetros secundários de Transição, Efeitos de personagem e Movimento de personagem
Três seletores de uma dimensão têm campos extras além da escolha principal. [**Transição** (Transition)](https://nodaro.ai/docs/nodes/creative-controls/transition) e [**Efeitos de personagem** (Character FX)](https://nodaro.ai/docs/nodes/creative-controls/character-fx) têm, cada um, `position`, `duration` e `intensity`. [**Movimento de personagem** (Character Motion)](https://nodaro.ai/docs/nodes/creative-controls/character-motion) tem `position` e `pace`.

Esses campos também são catálogos, com as mesmas linhas de qualquer outra opção, e cada lista começa com um `auto` sem efeito. O catálogo os expõe como `dimensions`, **além de** `options`. Desenhe o seletor principal a partir de `options` e os menus suspensos a partir de `dimensions`; um cliente que envia só ids nunca escreve ele mesmo o trecho de timing.

```ts
const fx = getPickerCatalog("character-fx")!
fx.options     // the effects of the main picker
fx.dimensions  // [{ field: "position", ... }, { field: "duration", ... }, { field: "intensity", ... }]

// Each dimension's rows are also exported directly:
//   TRANSITION_POSITIONS / TRANSITION_DURATIONS / TRANSITION_INTENSITIES
//   CHARACTER_FX_POSITIONS / CHARACTER_FX_DURATIONS / CHARACTER_FX_INTENSITIES
//   CHARACTER_MOTION_POSITIONS / CHARACTER_MOTION_PACES
```

Transição e Efeitos de personagem compartilham os mesmos ids: `start`, `middle`, `end` e `full`; `instant`, `short`, `medium` e `long`; `subtle`, `natural`, `dynamic` e `crazy`. Eles **não** compartilham o texto: uma transição acontece e se estende pelo clipe, enquanto um efeito aparece e persiste. Sempre leia as linhas do catálogo do próprio nó. Movimento de personagem compartilha os ids de posição e adiciona os próprios ids de ritmo, `slow-motion`, `slow`, `natural`, `fast` e `explosive`, com o próprio texto.

## Criar um seletor de várias dimensões
Um seletor de várias dimensões define vários campos independentes de uma vez. O Enquadramento, por exemplo, define o tamanho do plano, o ângulo, a cobertura, a composição e o ponto de vista. O catálogo dele tem uma entrada `{ field, label, options }` por campo em `dimensions`.

```tsx

const framing = getPickerCatalog("framing")!
// framing.dimensions = [
//   { field: "shotSize",    label: "Shot Size",   options: [{ id: "close-up", ... }, ...] },
//   { field: "angle",       label: "Angle",       options: [{ id: "low-angle", ... }, ...] },
//   { field: "coverage",    label: "Coverage",    options: [...] },
//   { field: "composition", label: "Composition", options: [...] },
//   { field: "vantage",     label: "Vantage",     options: [...] },
// ]

function FramingPicker({ value, onChange }: {
value: Record<string, string>
onChange: (v: Record<string, string>) => void
}) {
return framing.dimensions!.map((dim) => (
<section key={dim.field}>
<h4>{dim.label}</h4>
{dim.options.map((o) => (
<button
key={o.id}
aria-pressed={value[dim.field] === o.id}
title={o.description}
onClick={() => onChange({ ...value, [dim.field]: o.id })}
>
{o.label}
</button>
))}
</section>
))
}

// Selection to clause: getParameterPromptHint composes every field that is set
const value = { shotSize: "close-up", angle: "low-angle", composition: "rule-of-thirds" }
const clause = getParameterPromptHint({ type: "framing", data: value })
```

## Transformar seleções em prompt
`getParameterPromptHint({ type, data })` transforma qualquer seleção, de uma ou de várias dimensões, no trecho correspondente. É a mesma função que o Nodaro executa nos servidores dele, então os seus prompts correspondem aos do editor. Também existem construtores por catálogo, como `buildFramingHints(value)` para um seletor de várias dimensões e `getMoodPromptHint(id)` para uma opção.

```ts
// Several pickers, one prompt, one generation
const clauses = [
getParameterPromptHint({ type: "mood",    data: { mood: "serene" } }),
getParameterPromptHint({ type: "lens",    data: { lens: "portrait-85mm" } }),
getParameterPromptHint({ type: "framing", data: { shotSize: "close-up", angle: "eye-level" } }),
]
const prompt = ["a portrait of a woman in a garden", ...clauses].filter(Boolean).join(", ")
const result = await client.nodes.runAndWait("generate-image", { prompt, provider: "nano-banana-2" })
```

Adicione `hintMode: "compact"` ao `data` de um seletor para obter o `term` curto dele em vez do trecho completo. Esse é o modo **Compacto** da opção **Trecho do prompt** do editor; veja [Completo ou Compacto](https://nodaro.ai/docs/nodes/creative-controls/mood#full-or-compact).

| Função | O que faz |
| --- | --- |
| `getParameterPromptHint({ type, data })` | Transforma qualquer seleção no trecho composto correspondente. |
| `build<Name>Hints(value)` | Monta o trecho de um catálogo, como `buildFramingHints`. |
| `get<Name>PromptHint(id)` | Devolve o trecho de uma opção, como `getMoodPromptHint`. |
| `PICKER_CATALOGS`, `getPickerCatalog`, `listPickerCatalogs` | O registro. |

## Traduzir os rótulos
Os rótulos dos catálogos estão em inglês. As traduções para 12 idiomas vêm em `@nodaro/shared`, indexadas pelo `catalogId` de cada catálogo: `en`, `es`, `fr`, `de`, `pt-BR`, `ru`, `hi`, `ja`, `ko`, `zh-CN`, `he` e `ar`. Os valores de `promptHint` e `term` ficam em inglês, porque são os modelos que os leem.

O inglês não precisa de configuração. Para os outros idiomas, registre os bundles de tradução uma vez na inicialização e depois resolva os rótulos de forma síncrona, com o inglês como fallback:

```ts

// 1. Once at startup, in a Vite app: wire the lazily loaded locale bundles
registerSidecarLoaders(import.meta.glob("/node_modules/@nodaro/shared/src/i18n/*.*.ts"))

// 2. Before rendering a locale, load that catalog's bundle
await ensureLocaleCatalogLoaded(mood.catalogId, "fr")

// 3. Resolve a label, synchronously; English when a translation is missing or not loaded yet
const label = resolveLabel(mood.catalogId, option.id, option.label, "fr")
```

Os bundles são carregados sob demanda, então `registerSidecarLoaders` recebe um mapa de carregadores, como o que o `import.meta.glob` do Vite devolve. Backends e testes podem pular a tradução e usar o `label` em inglês.

## Mostrar imagens
O seu app pode mostrar as mesmas imagens que o editor mostra nestes seletores:

- **Fotos** em [Pessoa](https://nodaro.ai/docs/nodes/creative-controls/person), [**Figurino e beleza** (Styling)](https://nodaro.ai/docs/nodes/creative-controls/styling), [**Objeto na mão** (Held Prop)](https://nodaro.ai/docs/nodes/creative-controls/held-prop), [**Material**](https://nodaro.ai/docs/nodes/creative-controls/material) e [**Animal**](https://nodaro.ai/docs/nodes/creative-controls/animal).
- **Arte** nos seletores de música e de voz: **Gênero musical** (Music Genre), **Clima musical** (Music Mood), **Instrumentação** (Instrumentation), **Perfil da voz** (Voice Character) e **Estilo de fala** (Voice Delivery).
- **Imagens de prévia** nos seletores de look, só no Nodaro Cloud. São eles: **Estilo** (Style), **Cor / look** (Color / Look), **Época / período** (Era / Period), Lente, Clima, **Atmosfera** (Atmosphere), **Efeito de composição** (Composition Effect), **Câmera / película** (Camera / Film), Enquadramento, **Iluminação** (Lighting) e **Movimento de câmera** (Camera Motion).

**Pela API,** toda opção com imagem traz um `imageUrl` absoluto na instalação consultada, e uma opção sem imagem não traz nenhum. Pessoa e Figurino e beleza também retornam `sections`: os tópicos deles, em ordem, cada um com uma imagem redonda. As regras das URLs estão em [Ler os catálogos pela API](#read-the-catalogs-over-the-api).

**Na biblioteca,** `@nodaro/prompts` monta as mesmas URLs a partir dos mesmos dados:

- `pickerOptionImageUrl(catalog, field, id, { baseUrl })` para uma opção;
- `pickerSectionImageUrl(topicLabel, { baseUrl })` para um tópico de Pessoa ou de Figurino e beleza.

`baseUrl` é a instalação do Nodaro que serve os arquivos: as imagens pertencem à instalação, não ao pacote npm. Passe `lookPreviews: true` só com o Nodaro Cloud. Para todos os outros seletores, desenhe os seus próprios visuais a partir de `label`, `description` e `category`.

## Ler os catálogos pela API
Os dois endpoints são públicos, não precisam de token e ficam em cache por 5 minutos.

| Método | Caminho | O que retorna |
| --- | --- | --- |
| `GET` | `/v1/picker-catalogs` | Um diretório de todos os seletores: `nodeType`, `label`, `catalogId`, `kind`, `valueField` ou `fields`, `optionCount` e `imageCount`, o número de opções com imagem. |
| `GET` | `/v1/picker-catalogs/:nodeType` | O catálogo de um seletor. Um tipo desconhecido responde `404 not_found`. |

`GET /v1/picker-catalogs/:nodeType` aceita três parâmetros de query. Um valor inválido responde `400 validation_error`.

| Parâmetro | Valores | O que faz |
| --- | --- | --- |
| `detail` | `compact` (padrão) ou `full` | `compact` retorna `id`, `label`, `category`, `term`, `icon` e `imageUrl`. `full` adiciona a `description` e o `promptHint` de cada opção. |
| `category` | Um id de categoria | Filtra um seletor de uma dimensão por uma categoria. |
| `field` | Um nome de campo | Retorna uma dimensão de um seletor de várias dimensões, ou um campo secundário de Transição, Efeitos de personagem ou Movimento de personagem. |

```bash
curl -s https://app.nodaro.ai/v1/picker-catalogs/mood | jq '.data.options[0]'
```

```json
{ "id": "happy", "label": "Happy", "category": "positive", "term": "happy expression",
"imageUrl": "https://cdn.nodaro.ai/cdn-cgi/image/width=480,format=auto,quality=80/images/710df65a-3c1e-485b-b8b7-29f7b3baf479.png" }
```

### As URLs das imagens
| Seletores | Imagem |
| --- | --- |
| `person`, `styling`, `held-prop`, `material`, `animal` | Uma foto, WebP, com até 480 px de largura. |
| `music-genre`, `music-mood`, `instrumentation`, `voice-character`, `voice-delivery` | Uma imagem de emoji 3D (WebP, 128 px) ou uma bandeira (WebP, 120 px de largura). |
| Seletores de look, como `style`, `color-look`, `lens`, `framing`, `lighting`, `mood`, `camera-format` e `camera-motion` | No Nodaro Cloud, uma imagem fixa de 480 px da prévia renderizada, vinda da CDN do Nodaro; para `camera-motion`, um quadro do clipe. Uma instalação self-hosted não retorna nenhuma. |

- **Host.** Uma instalação serve as próprias imagens em `/picker-art/`, então o `imageUrl` usa o endereço público dela: `https://app.nodaro.ai` no Nodaro Cloud, ou o `PUBLIC_URL` de uma instalação self-hosted. Use cada URL como ela vem; nunca monte uma a partir do id de uma opção.
- **Cache.** Os nomes dos arquivos trazem um hash do conteúdo, então uma imagem alterada ganha uma URL nova. Os arquivos são servidos com `Cache-Control: public, max-age=31536000, immutable` e `Access-Control-Allow-Origin: *`, então qualquer origem pode carregá-los em um `<img>`, com `fetch` ou em um canvas.
- **Tópicos.** `person` e `styling` também retornam `sections`, cada uma com um `label`, os `fields` que ela agrupa e um `imageUrl` redondo opcional.

```bash
curl -s https://app.nodaro.ai/v1/picker-catalogs/person | jq '.data.sections[0], .data.dimensions[0].options[0]'
```

```json
{ "label": "Identity", "fields": ["type", "age", "ethnicity", "regionalAesthetic"],
"imageUrl": "https://app.nodaro.ai/picker-art/character/sections/identity.2d5ec1a4.webp" }
{ "id": "man", "label": "Man", "term": "man",
"imageUrl": "https://app.nodaro.ai/picker-art/character/person/man.d6bed999.webp" }
```

### Os catálogos curados de uma implantação
`GET /v1/catalogs` retorna todos os catálogos em uma única chamada, em um formato plano único. Uma implantação pode fazer a curadoria dos catálogos com pacotes vendorizados que substituem, estendem ou bloqueiam opções, e esse endpoint reflete essa curadoria. Quando a implantação registrou pacotes, a resposta traz `curated: true` e os catálogos em `data`. Sem nenhum pacote, ela traz `curated: false`, e você lê os catálogos incluídos, seletor por seletor, em `/v1/picker-catalogs/:nodeType`. O método do SDK é `client.catalogs.list()`.

### Preencher seletores a partir de uma descrição
`POST /v1/text-to-picker` escolhe valores de seletores a partir de uma descrição em texto livre de uma cena ou de uma tomada. Esse é o recurso AI Fill. Ele exige um token e é cobrado como uma chamada de LLM. Ele retorna `pickerJson`, organizado por tipo de seletor, depois por dimensão e depois pelos ids escolhidos, e `gaps`, os detalhes descritos que nenhuma opção de catálogo representa. O método do SDK é `client.pickerCatalogs.analyzeText()`, e o comando da CLI é `nodaro pickers analyze`.

## Metadados de Movimento de personagem
As opções do catálogo de Movimento de personagem podem trazer metadados `motion`, nos dois níveis de detalhe. Os metadados descrevem o movimento:

- Os requisitos, as poses inicial e final, e a visibilidade e as mãos depois do movimento.
- Se ele precisa das mãos livres, o tipo, um ritmo fixo e a contraparte.
- Apelidos de busca, e se o id foi descontinuado e qual id o substitui.

Um campo ausente significa desconhecido.

Mantenha os ids descontinuados funcionando ao carregar workflows salvos, e oculte-os das novas escolhas. O tipo é `CharacterMotionMetadata`, exportado por `@nodaro/prompts`.

## Frequently asked questions

### Preciso da API do Nodaro para usar os catálogos de seletores?

Não. Os catálogos, e a função que transforma uma seleção em um trecho de prompt, vêm no pacote npm @nodaro/prompts, então o seu app pode mostrar os seletores e montar prompts offline. Os endpoints da API existem para clientes que não conseguem incluir o pacote.

### Como transformo a seleção de um seletor em texto de prompt?

Chame getParameterPromptHint com o tipo de nó do seletor e os dados dele, como o id do clima escolhido. O Nodaro executa a mesma função nos servidores dele, então o seu prompt corresponde ao que o editor monta.

### Quais idiomas os rótulos dos seletores aceitam?

12 idiomas: inglês, espanhol, francês, alemão, português do Brasil, russo, hindi, japonês, coreano, chinês simplificado, hebraico e árabe. O texto do prompt e os termos ficam em inglês, porque são os modelos que os leem.

### Por que os seletores de look não têm imagens na minha instalação self-hosted?

As prévias dos seletores de look, como Clima e Iluminação, vêm da CDN do Nodaro, e só o Nodaro Cloud as retorna. Toda instalação serve as fotos de Pessoa, Figurino e beleza, Objeto na mão, Material e Animal, e a arte de música e de voz.

### Quantos catálogos de seletores existem?

39. São 28 seletores de uma dimensão, como Clima e Lente, e 11 seletores de várias dimensões, como Enquadramento, Iluminação e Pessoa.
