# Lumen > Lumen is a React component kit from Columen Labs: an app frame, data-driven pages and views, widgets, charts, drawers and pickers, all on one theme. You hand it data and a view config; it draws the screen. Lumen is built on shadcn/ui (Radix UI, Base UI, Tailwind CSS). Install: `bun add @columenlabs/lumen-ui @columenlabs/lumen-ui-types` (one exact version for both), then import `@columenlabs/lumen-ui/theme.css` after Tailwind. - Docs: https://lumen-ui.columenlabs.com/docs/ - Component manifest (JSON, props and snippets): https://lumen-ui.columenlabs.com/docs/manifests/components.json - Install guide: https://lumen-ui.columenlabs.com/docs/?path=/docs/install--docs - Prefer LumenSelector for every picker, LumenDrawer for side panels, LumenTabs for tabs and LumenConfirm for destructive confirms over the raw ui/* parts. ## Components ### IconButton Every icon button: the icon alone, its name in a tooltip and the accessible name. `busy` spins it, `pressed` fills it, `to` and `href` make it a link. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/frame-iconbutton--docs ```tsx import { IconButton } from '@columenlabs/lumen-ui'; ``` Props: - `icon: ComponentType<{ className?: string | undefined; }>` — A lucide icon, or any icon component that takes `className` (a view's own icon). - `label: string` — The action's name: the tooltip and the accessible name. - `asChild?: boolean` - `busy?: boolean` — Spins the icon while the action runs. - `href?: string` — Opens this outside address in a new tab instead of running `onClick`. - `pressed?: boolean` — A toggle that is on (pinned, followed): its icon fills in the app colour. - `size?: enum` = icon-sm — `icon-sm` (headers, toolbars) by default; `icon-xs` in table rows and chips, `icon` in forms. - `to?: string` — Opens this route instead of running `onClick`. - `variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | "destructive-confirm" | null` = ghost Example: ```tsx const Plain = () => ; ``` ### LumenShell The app frame: sidebar (icons-only collapse, a drawer on phone), page header, status bar, Settings and the profile menu. An app passes its brand, nav and routed page; its own parts go in the slots `brand`, `headerActions`, `panel`, `wrap`, `shared`, `profile` and `statusEnd`. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/frame-lumenshell--docs ```tsx import { LumenLayout, LumenShell, NavGroup, NoteLine, Tile, TileRow } from '@columenlabs/lumen-ui'; ``` Props: - `app: string` — The app's id: `data-app` on , which picks its accent. - `appLabel: string` — The app's name, heading its own Settings sections. - `brand: ReactNode` — The sidebar's top: the app's name, or a switcher between a product's apps. - `children: ReactNode` — The routed page (usually ``). - `nav: ReactNode` — The app's sidebar content: `NavGroup`s and `SidebarSection`s. - `finder?: LumenFinderConfig` — How ⌘K Find reads in this app: its chips and how the nav's pages group. - `headerActions?: ReactNode` = null — The product's buttons on every page header, after Refresh (the product's Tasks and the assistant). - `health?: ReactNode` — The services the app checks, as one `StatusHealth` chip at the status bar's end. - `hideAmounts?: boolean` = false — Shows Hide amounts (eye) in the header: masks the fields and values the app marks `amount`. - `onLogs?: (() => void)` — Opens the app's Logs page; shows Logs in the profile menu. - `onRefresh?: (() => Promise)` — Reloads the page's data and drops every cached read (TanStack Query `invalidateQueries`); shows Refresh in the header, which then mounts the page again. - `panel?: ReactNode` — A panel docked right of the page (the product's assistant). - `profile?: ReactNode` — Settings ▸ General ▸ Profile's content; no Profile section without it. - `releases?: string` — Turns on What's new: the app's releases API (`GET ?limit=&before=` answering `{releases}`). The status bar shows the newest version, which opens each release's Key takeaways. - `settings?: SettingsSectionDef[]` — The app's own Settings sections, listed after General and the product's. - `shared?: SettingsSectionDef[]` — Sections every app of the product shows, listed after General under `sharedLabel`. - `sharedLabel?: string` - `signOutUrl?: string` — Where Sign out goes; no Sign out without it. - `status?: ReactNode` — App-wide status bar items (health, freshness); pages add their own through `Page`. - `statusEnd?: ReactNode` — Status bar items after `health` and What's new, before the time zone. - `user?: ShellUser | null` = null — Who is signed in (the profile button); null while it loads. - `wrap?: ((frame: ReactNode) => ReactNode)` = (frame) => frame — Wraps the frame in the product's providers, inside the shell so they can call `useShell`. Example: ```tsx const Shell = () => Sample app} nav={()} user={{ email: 'ada@example.com', name: 'Ada Lovelace' }}>( ), }, ]} />); ``` ### LumenTabs A page’s tab row: every tab has an icon, a name and an optional count, and actions for the open tab sit at the row’s end. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/frame-lumentabs--docs ```tsx import { Badge } from '@columenlabs/lumen-ui/ui/badge'; import { LumenTabs } from '@columenlabs/lumen-ui'; ``` Props: - `onChange: (value: V) => void` - `tabs: LumenTab[]` - `value: string | undefined` - `actions?: ReactNode` — What only the open tab uses, at the row's right end. Example: ```tsx const Tabs = () => { const [, update] = useArgs(); return ( 12, }, { value: 'reports', label: 'Reports', icon: BarChart3Icon }, { value: 'setup', label: 'Setup', icon: SettingsIcon }, ]} onChange={(value) => update({ value })} /> ); }; ``` ### DetailsTable A small fixed table for a drawer or card: a header, rows of cells and an optional total row. `FactsTable` lists label and value pairs. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-detailstable--docs ```tsx import { DetailsTable, FactsTable } from '@columenlabs/lumen-ui'; ``` Props: - `head: { label: string; right?: boolean | undefined; grow?: boolean | undefined; }[]` — `grow` marks the column that takes the spare width; the last column does by default. - `rows: { key: string; cells: ReactNode[]; }[]` - `foot?: ReactNode[]` — An optional total row, one cell per column. Example: ```tsx const Details = () => ; ``` ### LumenLayout A page of cards from one saved view: its filter, a grid of widgets and its table. Give it a domain (the fields), a view config and the rows; it filters, groups, totals and lays out on the client. With `onConfigChange` the page offers Edit layout and + Widget. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-lumenlayout--docs ```tsx import { LumenLayout, SampleShell } from '@columenlabs/lumen-ui'; ``` Props: - `config: CustomViewConfig` - `crumbs: Crumb[]` - `domain: ViewDomain` - `load: LumenLoad` — The rows' loading state (`useLumenLoad` over the queries the rows come from): every view and widget shows its ↻, skeleton, error and load time from it, so it is required. - `rows: T[]` - `actions?: ReactNode` - `cards?: LumenLayoutCard[]` — The page's own cards, after the saved view's. One with a `frame` joins the saved layout as a `page` card: moved and sized like the rest, last until it is placed. - `columnPicker?: boolean` — False leaves Field out of the header's + Add column (a table whose columns are fixed, a Tune's tries); otherwise every editable view offers the Columns picker there. - `editable?: boolean` — Whether the page's layout can be edited (every page is a saved view, with this flag set in code for now). False locks it: no Edit, no + Widget, no column changes, even with `onConfigChange` given. Left out: editable where `onConfigChange` is. - `editor?: ViewEditorSection` — An app's own section in the Edit dialog (an explorer view's columns). - `fit?: boolean` — Cards take the page's width and wide ones scroll inside themselves (coverage pages). - `groupActions?: ((key: string, rows: T[]) => ReactNode)` - `hideFilter?: boolean` — No filter box (a short list, e.g. a playbook's variants). - `intro?: ReactNode` — Above the cards: tabs or a lead the app shows with this page. - `lead?: LumenLayoutCard[]` — The page's own cards before the saved view's (a chart over the list it draws from). - `meta?: ReactNode` — A short status line beside the breadcrumb ("Updated 18:22", a warnings badge). - `onConfigChange?: ((config: CustomViewConfig) => void)` — Saves an edited view; without it the page has no Edit button. - `onDelete?: (() => void)` - `onDuplicate?: (() => void)` — Duplicate and Delete, offered in the Edit dialog's More section. - `onOpen?: ((row: T) => void)` — A row click opens it (a page of its own) instead of the details drawer. - `onReset?: (() => void)` — A built-in page the person changed (`useAppView`): Edit layout offers Reset to default. - `onShownRows?: ((rows: T[]) => void)` — Told the rows the view shows after its filters, every page, whenever they change, so an app loads live data for those rows only. - `onSummaryRows?: ((rows: T[]) => void)` — Told every row the page's Summaries draw, so an app loads live data for those too : a Summary not linked to the table covers rows the table hides. - `onTokens?: ((tokens: FilterToken[]) => void)` — Told the filter's tokens whenever they change, so an app can load rows for what they pick (the Symbols view's `in` filter). - `rowPick?: string` — The `rowLink` value tapped when the page opens, so linked cards start narrowed to one row (a playbook's Results open on its Default variant). - `rowTitle?: ((row: T) => string)` — The details drawer title for a row. - `save?: enum` — How the person's chips, sort and grouping save (rule C, by surface): `document` (a saved view, the default) holds them for the visit and shows Changed with Save view and Revert; `sheet` (a workspace tab) saves every change at once through `onConfigChange`, like a cell in a sheet. - `selection?: { keys: Set; onChange: (keys: Set) => void; }` - `state?: ReactNode` — Shown in place of the table instead of the rows, e.g. an empty state ("No accounts yet"). - `status?: ReactNode` - `tile?: ((key: string, rows: T[]) => ReactNode)` — Draws one of the domain's tiles over the view's rows (not the table's filter); the view's `tiles` pick which, in order. - `viewId?: string` — The saved view's id, shown in the Edit dialog to copy for an MCP call. - `viewTools?: ReactNode` — Buttons in the view card's header ("+ Add time"). Example: ```tsx const TableOnly = () => ; ``` ### LumenPager The compact pager every table uses: the count opens the page sizes, the chevrons turn the page. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-lumenpager--docs ```tsx import { LumenPager } from '@columenlabs/lumen-ui'; ``` Props: - `onPage: (page: number) => void` - `onSize: (size: number) => void` - `page: number` - `size: number` - `total: number` - `noun?: string` = rows - `sizes?: readonly number[]` = [25, 50, 100, 250] as const — Page sizes offered; a server-paged source takes its own. Example: ```tsx const Pager = () => { const [, update] = useArgs(); return ( update({ page })} onSize={(size) => update({ size, page: 0 })} /> ); }; ``` ### LumenTokenizedFilter The filter box: type to search, or pick `field:value` chips grouped by field with counts. Number fields take one condition (`> 100`, `a..b`). `useLumenFilter` holds the state and returns the matching rows. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-lumentokenizedfilter--docs ```tsx import { LumenTokenizedFilter } from '@columenlabs/lumen-ui'; ``` Props: - `filter: { fields: FilterField[]; rows: T[]; indexed: { row: T; values: Record; nums: Record; }[]; tokens: FilterToken[]; setTokens: (next: FilterToken[]) => void; text: string; setText: Dispatch>; results: T[]; hasText: boolean; }` - `changed?: boolean` — Other unsaved changes the save icon also saves (the page's link colours). - `className?: string` - `label?: string` = Filter — The input's accessible name. - `link?: FilterLink | null` — Its card's link colour; a coloured card shares every chip. `null`: it never shares (a widget's own fixed filter). - `onSave?: ((tokens: FilterToken[]) => void)` — Saves the box's filters as the view's own. - `placeholder?: string` = Filter… - `saved?: FilterToken[]` — The view's saved filters; the save button shows when the box differs from them. Example: ```tsx const Filter = () => ; ``` ### LumenView One view as a card: a table of the rows a page hands it, with sort, totals, grouping and a pivot. LumenLayout puts it on a page with its filter; use it alone for a second table. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-lumenview--docs ```tsx import { LumenView } from '@columenlabs/lumen-ui'; ``` Props: - `config: CustomViewConfig` - `domain: ViewDomain` — The view's domain, its formula columns included (`withViewFields`). - `rows: T[]` — The rows the view shows: its page's filter already applied. - `cardId?: string` = view — The card's id on its page (its fold is kept per id); a page's second table passes its own. - `controls?: CardControls` — Its place in a LumenLayout: the layout's edit state and the card's ⋯ menu. - `filter?: ReactNode` — The view's own filter and Group in its header (the filter is the table's, not the page's), and how many tokens it holds. - `filterCount?: number` - `groupActions?: ((key: string, rows: T[]) => ReactNode)` — Buttons at the end of a group row's label (pin a group), given its key and rows. - `groupBy?: ViewField | null` = null — The grouping the page picked (its Group by switch); null: flat rows. - `inLayout?: boolean` = false — In a LumenLayout's scroller: the table scrolls with the page, its header pinned. - `link?: CardLink` — Its link colour on the layout. - `load?: LumenLoad` — The rows' loading state (`useLumenLoad`): the view shows its skeleton, progress bar, ↻, a failure with Retry and, with Observability details on, its load time. - `note?: ReactNode` — A short subtitle after the title (a repo path). - `onAddColumn?: AddColumn` — The + at the end of the header (Edits in place): Field (the Columns picker, at the +), Formula, Your data and Ask the assistant; each left out is not offered. - `onColumns?: ((keys: string[]) => void)` — The Columns picker in the header: turns any of the domain's fields on or off and hands back the view's new columns. Left out: no picker. - `onDetails?: ((row: T) => void)` — A row click: opens the details drawer, or the row's own page. - `onSort?: ((sort: { key: string; desc: boolean; } | null) => void)` — Told when a header click changes the sort. - `pivot?: boolean` = false — Shows the view's pivot instead of its rows. - `selection?: { keys: Set; onChange: (keys: Set) => void; }` — Row checkboxes (strategy lab): the checked row keys and their setter; the header box checks every shown row. - `shown?: Partial>` — Its controls from its card (Edit widget ▸ Controls); missing keys take the Table defaults. - `sort?: { key: string; desc: boolean; } | null` = z .object({ key: fieldKey, desc: z.boolean().default(false) }) .nullable() .default(null) — The sort shown, when the page holds it (rule C); left out: the view's saved sort. - `state?: ReactNode` — Shown in place of the table instead of the rows, e.g. an empty state ("No accounts yet"). - `strip?: ReactNode` — A line under the filter: the card's Link offer. - `subgroup?: string | null` — What each row opens, by the domain's sub-group key (the page's Group button); null: none, left out: the view's saved pick. - `tapped?: ((row: T) => boolean)` — Rows picked by a tap (a linked table's row filter): a muted fill, no border. - `title?: string` = config.name — The card's title; left out: the view's name. - `tools?: ReactNode` — Buttons in the card's header before the pager ("+ Add time"). - `total?: number` = rows.length — Every row before the filter, for "12 of 40 rows"; left out: `rows`. Example: ```tsx const Table = () => ; ``` ### Tile One headline number with its label and up to a few lines under it. `TileRow` lays tiles in a row that wraps on phones. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-tile--docs ```tsx import { Tile, TileRow } from '@columenlabs/lumen-ui'; ``` Props: - `label: ReactNode` - `value: ReactNode` - `caption?: ReactNode` - `className?: string` - `lines?: ReactNode[]` — Secondary lines under the value, one each (a Summary card's). - `valueClassName?: string` Example: ```tsx const One = () => ; ``` ### LumenConfirm Asks before an action that cannot be undone. Wrap the button that opens it, or control `open` yourself. Destructive by default (a red action); `destructive={false}` for a plain confirm. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-lumenconfirm--docs ```tsx import { Button } from '@columenlabs/lumen-ui/ui/button'; import { LumenConfirm } from '@columenlabs/lumen-ui'; ``` Props: - `onConfirm: () => void` - `title: ReactNode` — The question, e.g. `Delete view Options?`. - `action?: ReactNode` = Delete — The confirm button's label. - `cancel?: string` = Cancel — The button that backs out (Keep, Keep running). - `children?: ReactNode` — The button that opens the dialog; leave it out and control `open` instead. - `description?: ReactNode` = This cannot be undone. - `destructive?: boolean` = true — False for a confirm that destroys nothing (a new version): its action is not red. - `onOpenChange?: ((open: boolean) => void)` - `open?: boolean` - `typeToConfirm?: string` — A word the person must type before the action unlocks, for a step that loses a lot (`DELETE`). Example: ```tsx const Destructive = () => (); ``` ### LumenDrawer The one pullout drawer: a right-side panel on desktop whose width drags (320px to 60%), a bottom sheet on phone. Its body holds `DrawerSection`s; its footer always shows (Close when you pass no actions). Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-lumendrawer--docs ```tsx import { Button } from '@columenlabs/lumen-ui/ui/button'; import { DrawerSection, LumenDrawer } from '@columenlabs/lumen-ui'; ``` Props: - `children: ReactNode` — Stacked DrawerSections; the drawer pads and scrolls them. - `onClose: () => void` - `open: boolean` - `title: ReactNode` - `defaultSize?: Size` = 32% — Width on desktop: starts at `defaultSize`, drags between `minSize` and `maxSize`. - `description?: ReactNode` — A muted line under the title ("Active · invited Oct 16"); read aloud only when left out. - `footer?: ReactNode` — The footer's actions, right-aligned (a destructive action, Cancel and Save); Close when left out. - `footerStart?: ReactNode` — Left side of the footer: a count or status line ("12 releases"). - `maxSize?: Size` = 60% - `minSize?: Size` = 320px Example: ```tsx const Drawer = () => { const [, update] = useArgs(); return ( <> update({ open: false })} footer={}>

