Lucentive Labs
Lucentive Labs Docs

Quickstart

Author a typed config, render it, and read back a deterministic export brief — in five steps.

The shortest path from a design question to a locked, machine-readable answer. Five steps; the last one is live on this page.

Author a config

A Config is groups → options → specimens. Each group is one decision; each option is a choice; each specimen is what the choice looks like. Mark one option per group recommended to set the default pick.

loupe.config.ts
import type { Config } from "@lucentive-labs/loupe-schema";

export const config: Config = {
  version: 1,
  title: "My direction",
  groups: [
    {
      id: "color",
      title: "Color system",
      prompt: "Which palette carries the brand?",
      options: [
        {
          id: "ink",
          label: "Ink + signal",
          recommended: true,
          specimen: { kind: "palette", colors: ["#0c1a1c", "#11272b", "#8499ab", "#2fd4c4"] },
        },
        {
          id: "amber",
          label: "Warm amber",
          specimen: { kind: "palette", colors: ["#14110e", "#f6a13c", "#ffc879", "#f7efe3"] },
        },
      ],
    },
  ],
};

Validate it

parseConfig throws on structural errors (bad crop rects, missing fields). validateConfig returns human-readable semantic errors — duplicate ids, missing asset references, dangling preview bands. Run both before you render.

tsx -e "import('@lucentive-labs/loupe-schema').then(async m => {
  const { config } = await import('./loupe.config.ts');
  const cfg = m.parseConfig(config);
  const errs = m.validateConfig(cfg);
  if (errs.length) { console.error('INVALID:\n- ' + errs.join('\n- ')); process.exit(1); }
  console.log('config valid');
})"

Render it

Pick the adapter that fits. In a React app, mount <Loupe /> and import the canonical styles once. For a portable, dependency-free artifact, run the generator (see the agent method).

app/page.tsx
import { Loupe } from "@lucentive-labs/loupe-react";
import "@lucentive-labs/loupe-dom/styles.css";
import { config } from "./loupe.config";

export default function Page() {
  return <Loupe config={config} />;
}

Lock your picks

Click a tile to lock it. The sticky preview recomposes, the "your picks" thumbnails update, and the progress counter advances. Single-select per group; click the same tile again does nothing, and Clear unlocks a group back to open.

Read the brief

The export brief is the deterministic handoff. The Copy brief button copies the markdown; both forms are also available from core:

tsx -e "Promise.all([
  import('@lucentive-labs/loupe-core'),
  import('@lucentive-labs/loupe-schema'),
  import('./loupe.config.ts'),
]).then(([core, schema, m]) => {
  const cfg = schema.parseConfig(m.config);
  const sel = core.recommendedSelections(cfg);
  const b = core.selectExportBrief(cfg, sel);
  console.log(b.markdown);
})"

See it run

The same flow, live. Lock a tile and the brief on the tutorial page updates with it.

LiveLock a tile in each group
60-second direction
2 of 2 locked
01
Color system
Which palette carries the brand?
02
Headline voice
What does the type say before the words do?

Build brief

The deterministic handoff for the next build pass. Stays in sync with your locked tiles.

Banned

  • Generic SaaS gradient blobs.
  • Cold corporate blue as the brand.

Keep loupe.config.ts as the single source of truth. The preview and the brief are both derived from the same selections, so they never drift.