Lucentive Labs
Lucentive Labs Docs
Working with agents

Recipes

Reproduce a real brand lab, embed Loupe in a React app, and swap brands without touching the config.

Three concrete tasks, each end to end. Copy, adapt, ship.

Reproduce a brand lab

This is what the examples/ias-control-plane fixture does: lock a real product surface decision — not a marketing brand — as an asset-light config (palette / type / layoutMock / motion only, so the artifact is one tiny self-contained file). The decisions are the actual choices a control plane makes: the hero treatment, how a run is drawn, the type system, how color carries agent state.

The shape, with the IAS signal-cyan-on-ink theme baked in:

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

const SIGNAL = "#54deec";   // the one accent
const INK = "#070c12";      // deep control-plane ink

export const config: Config = {
  version: 1,
  title: "IAS · Control-Plane Visual Lock",
  assets: {},                // asset-light: no binary files
  theme: {
    "color-bg": INK,
    "color-primary": SIGNAL,
    "color-ring": SIGNAL,
    "color-signal": SIGNAL,
    "font-mono": "'JetBrains Mono', ui-monospace, monospace",
    "radius-sm": "5px",
  },
  groups: [
    {
      id: "hero",
      title: "Control-plane hero treatment",
      prompt: "What does the first screen of the control plane show?",
      options: [
        { id: "liveFleet", label: "Live fleet, no chrome", recommended: true, specimen: { kind: "layoutMock", plan: "belowfold" } },
        { id: "split", label: "Split: claim + live panel", specimen: { kind: "layoutMock", plan: "threshold" } },
      ],
    },
    {
      id: "statusColor",
      title: "Status & signal color usage",
      prompt: "How does color carry agent state?",
      options: [
        { id: "cyanPlusState", label: "Cyan brand + 3 state colors", recommended: true, specimen: { kind: "palette", colors: ["#54deec", "#3ad6a8", "#f5b34a", "#ef5d6b"] } },
        { id: "monoPlusCyan", label: "Monochrome + cyan signal", specimen: { kind: "palette", colors: ["#2a3744", "#5a6b7a", "#8499ab", "#54deec"] } },
      ],
    },
  ],
  banned: ["Generic SaaS gradient blobs, AI-purple hero glows.", "Cyan used as decoration until it stops meaning signal."],
  workflow: ["Signal cyan #54deec is the single brand accent.", "Hand the exported brief to the IAS build pass as ground truth."],
};

Generate, verify, and export exactly as in the agent method. Here is a faithful, asset-light version of that lab running live — note it wears its own theme (signal cyan on ink), not this site's Night Atlas skin:

Live · a reproduced product lab (IAS)Lock tiles across the groups
IAS · Control-Plane Visual Lock
4 of 4 locked
01
Control-plane hero treatment
What does the first screen of the control plane show?
02
Agent run / trace visualization
How is a single agent run drawn?
03
Typography
What does the type system say a control plane is?
04
Status & signal color usage
How does color carry agent state?

Build brief

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

Workflow

  • Signal cyan #54deec is the single brand accent; state colors mean running / waiting / failed and nothing else.
  • Dark-first: the deep control-plane ink is canonical.
  • Hand the exported brief to the IAS build pass as ground truth.

Banned

  • Generic SaaS gradient blobs, abstract 3D orbs, or AI-purple hero glows.
  • Cyan used as decoration everywhere until it stops meaning 'signal'.
  • Status conveyed by color alone with no label.

Start from examples/ias-control-plane for an asset-light product lock, or examples/human-today when the decision is photographic art direction with imageCrop.

Embed Loupe in a React app

When a shipping app needs the picker live in its own UI, mount the React adapter and import the styles once.

Install

pnpm add @lucentive-labs/loupe-react @lucentive-labs/loupe-dom @lucentive-labs/loupe-schema

Keep the config a stable constant

The store is keyed on config identity, so define it as a module constant (or useMemo it) — never a fresh object per render.

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

export const config: Config = {
  version: 1,
  title: "My direction",
  groups: [ /* ... */ ],
};

Mount it and import the stylesheet once

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

export default function DecisionPage() {
  return (
    <Loupe
      config={config}
      storageKey="my-decision"          // persist picks across reloads
      onLockChange={(sel) => console.log("locked:", sel)}
    />
  );
}

To keep a live brief beside the picker (like the tutorial), feed onLockChange selections into selectExportBrief:

a live brief next to the picker
import { useState } from "react";
import { Loupe } from "@lucentive-labs/loupe-react";
import type { Selections } from "@lucentive-labs/loupe-core";
import { recommendedSelections, selectExportBrief } from "@lucentive-labs/loupe-core";
import { config } from "./loupe.config";

export function DecisionWithBrief() {
  const [sel, setSel] = useState<Selections | null>(null);
  const brief = selectExportBrief(config, sel ?? recommendedSelections(config));
  return (
    <>
      <Loupe config={config} onLockChange={setSel} />
      <pre>{brief.markdown}</pre>
    </>
  );
}

Swap brands without touching the config

One config, many skins. Because the brand is just the --loupe-* token contract, you re-skin at the render site — the decisions and tiles never change.

the same config, two brands
import { Loupe } from "@lucentive-labs/loupe-react";
import { config } from "./loupe.config";

const northwind = {
  "color-bg": "#14110e",
  "color-primary": "#f6a13c",
  "color-ring": "#f6a13c",
  "font-sans": "Manrope, ui-sans-serif, system-ui, sans-serif",
};

const ias = {
  "color-bg": "#070c12",
  "color-primary": "#54deec",
  "color-ring": "#54deec",
  "font-mono": "'JetBrains Mono', ui-monospace, monospace",
};

// The theme prop overrides config.theme; swap the object to swap brands.
<Loupe config={config} theme={northwind} />;
<Loupe config={config} theme={ias} />;

For a portable artifact, do the same with config.theme (baked in) or applyTheme(el, tokens) after mount. To bridge an existing Tailwind/design-system, point the tokens at var(--your-token) — see Theming › the Tailwind bridge.

Precedence: DEFAULT_TOKENSconfig.theme → the theme prop / applyTheme → your own CSS. They merge, so a partial override (just color-primary) is fine.