The first store, on the river front.

1,701 orders this month.

); }; ``` ### LumenForm A form from a list of fields: text, secret, number, date, textarea, select and multi. Each field checks itself; Save lists what needs fixing and takes you to it instead of saving. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-lumenform--docs ```tsx import { LumenForm } from '@columenlabs/lumen-ui'; ``` Props: - `fields: readonly LumenFormField[]` - `onSave: (values: LumenFormValues) => void` — Runs only when every check passes. A secret left empty is left out. - `busy?: boolean` = false - `footer?: ReactNode` — Beside Save, on the left (a Test button, a note). - `initial?: LumenFormValues` - `onCancel?: (() => void)` - `saveLabel?: string` = Save Example: ```tsx const Form = () => String(v).startsWith('https://') || 'Must start with https://', }, { id: 'key', label: 'API key', kind: 'secret', saved: '••••3f9a' }, { id: 'region', label: 'Region', kind: 'select', options: [ { value: 'north', label: 'North' }, { value: 'centre', label: 'Centre' }, { value: 'south', label: 'South' }, ], }, { id: 'notes', label: 'Notes', kind: 'textarea' }, ]} />; ``` ### LumenSelector Every picker in one part: one value or many, groups, search past 8 options, and an Add row that turns typed text into a new value. On a phone the list opens as a bottom sheet. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-lumenselector--docs ```tsx import { LumenSelector } from '@columenlabs/lumen-ui'; ``` Props: - `onChange: ((value: V) => void) | ((value: V[]) => void)` - `value: string | readonly V[] | null | undefined` - `align?: enum` - `className?: string` - `create?: ((query: string) => V | null)` — Turns typed text into a new value (a ticker, "ema 13", a new sector); null refuses it. - `createLabel?: ((query: string) => string)` — The create row's text; `Add "…"` by default. - `disabled?: boolean` - `empty?: string` — Shown when nothing matches. - `groups?: readonly LumenOptionGroup[]` - `header?: ReactNode` — Above the list (a scope switch such as "for SPY / All"). - `icon?: Icon` — The icon trigger's icon, or a leading icon in a field or chip. - `id?: string` - `label?: string` — Accessible name; also the phone sheet's title. - `max?: number` — At most this many picked; the rest of the list greys out. - `multiple?: boolean` - `onHighlight?: ((value: V) => void)` — A row was highlighted (hover or arrow keys): a caller reads it ahead (the assistant's Chats). - `onOpenChange?: ((open: boolean) => void)` - `open?: boolean` - `options?: readonly LumenOption[]` — The options; or `groups` to fix the group order. - `placeholder?: string` - `search?: boolean | "auto"` — `auto`: a search box past 8 options. - `size?: enum` - `trigger?: enum` — `field` by default. Example: ```tsx const Single = () => { const [, update] = useArgs(); return (
update({ value })} />
); }; ``` ### RenameField A name renamed in place: Enter or leaving the field saves, Escape undoes, and empty or ↺ goes back to the source’s own name. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-renamefield--docs ```tsx import { RenameField } from '@columenlabs/lumen-ui'; ``` Props: - `onSave: (name: string | null) => void` — null resets to `original`. - `original: string` — The source's own name (the broker's, the bank's), what Reset goes back to. - `originLabel: string` — Where `original` comes from, for the tooltip: "At the broker", "At the bank". - `value: string` — The name shown now: the person's own, or `original`. Example: ```tsx const Rename = args => { const [, update] = useArgs(); return ( update({ value: name ?? args.original })} /> ); }; ``` ### ResetButton Puts a setting back to its default: a ↺ that shows only while something differs, its tooltip naming what it goes back to. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-resetbutton--docs ```tsx import { ResetButton } from '@columenlabs/lumen-ui'; ``` Props: - `changed: boolean` — Whether anything differs from the default; nothing shows when it doesn't. - `label: string` — The tooltip and accessible name: "Reset to 15 – 45", "Reset all". - `onReset: () => void` - `size?: enum` = icon-xs Example: ```tsx const Reset = () => ; ``` ### SaveButton Save that says why it cannot save: with issues, a chip before it counts them and lists each; tapping Save takes you to the first one. Never a bare disabled button. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-savebutton--docs ```tsx import { SaveButton } from '@columenlabs/lumen-ui'; ``` Props: - `issues: FormIssue[]` - `busy?: boolean` = false — Saving now: Save waits. - `className?: string` - `disabled?: boolean` = false — Nothing to save (no change): Save is greyed and needs no reason. - `icon?: ComponentType<{ className?: string; }> | null` - `label?: string` = Save - `onSave?: (() => void)` - `type?: enum` = button — `submit` inside a form: the form's own submit runs only when there are no issues. Example: ```tsx const Ready = () => ; ``` ### GrowthChart Growth of $1 for a set of lines on a log, linear or fit scale, with optional bands, markers and this year’s start. `format` changes the axis from `1.23×` to any unit. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/charts-growthchart--docs ```tsx import { GrowthChart, paletteAt } from '@columenlabs/lumen-ui'; ``` Props: - `className: string` - `lines: Series[]` - `scale: enum` - `bands?: ChartBand[]` = [] - `format?: ((v: number) => string)` = (v: number) => `${v < 0 ? MINUS : ''}${Math.abs(v).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}×` - `markers?: ChartMarker[]` = [] - `sinceYears?: number` - `ytd?: boolean` = true Example: ```tsx const Growth = () => ; ``` ### LumenEChart Any Apache ECharts option, themed: `useChartTokens` gives the theme’s colours and fonts and redraws on a theme change; `axisStyle` and `baseOption` style axes the kit’s way. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/charts-lumenechart--docs ```tsx import { LumenEChart } from '@columenlabs/lumen-ui'; ``` Props: - `label: string` — What the chart shows, for screen readers. - `option: ECBasicOption` - `className?: string` - `height?: number` = 260 - `on?: EChartHandlers` — Event handlers by ECharts event name ('click', 'axisareaselected'). Example: ```tsx const Bars = () => ; ``` ### LumenGeoMap A world map shaded by one number per country (ISO alpha-2 codes); countries left out draw empty. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/charts-lumengeomap--docs ```tsx import { LumenGeoMap } from '@columenlabs/lumen-ui'; ``` Props: - `label: string` — What the map shows, for screen readers. - `values: Record` — The number for each country, by ISO alpha-2 code; countries left out draw empty. - `height?: number` = 260 - `unit?: string` — What the number counts ("visits"), shown in the tooltip after it. Example: ```tsx const World = () => ; ``` ### PriceChart A price chart on TradingView Lightweight Charts™: candles, OHLC bars, line, area or Heikin-Ashi, with volume, overlays on the price and a pane per oscillator. Indicators are worked out in the browser from the bars (`computeIndicators`). Made-up bars here. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/charts-pricechart--docs ```tsx import { PriceChart, resolveIndicator, computeIndicators } from '@columenlabs/lumen-ui'; ``` Props: - `bars: ChartBar[]` - `chosen: ChartIndicator[]` — Overlays drawn on the price and oscillators in their own panes. - `type: enum` - `values: IndicatorValues` - `baseline?: number | null` = null — Draws this level as a dashed line (the prior close). - `className?: string` - `navigator?: boolean` = false — The timeline under the chart: drag its window to move, its edges to zoom. - `onOutside?: ((span: TimeSpan) => void)` — The timeline asked for a span the chart's bars do not cover (intraday bars, older days). - `onRange?: ((span: TimeSpan, byPerson: boolean) => void)` — The shown span (first and last bar), and whether the person moved it (drag, wheel, timeline). - `range?: TimeSpan` — The span to show; a new object moves the chart there, panning and zooming move it after. - `timeline?: ChartBar[]` — The navigator's bars when they differ from the chart's: the symbol's whole history, coarser, with the shown span cut out of it. Left out: the chart's own bars. - `timeZone?: string` = America/Los_Angeles — The person's time zone, for intraday bars (Unix seconds). - `visible?: number` — Bars shown at first, counted from the latest; left out: all of them. - `zoom?: boolean` = false — Wheel and pinch zoom (off where the page itself scrolls under the chart). Example: ```tsx const Candles = () => resolveIndicator(id) ?? [])} values={computeIndicators(bars, chosen)} className="h-96 w-full" navigator />; ``` ### LumenCodeEditor An editable code box (CodeMirror) for SQL, JavaScript or JSON, loaded only when it first shows. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/code-lumencodeeditor--docs ```tsx import { LumenCodeEditor } from '@columenlabs/lumen-ui'; ``` Props: - `onChange: (value: string) => void` - `value: string` - `className?: string` - `lang?: enum` - `readOnly?: boolean` - `wrap?: boolean` — Wraps long lines instead of scrolling sideways (off by default). Example: ```tsx const Editor = () => { const [, update] = useArgs(); return ( update({ value })} /> ); }; ``` ### LumenCode Read-only code, highlighted by Shiki in the theme’s light and dark colours, with a copy button in its header. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/code-lumencode--docs ```tsx import { LumenCode } from '@columenlabs/lumen-ui'; ``` Props: - `code: string` - `className?: string` - `header?: boolean` = true — The label and copy icon over the code (chat); off where the page has its own (a file view). - `lang?: string` — The fence tag or file extension; none draws plain text. - `lineNumbers?: boolean` = false — Numbers each line (files); off for chat snippets. - `wrap?: boolean` = false — Wraps long lines instead of scrolling sideways. Example: ```tsx const Code = () => ; }`} />; ``` ### ColorDot A small dot in any CSS colour (a theme token reads in both themes); `ColorChip` puts one before a label. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/feedback-colordot--docs ```tsx import { ColorChip, ColorDot } from '@columenlabs/lumen-ui'; ``` Props: - `color: string` - `className?: string` Example: ```tsx const Dot = () => ; ``` ### LumenBoundary Keeps a crash inside its card: the card says it hit an error, with Try again and Reload, and the page around it keeps working. Every grid card and widget body sits in one. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/feedback-lumenboundary--docs ```tsx import { LumenBoundary } from '@columenlabs/lumen-ui'; ``` Props: - `where: string` — Logged with the error: the card's id, or the panel's name ("assistant"). - `what?: enum` = card Example: ```tsx const CaughtError = () => ; ``` ### NoteLine One muted line over a card or page: an icon, what it says, and an optional button. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/feedback-noteline--docs ```tsx import { Button } from '@columenlabs/lumen-ui/ui/button'; import { NoteLine } from '@columenlabs/lumen-ui'; ``` Props: - `icon: LucideIcon` - `title: ReactNode` — The point, in a few words. - `action?: ReactNode` — A button the note carries. - `note?: ReactNode` — The detail, one line. Example: ```tsx const Note = () => Connect )} />; ``` ### Badge shadcn/ui's Badge with Lumen's `success`, `warning` and `info` tones added. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/base-parts-badge--docs ```tsx import { Badge } from '@columenlabs/lumen-ui/ui/badge'; ``` Props: - `asChild?: boolean` = false - `variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | "success" | "warning" | "info" | null` = default Example: ```tsx const Default = () => Live; ``` ### Button shadcn/ui's Button in Lumen's theme. `destructive` stays muted until hovered and always opens a confirm; its final button is `destructive-confirm`. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/base-parts-button--docs ```tsx import { Button } from '@columenlabs/lumen-ui/ui/button'; ``` Props: - `asChild?: boolean` = false - `size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null` = default - `variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | "destructive-confirm" | null` = default Example: ```tsx const Default = () => ; ``` ### Gallery The common base parts in one place, in the light or dark theme. Each is the shadcn/ui component of the same name; its own docs list every prop. Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/base-parts-gallery--docs ```tsx import { Alert, AlertDescription, AlertTitle } from '@columenlabs/lumen-ui/ui/alert'; import { Badge } from '@columenlabs/lumen-ui/ui/badge'; import { Button } from '@columenlabs/lumen-ui/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@columenlabs/lumen-ui/ui/card'; import { Checkbox } from '@columenlabs/lumen-ui/ui/checkbox'; import { Input } from '@columenlabs/lumen-ui/ui/input'; import { Kbd, KbdGroup } from '@columenlabs/lumen-ui/ui/kbd'; import { Label } from '@columenlabs/lumen-ui/ui/label'; import { Progress } from '@columenlabs/lumen-ui/ui/progress'; import { Separator } from '@columenlabs/lumen-ui/ui/separator'; import { Skeleton } from '@columenlabs/lumen-ui/ui/skeleton'; import { Slider } from '@columenlabs/lumen-ui/ui/slider'; import { Spinner } from '@columenlabs/lumen-ui/ui/spinner'; import { Switch } from '@columenlabs/lumen-ui/ui/switch'; import { Textarea } from '@columenlabs/lumen-ui/ui/textarea'; ``` Example: ```tsx const Gallery = () => (
Buttons and badges Variants share one height per size. Live Late New Draft Fields Inputs, switches and sliders.