NodaroAccount
Nodaro Docs
DocumentationNode ReferenceModelsTemplatesAI Agents (MCP)DevelopersSelf-hostingResearchCompare

Picker catalogs

Build Nodaro's pickers in your own app with @nodaro/prompts, turn selections into prompt clauses, translate labels, show pictures or read catalogs over the API.

A picker catalog is the data behind one of Nodaro's pickers, the Creative Controls nodes such as Mood, Lens, Framing and Person. It holds the picker's options, the prompt text each option adds, its categories and the keys of its translations. The catalogs ship as plain data in npm packages, so you can build the same pickers in your own app, in your own style, and assemble exactly the prompts Nodaro assembles.

A picker never calls a model. It adds a descriptive clause to the prompt of the node it feeds; see Creative controls.

Import the package or call the API

Your appUse
Can bundle an npm packageImport the catalogs from @nodaro/prompts. They are typed, work offline and need no API call.
Cannot bundle a package, such as an AI agent over MCPRead the same data from the discovery surface: the REST endpoint GET /v1/picker-catalogs, client.pickerCatalogs in the SDK, nodaro pickers in the CLI, or the MCP tool get_picker_catalog.

Prefer the package when you can: it saves a round trip for data that changes only with a release.

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

@nodaro/prompts holds the catalogs and the prompt functions, @nodaro/shared holds the translations, and @nodaro/sdk runs the generation.

The registry

import { PICKER_CATALOGS, getPickerCatalog, listPickerCatalogs } from "@nodaro/prompts/picker-catalogs"
ExportWhat it returns
PICKER_CATALOGSEvery picker catalog: 28 single-dimension and 11 multi-dimension pickers.
getPickerCatalog(nodeTypeOrCatalogId)One catalog, by node type (such as "mood") or by catalog id.
listPickerCatalogs()All of them.

The catalog types:

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)
  videoPromptHint?: string // Style only: the clause a video prompt adds instead of promptHint (in the package only; the API does not return it)
  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
}

Render label and send term or promptHint to the model. Never derive one from another.

Build a single-dimension picker

A single-dimension picker, such as Mood, is one choice from a flat list, optionally grouped. options carries everything you need to draw the grid.

