Parameter Picker Catalogs
Nodaro’s editor has a family of parameter pickers — curated, tile-grid selectors for things like Mood, Lens, Setting, Framing, Lighting, Person, Music Genre, Voice Character, and ~40 more. A picker never calls the API; it contributes a descriptive clause to a downstream node’s prompt. (The deepest multi-dim picker, Person, defaults to a Compact grouped-pill view with a per-device Detailed toggle for the full tile-grid; the view mode is purely presentational and never changes the emitted clause — see the Person node page.)
Every picker’s data — its options, the prompt fragment each option contributes, its categories, and its i18n keys — ships as pure data in @nodaro/shared. So you can build the exact same pickers in your own app, in your own styling, and assemble the exact same prompts, with no API calls and no coupling to Nodaro’s UI.
Library first. Picker catalogs are static config + pure functions, so the preferred path is to
importthe data and run it locally — typed, offline, tree-shakeable, with no API round-trips. (The SDK/API is otherwise for server state: jobs, credits, uploads, workflows.) A read-only discovery endpoint also exists for clients that can’t bundle the package (e.g. LLM agents over MCP):GET /v1/picker-catalogs,client.pickerCatalogs.list()/get(),nodaro pickers, and the MCPget_picker_catalogtool — see API Integration §11, the SDK Reference, the CLI, and the MCP tools. They return the same data this page documents.
npm install @nodaro/shared @nodaro/sdk
The registry
import { PICKER_CATALOGS, getPickerCatalog, listPickerCatalogs } from "@nodaro/shared"
| Export | Description |
|---|---|
PICKER_CATALOGS |
readonly PickerCatalog[] — every picker (27 single + 11 multi). |
getPickerCatalog(nodeTypeOrCatalogId) |
One catalog by nodeType (e.g. "mood") or catalogId. |
listPickerCatalogs() |
All of them. |
interface PickerOption {
id: string
label: string // English; localize via catalogId (see i18n)
description?: string
category?: string // group id (matches categoryOrder / categoryLabels)
promptHint: string // the clause this option contributes ("" for no-op options like "auto")
icon?: string // reserved; previews are app-side — render your own (see Visual)
}
interface PickerDimension { // multi-dim pickers only
field: string // e.g. "shotSize"
label: string
options: readonly PickerOption[]
}
interface PickerCatalog {
nodeType: string // "mood", "framing", …
label: string
catalogId: string // i18n key
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-dim
dimensions?: readonly PickerDimension[] // multi-dim
}
Single-dimension pickers (e.g. Mood)
A single picker is one choice from a flat (optionally grouped) list. options carries everything you need to render the grid.
import { getPickerCatalog, getParameterPromptHint } from "@nodaro/shared"
import { createClient } from "@nodaro/sdk"
const client = createClient({ apiKey: process.env.NODARO_API_KEY })
const mood = getPickerCatalog("mood")! // { nodeType:"mood", valueField:"mood", options:[…], categoryOrder, categoryLabels }
// 1. Render your own tile-grid (group by category if you like)
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 → prompt clause → run
const selected = "serene"
const clause = getParameterPromptHint({ type: "mood", data: { mood: selected } })
// (or just: mood.options!.find(o => o.id === selected)!.promptHint)
const prompt = ["a portrait of a woman", clause].filter(Boolean).join(", ")
const { jobIds } = await client.nodes.run("generate-image", { prompt })
Multi-dimension pickers (e.g. Framing)
Some pickers set several independent fields at once — Framing is shot size and angle and coverage and composition and vantage. Each catalog exposes dimensions, one { field, label, options } per field.
import { getPickerCatalog, getParameterPromptHint } from "@nodaro/shared"
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 → clause. getParameterPromptHint composes all the set fields:
const value = { shotSize: "close-up", angle: "low-angle", composition: "rule-of-thirds" }
const clause = getParameterPromptHint({ type: "framing", data: value })
// → "close-up shot, …, low-angle, …, rule-of-thirds composition, …"
getParameterPromptHint({ type, data }) is the universal way to turn any selection (single or multi) into its clause — the exact same function Nodaro runs server-side, so your output matches the editor’s. (Per-catalog builders like buildFramingHints(value) exist too, if you prefer.)
Putting it together
// Compose several pickers into one prompt, then generate.
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 { jobIds } = await client.nodes.run("generate-image", { prompt, model: "gpt-image" })
Localization
Catalog labels are English. Localized strings for 12 locales (en, es, fr, de, pt-BR, ru, hi, ja, ko, zh-CN, he, ar) ship in @nodaro/shared, keyed by each catalog’s catalogId. promptHint clauses stay English (they feed the model).
English needs no setup. For other locales, register the per-locale “sidecar” bundles once at startup, then resolve labels synchronously (with English fallback):
import { registerSidecarLoaders, ensureLocaleCatalogLoaded, resolveLabel } from "@nodaro/shared"
// 1. Once at startup (Vite app): wire the lazy-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 (sync; falls back to English if missing or not yet loaded)
const label = resolveLabel(mood.catalogId, option.id, option.label, "fr")
The bundles load lazily, so registerSidecarLoaders takes a glob of loaders (Vite’s import.meta.glob). Backends and tests skip i18n and use the English label directly.
Visual
Picker icon/thumbnails are not shipped — the editor’s previews are bespoke React components, and an external app should render its own visuals in its own style (the data gives you label, description, and category to build a rich grid). icon? is reserved for a future release that bakes static thumbnails into the catalog data.
Reference
getParameterPromptHint({ type, data }) |
Selection → composed prompt clause (universal). |
build<Name>Hints(value) |
Per-catalog clause builder (e.g. buildFramingHints). |
get<Name>PromptHint(id) |
Single option → clause (e.g. getMoodPromptHint). |
PICKER_CATALOGS / getPickerCatalog / listPickerCatalogs |
The registry. |
See also: SDK Quickstart · SDK Reference · Embed App Guide