@usetheo/ui 0.35.1 → 0.35.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,62 @@
1
1
  # @usetheo/ui
2
2
 
3
- Violet Forge design system — the generic React components + primitives that form the non-AI foundation of the Theo UI stack. Apache-2.0, ESM-only.
3
+ Violet Forge design system — the generic React components and primitives that form the non-AI
4
+ foundation of the Theo UI stack. Apache-2.0, ESM-only.
4
5
 
5
- **54 components** (39 primitives + 15 composites) + the Violet Forge foundation (`cn`, tailwind-preset, themes). Depended on by `@theokit/ui` (the AI-native layer).
6
+ This is the layer with **no agent in it**: the shell, the tables, the forms, the feedback states,
7
+ the dashboard pieces. [`@theokit/ui`](https://github.com/usetheokit/theokit-ui) builds on top of it
8
+ with the agent surfaces (chat threads, tool calls, approval cards) and shares the same tokens, so
9
+ the two compose without a visual seam.
10
+
11
+ **Rule of thumb:** if the component would exist in an app with no agent in it, it belongs here.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pnpm add @usetheo/ui
17
+ ```
18
+
19
+ Or copy a single component, shadcn-style, from the published registry:
20
+
21
+ ```bash
22
+ npx shadcn@latest add https://usetheodev.github.io/usetheo-ui/r/data-table.json
23
+ ```
24
+
25
+ ## Catalog
26
+
27
+ <!-- BEGIN:catalog -->
28
+
29
+ **87 components** — 45 primitives + 42 composites.
30
+
31
+ ### Primitives (45)
32
+
33
+ `ActionBar` · `Alert` · `Avatar` · `Badge` · `Breadcrumb` · `Button` · `Card` · `Checkbox` · `Combobox` · `CopyButton` · `DangerZone` · `DescriptionList` · `Dialog` · `DropdownMenu` · `EmptyState` · `FileDropzone` · `FormField` · `Input` · `JsonViewer` · `Label` · `LoginSplit` · `MetricsPanel` · `Pagination` · `PinInput` · `PlanBadge` · `Progress` · `RadioGroup` · `ScrollArea` · `Select` · `Sheet` · `Sidebar` · `Skeleton` · `Slider` · `SocialAuthRow` · `StatTile` · `StatusDot` · `Switch` · `Table` · `Tabs` · `Textarea` · `Timestamp` · `Toast` · `Tooltip` · `Topnav` · `UpdateBanner`
34
+
35
+ ### Composites (42)
36
+
37
+ `AccountMenu` · `AnnotationInput` · `AnnotationSummaryGroup` · `AttributesTable` · `ChatMessageCard` · `CodeBlock` · `CommandPalette` · `CommentThread` · `ConfirmDialog` · `DataTable` · `DatasetItemDiff` · `DeploymentRow` · `DiffView` · `DomainConfig` · `EnvVarEditor` · `EvaluatorForm` · `Histogram` · `IoCards` · `MessageBranchSelector` · `MetricCard` · `PageShell` · `PercentileChart` · `PreviewEnvCard` · `PriceBreakdown` · `ProjectCard` · `PromptTemplateEditor` · `PromptVersionDiff` · `RollbackUi` · `SessionSummary` · `SessionTimeline` · `SeverityBadge` · `SpanGraph` · `SpanTree` · `SpanWaterfall` · `StatusIndicator` · `Stepper` · `TagInput` · `TaskHeader` · `TokenCostBreakdown` · `TraceCompare` · `TraceTranscript` · `TrendChart`
38
+
39
+ <!-- END:catalog -->
40
+
41
+ Generated from `src/components/` by `pnpm sync:readme` — do not edit the block above by hand.
42
+
43
+ ## Theming
44
+
45
+ The tokens come from the Violet Forge foundation shipped here (`tailwind-preset`, `themes`, `cn`).
46
+ Consumers that also install `@theokit/ui` get the same token names, so a theme defined once applies
47
+ to both packages.
48
+
49
+ ## Development
50
+
51
+ ```bash
52
+ pnpm install
53
+ pnpm dev # Ladle catalog
54
+ pnpm test
55
+ pnpm typecheck
56
+ pnpm lint
57
+ pnpm build
58
+ pnpm sync:readme # regenerate the catalog block above
59
+ pnpm registry:build # rebuild registry/r/*.json for the shadcn path
60
+ ```
6
61
 
7
62
  > Seeded from `theo-ui` @ `2b46eca` (2026-07-03, AI-exclusive pivot milestone M-B).
@@ -2,7 +2,7 @@ import { type HTMLAttributes, type ReactNode } from "react";
2
2
  /**
3
3
  * MetricCard — dashboard metric tile (composite).
4
4
  *
5
- * Pattern recurrente "Card + CardHeader + value + delta + trend icon" promoted
5
+ * Recurring pattern "Card + CardHeader + value + delta + trend icon" promoted
6
6
  * to a first-class composite per ADR-0007 community-best-practices plan.
7
7
  *
8
8
  * Trend → token mapping (default `invertTrend=false`):
@@ -51,37 +51,38 @@ export declare function niceMax(values: number[]): number;
51
51
  */
52
52
  export declare function seriesPath(points: TrendPoint[], xScale: (x: number) => number, yScale: (y: number) => number): string;
53
53
  /**
54
- * Quais posicoes do eixo recebem rotulo.
54
+ * Which axis positions receive a label.
55
55
  *
56
- * Rotular 31 pontos em ~450px e ilegivel; rotular so as pontas nao responde "quando foi o pico".
57
- * A densidade sai da largura REAL (`viewW`, que o ResizeObserver ja rastreia), com o primeiro e o
58
- * ultimo SEMPRE presentes: sao as bordas da janela, e sem eles o leitor nao sabe de quando ate
59
- * quando o grafico fala.
56
+ * Labelling 31 points in ~450px is illegible; labelling only the edges does not answer "when was
57
+ * the spike". The density comes from the REAL width (`viewW`, which the ResizeObserver already
58
+ * tracks), with the first and the last ALWAYS present: they are the window's edges, and without
59
+ * them the reader does not know from when to when the chart speaks.
60
60
  *
61
- * Fora do JSX porque e aritmetica, e aritmetica isolada e aritmetica que a mutacao alcanca.
61
+ * Outside the JSX because it is arithmetic, and isolated arithmetic is arithmetic mutation reaches.
62
62
  */
63
63
  export declare function xTicks(axisXs: number[], viewW: number): number[];
64
64
  /**
65
- * Quais pontos recebem marcador.
65
+ * Which points receive a marker.
66
66
  *
67
- * Duas razões distintas para marcar, e a segunda só apareceu quando séries com lacuna passaram a
68
- * existir (M144):
67
+ * Two distinct reasons to mark, and the second only appeared once series with gaps came to exist
68
+ * (M144):
69
69
  *
70
- * 1. **Série esparsa** (< `SPARSE_MARKER_MAX` pontos FINITOS) — 1-4 pontos leem como um penhasco
71
- * dramático; o marcador ancora o leitor no dado real. Regra do M76 — que passou a contar dado,
72
- * não slot, porque a densificação do M144 desfez a equivalência entre os dois.
73
- * 2. **Ponto isolado** — um ponto finito cercado de lacunas dos dois lados vira um `M` sozinho no
74
- * `path`, e um `M` sozinho **desenha nada**: bounding-box zero, invisível. O dado existe, está
75
- * correto, e o operador não vê. Medido no e2e do M144 (`d="M419.86,92.00 M448.00,36.00"`).
70
+ * 1. **Sparse series** (< `SPARSE_MARKER_MAX` FINITE points) — 1-4 points read as a dramatic
71
+ * cliff; the marker anchors the reader on the real data. M76's rule — which now counts data,
72
+ * not slots, because M144's densification broke the equivalence between the two.
73
+ * 2. **Isolated point** — a finite point surrounded by gaps on both sides becomes a lone `M` in
74
+ * the `path`, and a lone `M` **draws nothing**: zero bounding box, invisible. The data exists,
75
+ * it is correct, and the operator does not see it. Measured in M144's e2e
76
+ * (`d="M419.86,92.00 M448.00,36.00"`).
76
77
  *
77
- * A regra antiga só cobria (1), chaveando no TOTAL de pontos em vez de perguntar se o ponto de fato
78
- * é desenhado — apesar de o comentário dela já declarar a intenção *"a dot keeps real data from
79
- * rendering as an invisible line"*. Depois da densificação, uma série de 30 dias com dois dias
80
- * finitos separados por lacuna cai fora de (1) e desaparece. Chart.js e Plotly marcam o ponto
81
- * isolado exatamente por isto.
78
+ * The old rule covered only (1), keying on the TOTAL number of points instead of asking whether
79
+ * the point is actually drawn — even though its own comment already stated the intent *"a dot
80
+ * keeps real data from rendering as an invisible line"*. After the densification, a 30-day series
81
+ * with two finite days separated by a gap falls outside (1) and disappears. Chart.js and Plotly
82
+ * mark the isolated point for exactly this reason.
82
83
  *
83
- * Fora do JSX de propósito: é a aritmética da decisão, e aritmética isolada é aritmética que a
84
- * mutação alcança.
84
+ * Outside the JSX on purpose: it is the decision's arithmetic, and isolated arithmetic is the kind
85
+ * a mutation reaches.
85
86
  */
86
87
  export declare function markedPoints(points: TrendPoint[]): TrendPoint[];
87
88
  export interface TrendChartProps extends HTMLAttributes<HTMLElement> {
@@ -98,18 +99,18 @@ export interface TrendChartProps extends HTMLAttributes<HTMLElement> {
98
99
  */
99
100
  yMax?: number;
100
101
  /**
101
- * Formata o `x` para o eixo e para a tabela acessivel. **Opcional de proposito.**
102
+ * Formats the `x` for the axis and for the accessible table. **Optional on purpose.**
102
103
  *
103
- * O contrato do `x` e ambiguo — "bucket index or epoch-ms" — e na pratica quase unanime: das seis
104
- * fabricas de serie do consumidor, cinco passam INDICE e uma passa epoch-ms. Inferir por magnitude
105
- * funcionaria e seria a forma exata do defeito que este componente ja pagou (a regra de marcador
106
- * do M76 chaveava em `points.length` em vez de perguntar o que queria saber, e a densificacao do
107
- * M144 a desligou em silencio). Quem sabe o que o `x` significa e o chamador.
104
+ * The `x` contract is ambiguous — "bucket index or epoch-ms" — and in practice nearly unanimous:
105
+ * of the consumer's six series factories, five pass an INDEX and one passes epoch-ms. Inferring by
106
+ * magnitude would work, and would be the exact shape of the defect this component has already paid
107
+ * for (M76's marker rule keyed on `points.length` instead of asking what it wanted to know, and
108
+ * M144's densification switched it off silently). The one who knows what `x` means is the caller.
108
109
  *
109
- * Sem esta prop, nada muda: nenhum rotulo de eixo X e desenhado e a tabela segue numerando.
110
+ * Without this prop nothing changes: no X-axis label is drawn and the table keeps numbering.
110
111
  */
111
112
  xFormatter?: (x: number) => string;
112
- /** Cabecalho da primeira coluna da tabela acessivel. "Point" sobre datas seria desonesto. */
113
+ /** Header of the accessible table's first column. "Point" over dates would be dishonest. */
113
114
  xLabel?: string;
114
115
  }
115
116
  declare const TrendChart: import("react").ForwardRefExoticComponent<TrendChartProps & import("react").RefAttributes<HTMLElement>>;
package/dist/index.js CHANGED
@@ -593,7 +593,7 @@ var buttonVariants = cva(
593
593
  },
594
594
  size: {
595
595
  sm: "h-8 px-3 text-body-sm",
596
- // md: tier ajustável via density (CSS var on :root). See D3 ADR of
596
+ // md: tier adjustable via density (CSS var on :root). See D3 ADR of
597
597
  // faang-density-tightening plan. Default `comfortable` density makes
598
598
  // this 36px (--theo-control-h: 2.25rem). sm and lg stay hardcoded.
599
599
  md: "h-[var(--theo-control-h,2rem)] px-[var(--theo-control-px,0.75rem)] text-body-sm",
@@ -4936,17 +4936,19 @@ function DataTableDefaultBody(props) {
4936
4936
  }
4937
4937
  const totalPages = pagination ? Math.ceil(sortedData.length / effectivePageSize) : 1;
4938
4938
  return (
4939
- // `overflow-x-auto`: uma tabela mais larga que o contêiner ROLA dentro dele, em vez de vazar por
4940
- // cima do resto da página. Medido no consumidor: 11 colunas produziam 1838 px dentro de 1486 px,
4941
- // e a raiz era só `w-full` — então o excesso escapava e desconfigurava a tela inteira. Não era
4942
- // defeito de uma tela: era desta raiz, e alcançava qualquer consumidor com colunas demais.
4939
+ // `overflow-x-auto`: a table wider than its container SCROLLS inside it, instead of spilling
4940
+ // over the rest of the page. Measured in the consumer: 11 columns produced 1838 px inside
4941
+ // 1486 px, and the root was only `w-full` — so the excess escaped and broke the whole screen.
4942
+ // It was not one screen's defect: it was this root's, and it reached any consumer with too
4943
+ // many columns.
4943
4944
  //
4944
- // A variante virtualizada já fazia isto; a não-virtualizada é que tinha ficado para trás.
4945
+ // The virtualised variant already did this; the non-virtualised one had fallen behind.
4945
4946
  //
4946
- // Efeito colateral declarado: com `overflow-x` diferente de `visible`, o CSS computa `overflow-y`
4947
- // como `auto` — logo este `div` vira contexto de rolagem, e um `stickyHeader` passa a grudar nele
4948
- // e não na viewport. Para o cabeçalho grudar de fato, o consumidor precisa dar altura máxima ao
4949
- // contêiner (`className="max-h-[...]"`), que é o que torna a rolagem vertical real.
4947
+ // Declared side effect: with `overflow-x` set to anything other than `visible`, CSS computes
4948
+ // `overflow-y` as `auto` — so this `div` becomes a scroll container, and a `stickyHeader` now
4949
+ // sticks to it rather than to the viewport. For the header to actually stick, the consumer has
4950
+ // to give the container a max height (`className="max-h-[...]"`), which is what makes the
4951
+ // vertical scrolling real.
4950
4952
  /* @__PURE__ */ jsxs("div", { className: cn("w-full overflow-x-auto", className), children: [
4951
4953
  /* @__PURE__ */ jsxs(Table, { children: [
4952
4954
  /* @__PURE__ */ jsx(Table.Header, { className: stickyHeader ? "sticky top-0 z-10 bg-card" : void 0, children: /* @__PURE__ */ jsx(
@@ -5109,14 +5111,14 @@ function niceMax(values) {
5109
5111
  }
5110
5112
  function seriesPath(points, xScale, yScale) {
5111
5113
  const out = [];
5112
- let emTrecho = false;
5114
+ let inStretch = false;
5113
5115
  for (const p of points) {
5114
5116
  if (!Number.isFinite(p.y)) {
5115
- emTrecho = false;
5117
+ inStretch = false;
5116
5118
  continue;
5117
5119
  }
5118
- out.push(`${emTrecho ? "L" : "M"}${xScale(p.x).toFixed(2)},${yScale(p.y).toFixed(2)}`);
5119
- emTrecho = true;
5120
+ out.push(`${inStretch ? "L" : "M"}${xScale(p.x).toFixed(2)},${yScale(p.y).toFixed(2)}`);
5121
+ inStretch = true;
5120
5122
  }
5121
5123
  return out.join(" ");
5122
5124
  }
@@ -5128,17 +5130,19 @@ function xTicks(axisXs, viewW) {
5128
5130
  if (axisXs.length <= 2) return [...axisXs];
5129
5131
  const maxTicks = Math.max(2, Math.floor((viewW - PAD.left - PAD.right) / MIN_TICK_PX2));
5130
5132
  if (axisXs.length <= maxTicks) return [...axisXs];
5131
- const passo = (axisXs.length - 1) / (maxTicks - 1);
5132
- const escolhidos = /* @__PURE__ */ new Set();
5133
- for (let i = 0; i < maxTicks; i++) escolhidos.add(Math.round(i * passo));
5134
- return [...escolhidos].sort((a, b) => a - b).map((i) => axisXs[i]);
5133
+ const step = (axisXs.length - 1) / (maxTicks - 1);
5134
+ const chosen = /* @__PURE__ */ new Set();
5135
+ for (let i = 0; i < maxTicks; i++) chosen.add(Math.round(i * step));
5136
+ return [...chosen].sort((a, b) => a - b).map((i) => axisXs[i]);
5135
5137
  }
5136
5138
  var SPARSE_MARKER_MAX = 5;
5137
5139
  function markedPoints(points) {
5138
- const finito = (p) => Number.isFinite(p?.y);
5139
- const finitos = points.filter(finito);
5140
- if (finitos.length < SPARSE_MARKER_MAX) return finitos;
5141
- return points.filter((p, i) => finito(p) && !finito(points[i - 1]) && !finito(points[i + 1]));
5140
+ const isFinitePoint = (p) => Number.isFinite(p?.y);
5141
+ const finitePoints = points.filter(isFinitePoint);
5142
+ if (finitePoints.length < SPARSE_MARKER_MAX) return finitePoints;
5143
+ return points.filter(
5144
+ (p, i) => isFinitePoint(p) && !isFinitePoint(points[i - 1]) && !isFinitePoint(points[i + 1])
5145
+ );
5142
5146
  }
5143
5147
  var TrendChart = forwardRef(
5144
5148
  ({
@@ -5210,16 +5214,16 @@ var TrendChart = forwardRef(
5210
5214
  "data-slot": "trend-chart-surface",
5211
5215
  className: "relative",
5212
5216
  onMouseMove: (e) => {
5213
- const caixa = e.currentTarget.getBoundingClientRect();
5214
- if (caixa.width === 0 || axisXs.length === 0) return;
5215
- const frac = (e.clientX - caixa.left) / caixa.width;
5216
- const alvo = xMin + frac * (xMax - xMin);
5217
- let melhor = 0;
5217
+ const box = e.currentTarget.getBoundingClientRect();
5218
+ if (box.width === 0 || axisXs.length === 0) return;
5219
+ const frac = (e.clientX - box.left) / box.width;
5220
+ const target = xMin + frac * (xMax - xMin);
5221
+ let best = 0;
5218
5222
  for (let i = 1; i < axisXs.length; i++) {
5219
- if (Math.abs(axisXs[i] - alvo) < Math.abs(axisXs[melhor] - alvo))
5220
- melhor = i;
5223
+ if (Math.abs(axisXs[i] - target) < Math.abs(axisXs[best] - target))
5224
+ best = i;
5221
5225
  }
5222
- setHoverIdx(melhor);
5226
+ setHoverIdx(best);
5223
5227
  },
5224
5228
  onMouseLeave: () => setHoverIdx(null),
5225
5229
  children: [
package/package.json CHANGED
@@ -1,10 +1,33 @@
1
1
  {
2
2
  "name": "@usetheo/ui",
3
- "version": "0.35.1",
4
- "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be",
3
+ "version": "0.35.3",
5
4
  "type": "module",
6
5
  "license": "Apache-2.0",
7
- "description": "Violet Forge design system \u2014 generic React components + primitives (the non-AI foundation of @theokit/ui).",
6
+ "description": "Violet Forge design system — generic React components + primitives (the non-AI foundation of @theokit/ui).",
7
+ "keywords": [
8
+ "react",
9
+ "design-system",
10
+ "components",
11
+ "ui",
12
+ "tailwind",
13
+ "radix",
14
+ "primitives",
15
+ "dashboard",
16
+ "theo",
17
+ "violet-forge"
18
+ ],
19
+ "homepage": "https://github.com/usetheodev/usetheo-ui#readme",
20
+ "bugs": {
21
+ "url": "https://github.com/usetheodev/usetheo-ui/issues"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/usetheodev/usetheo-ui.git"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public",
29
+ "provenance": true
30
+ },
8
31
  "sideEffects": false,
9
32
  "files": [
10
33
  "dist"
@@ -22,19 +45,6 @@
22
45
  "engines": {
23
46
  "node": ">=20"
24
47
  },
25
- "scripts": {
26
- "dev": "ladle serve",
27
- "typecheck": "tsc --noEmit",
28
- "lint": "biome check src",
29
- "lint:ci": "biome check src",
30
- "format:check": "biome format src",
31
- "test": "vitest run",
32
- "test:run": "vitest run",
33
- "build": "tsup && tsc -p tsconfig.dts.json",
34
- "prepublishOnly": "npm run build",
35
- "registry:build": "tsx scripts/build-registry.ts",
36
- "registry:validate": "tsx scripts/validate-registry.ts"
37
- },
38
48
  "peerDependencies": {
39
49
  "react": ">=18",
40
50
  "react-dom": ">=18"
@@ -64,6 +74,7 @@
64
74
  },
65
75
  "devDependencies": {
66
76
  "@biomejs/biome": "^1.9.4",
77
+ "@changesets/cli": "^2.31.1",
67
78
  "@ladle/react": "^4.1.2",
68
79
  "@tailwindcss/vite": "^4.3.2",
69
80
  "@testing-library/jest-dom": "^6.6.3",
@@ -84,5 +95,21 @@
84
95
  "vitest": "^4.1.8",
85
96
  "vitest-axe": "^0.1.0"
86
97
  },
87
- "_provenance": "seeded from theo-ui @ 2b46eca (2026-07-03, pivot M-B)"
88
- }
98
+ "_provenance": "seeded from theo-ui @ 2b46eca (2026-07-03, pivot M-B)",
99
+ "scripts": {
100
+ "dev": "ladle serve",
101
+ "typecheck": "tsc --noEmit",
102
+ "lint": "biome check src",
103
+ "lint:ci": "biome check src",
104
+ "format:check": "biome format src",
105
+ "test": "vitest run",
106
+ "test:run": "vitest run",
107
+ "build": "tsup && tsc -p tsconfig.dts.json",
108
+ "registry:build": "tsx scripts/build-registry.ts",
109
+ "registry:validate": "tsx scripts/validate-registry.ts",
110
+ "changeset": "changeset",
111
+ "version-packages": "changeset version && pnpm run sync:readme",
112
+ "release": "pnpm run build && changeset publish",
113
+ "sync:readme": "tsx scripts/sync-readme.ts"
114
+ }
115
+ }