import { getPickerCatalog, getParameterPromptHint } from "@nodaro/prompts/picker-catalogs"
import { createClient, StaticTokenAuth } from "@nodaro/sdk"

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({ id: "mood-1", 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)

Secondary parameters of Transition, Character FX and Character Motion

Three single-dimension pickers carry extra fields beside the main choice. Transition and Character FX each have position, duration and intensity. Character Motion has position and pace.

Those fields are catalogs too, with the same rows as every other option, each list led by a no-op auto. The catalog exposes them as dimensions, in addition to options. Render the main picker from options and the dropdowns from dimensions; a client that sends only ids never writes the timing clause itself.

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

Transition and Character FX share the same ids: start, middle, end and full; instant, short, medium and long; subtle, natural, dynamic and crazy. They do not share the wording: a transition occurs and spans the clip, while an effect appears and persists. Always read the rows from the node's own catalog. Character Motion shares the position ids and adds its own pace ids, slow-motion, slow, natural, fast and explosive, with its own wording.

The cut flags of Transition. In the @nodaro/prompts catalog (getPickerCatalog("transition")), a transition that is a cut carries instant: true. On a cut, duration and intensity add no clause, and the position full adds nothing. The two cuts that can blend, seamless-match and jump-match, also carry blendable: true. The short row of the duration dimension carries blendsCut: true: it is the step that turns a blendable cut into a blended cut. So offer a cut its position without full, and no intensity. Offer a blendable cut its duration as a blend: auto, which keeps the hard cut, plus the rows with blendsCut. For several picks, isInstantTransition and isBlendableTransition, both exported by @nodaro/prompts, are true only when every pick is. The flags exist only in the package's catalog. GET /v1/picker-catalogs/transition, client.pickerCatalogs.get("transition") and the MCP get_picker_catalog tool do not carry them.

Build a multi-dimension picker

A multi-dimension picker sets several independent fields at once. Framing, for example, sets the shot size, the angle, the coverage, the composition and the vantage. Its catalog has one { field, label, options } entry per field in dimensions.

import { getPickerCatalog, getParameterPromptHint } from "@nodaro/prompts/picker-catalogs"

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({ id: "framing-1", type: "framing", data: value })

Turn selections into a prompt

getParameterPromptHint({ id, type, data }) turns any selection, single or multi, into its clause. It is the same function Nodaro runs on its servers, so your prompts match the editor's. Per-catalog builders exist too, such as buildFramingHints(value) for a multi-dimension picker and getMoodPromptHint(id) for one option.

// Several pickers, one prompt, one generation
const clauses = [
  getParameterPromptHint({ id: "mood-1",    type: "mood",    data: { mood: "serene" } }),
  getParameterPromptHint({ id: "lens-1",    type: "lens",    data: { lens: "portrait-85mm" } }),
  getParameterPromptHint({ id: "framing-1", 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" })

Video prompts and the Style wording

Every style has two wordings. Still images read its promptHint. Video prompts read a video wording, videoPromptHint, written for moving shots: it names the medium first, then its techniques. Nodaro's video nodes and video routes add the video wording. To match them, pass the surface as the third argument when the clause goes into a video prompt. The Style picker then returns its video wording. Every other picker returns the same clause for both surfaces.

import { getParameterPromptHint } from "@nodaro/prompts/picker-catalogs"
import { getStyleVideoPromptHint } from "@nodaro/prompts/pickers"

const style = { id: "style-1", type: "style", data: { style: "anime" } }
getParameterPromptHint(style)                                   // still image: the option's promptHint
getParameterPromptHint(style, undefined, { surface: "video" })  // video: the style's video wording
getStyleVideoPromptHint("anime")                                // the same video wording, by id

Without a third argument, or with surface: "image", the function returns the image wording. See Styles for images and for video.

Add hintMode: "compact" to a picker's data to get its short term instead of the full clause. This is the Compact mode of the editor's Prompt hint toggle; see Full or Compact.

FunctionWhat it does
getParameterPromptHint({ id, type, data })Turns any selection into its composed clause. Pass { surface: "video" } as the third argument for a video prompt.
getStyleVideoPromptHint(id)Returns the video wording of one style. A style with no video wording returns its promptHint, and an unknown id returns "".
build<Name>Hints(value)Builds the clause of one catalog, such as buildFramingHints.
get<Name>PromptHint(id)Returns the clause of one option, such as getMoodPromptHint.
PICKER_CATALOGS, getPickerCatalog, listPickerCatalogsThe registry.

Translate the labels

Catalog labels are English. Translations for 12 locales ship in @nodaro/shared, keyed by each catalog's catalogId: en, es, fr, de, pt-BR, ru, hi, ja, ko, zh-CN, he and ar. The promptHint and term values stay English, because the models read them.

English needs no setup. For the other locales, register the translation bundles once at startup, then resolve labels synchronously, with English as the fallback:

import { registerSidecarLoaders, ensureLocaleCatalogLoaded, resolveLabel } from "@nodaro/shared"

// 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")

The bundles load on demand, so registerSidecarLoaders takes a map of loaders, such as the one Vite's import.meta.glob returns. Backends and tests can skip translation and use the English label.

Show pictures

Your app can show the same pictures the editor shows on these pickers:

  • Photos on Person, Styling, Held Prop, Material and Animal.
  • Art on the music and voice pickers: Music Genre, Music Mood, Instrumentation, Voice Character and Voice Delivery. On Nodaro Cloud, all five show a photographic render of each option instead of their 3D emoji and flags. The language and accent renders carry their flags.
  • Preview stills on the look pickers, on Nodaro Cloud only. These are Style, Color / Look, Era / Period, Lens, Mood, Atmosphere, Composition Effect, Camera / Film, Framing, Lighting and Camera Motion.

Over the API, every option with a picture carries an absolute imageUrl on the install you asked, and an option without one has none. On the REST routes, and so in the SDK, a render from the Nodaro CDN also carries imageSrcSet, from 240 to 1,920 px wide, so a large tile stays sharp. The MCP tool leaves it out. Person and Styling also return sections: their topics in order, each with a round picture. The URL rules follow in Read the catalogs over the API.

A deployment can turn the pictures off with catalogs.images in its surface profile. Its replies then carry no imageUrl or imageSrcSet, every imageCount is 0, and its editor shows none of these pictures. See Editions and surface profiles.

A deployment can also show pictures of its own with catalogs.images: "own". Its replies and its editor then show the pictures it registered, on its own media host. On the REST routes, an option it gave an enlarged picture also carries imageLargeUrl.

In the library, @nodaro/prompts builds the same URLs from the same data:

  • pickerOptionImageUrl(catalog, field, id, { baseUrl }) for an option, or pickerOptionImage(...) for { url, srcSet? }. Pass srcSet: true for the widths;
  • pickerSectionImageUrl(topicLabel, { baseUrl }) for a Person or Styling topic.

baseUrl is the Nodaro install that serves the files: the pictures belong to the install, not to the npm package. Pass lookPreviews: true only against Nodaro Cloud. For every other picker, draw your own visuals from label, description and category.

Read the catalogs over the API

Both endpoints are public, need no token, and are cached for 5 minutes.

MethodPathWhat it returns
GET/v1/picker-catalogsA directory of every picker: nodeType, label, catalogId, kind, valueField or fields, optionCount, and imageCount, the number of options with a picture (0 on a deployment that turned pictures off).
GET/v1/picker-catalogs/:nodeTypeOne picker's catalog. An unknown type answers 404 not_found.

GET /v1/picker-catalogs/:nodeType takes three query parameters. A bad value answers 400 validation_error.

ParameterValuesWhat it does
detailcompact (default) or fullcompact returns id, label, category, term, icon and imageUrl. full adds each option's description and promptHint.
categoryA category idFilters a single-dimension picker to one category.
fieldA field nameReturns one dimension of a multi-dimension picker, or one secondary field of Transition, Character FX or Character Motion.

For a style option, promptHint is the image wording. A video prompt adds the style's video wording, which no catalog endpoint returns.

Image and video styles. A style option offered for one kind of output only carries media, at both detail levels. The five video-only styles (felt-puppet, knitted-yarn, toy-figure-3d, painterly-3d and painted-clay) carry ["video"]. An option without media is offered for images and video. An image style list leaves out an option whose media lacks "image". A style sent to an image route anyway is still accepted, and the image receives its image wording.

Styles for restyling a clip. A style option that does not work well as a restyle of filmed footage carries restyle: false, at both detail levels: today fantasy, surrealism and concept-art. A list of styles for restyling a clip leaves it out. Video generation still offers it, and a restyle that names it is still accepted.

curl -s https://app.nodaro.ai/v1/picker-catalogs/mood | jq '.data.options[0]'
{ "id": "happy", "label": "Happy", "category": "positive", "term": "happy expression",
  "imageUrl": "https://cdn.nodaro.ai/cdn-cgi/image/width=480,format=auto,quality=80/picker-renders/710df65a-3c1e-485b-b8b7-29f7b3baf479.png",
  "imageSrcSet": "https://cdn.nodaro.ai/cdn-cgi/image/width=240,format=auto,quality=80/picker-renders/710df65a-3c1e-485b-b8b7-29f7b3baf479.png 240w, https://cdn.nodaro.ai/cdn-cgi/image/width=480,format=auto,quality=80/picker-renders/710df65a-3c1e-485b-b8b7-29f7b3baf479.png 480w, https://cdn.nodaro.ai/cdn-cgi/image/width=960,format=auto,quality=80/picker-renders/710df65a-3c1e-485b-b8b7-29f7b3baf479.png 960w, https://cdn.nodaro.ai/cdn-cgi/image/width=1920,format=auto,quality=80/picker-renders/710df65a-3c1e-485b-b8b7-29f7b3baf479.png 1920w" }
<img src="{imageUrl}" srcset="{imageSrcSet}" sizes="160px" alt="Happy">

The picture URLs

PickersPicture
person, styling, held-prop, material, animalA photo, WebP, up to 480 px wide.
music-genre, music-mood, instrumentation, voice-character, voice-deliveryOn Nodaro Cloud, a square photographic render from the Nodaro CDN: a 480 px still, plus imageSrcSet up to its full size. A language or accent render carries its flag. On a self-hosted install, a 3D emoji picture (WebP, 128 px) or a flag (WebP, 120 px wide).
Look pickers, such as style, color-look, lens, framing, lighting, mood, camera-format and camera-motionOn Nodaro Cloud, a 480 px still of the rendered preview from the Nodaro CDN, plus imageSrcSet; for camera-motion, a frame of the clip. A self-hosted install returns none.
  • Host. An install serves its own pictures under /picker-art/, so imageUrl uses its public address: https://app.nodaro.ai on Nodaro Cloud, or the PUBLIC_URL of a self-hosted install. Use each URL as given; never build one from an option id.
  • Caching. File names carry a content hash, so a changed picture gets a new URL. The files are served with Cache-Control: public, max-age=31536000, immutable and Access-Control-Allow-Origin: *, so any origin can load them in an <img>, with fetch or on a canvas.
  • Sizes (imageSrcSet). On the REST routes, a picture from the Nodaro CDN also carries imageSrcSet: the same picture at 240, 480, 960 and 1,920 px wide, as an HTML srcset value. The CDN never enlarges a picture, so the largest copy is at most the size of the source. The MCP tools leave imageSrcSet out. Give it to <img srcset> with a sizes that says how wide the tile is. The browser then fetches the smallest copy that stays sharp on that screen. imageUrl is the 480 px copy. Self-hosted pictures have one size and no imageSrcSet.
  • Topics. person and styling also return sections, each with a label, the fields it groups and an optional round imageUrl.
  • A deployment without pictures. On a deployment that turned the pictures off (catalogs.images), the replies carry no picture at all. No option has imageUrl or imageSrcSet, no topic has imageUrl, and every picker in the directory has an imageCount of 0. The same is true for GET /v1/catalogs and the MCP get_picker_catalog tool. Everything else is unchanged, so a client that draws a label for an option with no imageUrl needs no change.
curl -s https://app.nodaro.ai/v1/picker-catalogs/person | jq '.data.sections[0], .data.dimensions[0].options[0]'
{ "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" }

A deployment's curated catalogs

GET /v1/catalogs returns every catalog in one call, as a single flat shape. A deployment can curate its catalogs with vendored packs that replace, extend or deny options, and this endpoint reflects that curation. When the deployment registered packs, the response carries curated: true and the catalogs in data. With none, it carries curated: false, and you read the bundled catalogs per picker from /v1/picker-catalogs/:nodeType. The SDK method is client.catalogs.list().

Fill pickers from a description

POST /v1/text-to-picker chooses picker values from a free-text description of a scene or a shot. This is the AI Fill feature. It needs a token. A run makes one LLM call per family of pickers and is charged once. The charge is for the tokens its calls actually used, at the selected model's rate on the lane that served each call, the same rule as Describe to Picker. A default run reads every picker's catalog, so it uses several times the text of one image read.

  • Before the run, a hold is reserved: each call's first attempt at the model's output cap. A balance below the hold is refused with 402 ("Needs up to N credits").
  • The quote: POST /v1/credits/model-costs with the key text-to-picker:<model>, plus :direct on the provider's own API, returns an estimate of a typical run. It is listed in estimates, so show it as ≈N. Without llmModel, the run uses Gemini 3.8 Flash on the aggregator. Claude Opus 5.5 with no effort runs at high on Anthropic's own API, so its key is text-to-picker:claude-opus-5.5:direct.
  • Failures: every call the provider billed is charged, a failed one included. A run whose calls the provider did not bill is refunded.

Claude Fable 5 is offered on this route. See How LLM calls are charged. The route returns pickerJson, keyed by picker type, then dimension, then the chosen ids, and gaps, the described details no catalog option represents. The SDK method is client.pickerCatalogs.analyzeText(), and the CLI command is nodaro pickers analyze.

Character Motion metadata

The options of the Character Motion catalog can carry motion metadata, at both detail levels. The metadata describes the move:

  • Its requirements, its start and end poses, and the visibility and hands after the move.
  • Whether it needs free hands, its kind, a fixed pace and its counterpart.
  • Search aliases, and whether the id is retired and which id replaces it.

A missing field means unknown.

Keep retired ids working when you load saved workflows, and hide them from new choices. The type is CharacterMotionMetadata, exported by @nodaro/prompts.

Frequently asked questions

Do I need the Nodaro API to use the picker catalogs?

No. The catalogs, and the function that turns a selection into a prompt clause, ship in the @nodaro/prompts npm package, so your app can render the pickers and build prompts offline. The API endpoints exist for clients that cannot bundle the package.

How do I turn a picker selection into prompt text?

Call getParameterPromptHint with the picker's node type and its data, such as the chosen mood id. Nodaro runs the same function on its servers, so your prompt matches the one the editor builds.

Which languages do the picker labels support?

12 locales, English, Spanish, French, German, Brazilian Portuguese, Russian, Hindi, Japanese, Korean, Simplified Chinese, Hebrew and Arabic. The prompt text and the terms stay in English, because the models read them.

Why do the look pickers have no pictures on my self-hosted install?

The previews of the look pickers, such as Mood and Lighting, come from the Nodaro CDN, and only Nodaro Cloud returns them. Every install serves the photos of Person, Styling, Held Prop, Material and Animal, and the music and voice art.

How many picker catalogs are there?

39. There are 28 single-dimension pickers, such as Mood and Lens, and 11 multi-dimension pickers, such as Framing, Lighting and Person.

Last updated on

On this page