@timbal-ai/timbal-react 1.6.0 → 1.6.1

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/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ All notable changes to `@timbal-ai/timbal-react` are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.6.1] — 2026-06-27
8
+
9
+ ### Fixed
10
+
11
+ - **recharts charts no longer white-screen under React 19** — recharts 3.6+ stores React elements inside a Redux-Toolkit/`immer` store, and `immer` **11.0.0** froze React 19's Fiber internals, so chart routes crashed with `Cannot assign to read only property 'lanes'`. The crash was always a transitive-dependency mismatch (a stale lockfile pinning `immer@11.0.0`), not a problem with the chart components — `LineAreaChart`, `PieChart`, `RadialChart`, and `RadarChart` are unchanged. The package now forces `immer` to `>=11.0.1` via `overrides`/`resolutions`, deduping to a single safe copy.
12
+
13
+ ### Changed
14
+
15
+ - **recharts pinned to an exact tested version (`3.8.1`)** so the recharts/Redux-Toolkit/`immer` matrix the package validates against can't float into an untested 3.x range.
16
+ - **`APP_KIT_AGENT_INSTRUCTIONS` charts section** now tells codegen agents that the React 19 chart crash is a dependency override (`"overrides": { "immer": ">=11.0.1" }`), **not** a code change — and to keep using the chart components instead of hand-rolling SVG/CSS charts as a workaround.
17
+
18
+ ### Tooling
19
+
20
+ - **`check:deps` preflight guard** (`scripts/check-immer.mjs`, wired into CI) fails the build if any installed `immer` falls in the broken `[11.0.0, 11.0.1)` window, turning a silent runtime white-screen into a loud, actionable error. Example apps (`examples/app-kit`, `examples/mock-ui`) carry the same `immer` override.
21
+
7
22
  ## [1.6.0] — 2026-06-26
8
23
 
9
24
  ### Changed
package/README.md CHANGED
@@ -40,6 +40,14 @@ npm install react react-dom react-is @assistant-ui/react @timbal-ai/timbal-sdk
40
40
  ```
41
41
 
42
42
  > The app-kit charts are built on [recharts](https://recharts.org) (installed automatically). `react-is` is a recharts peer and **must match your React version** — install it explicitly if your package manager doesn't hoist peers.
43
+ >
44
+ > **Required for React 19:** pin `immer` to ≥ 11.0.1 in your app. recharts stores React elements in a Redux-Toolkit/immer store, and immer **11.0.0** freezes React 19's Fiber internals — charts crash with `Cannot assign to read only property 'lanes'` (a blank route). Add an override so a stale lockfile can't reintroduce it:
45
+ >
46
+ > ```json
47
+ > { "overrides": { "immer": ">=11.0.1" } }
48
+ > ```
49
+ >
50
+ > Yarn uses `"resolutions"`. Fresh installs already resolve a safe immer; the override just makes it durable.
43
51
 
44
52
  ### Tailwind setup
45
53
 
package/dist/app.cjs CHANGED
@@ -318,7 +318,11 @@ var HOUSE_RULES = [
318
318
  {
319
319
  id: "compose-from-blocks",
320
320
  rule: "Build from premade blocks (MetricRow, MetricChartCard, DataTable, IntegrationCard). Drop to raw primitives only when no block fits.",
321
- why: "Slop appears the moment generation falls below the curated block layer."
321
+ why: "Slop appears the moment generation falls below the curated block layer.",
322
+ // "Should have used a block" is a judgement about absence, not a textual
323
+ // pattern — no high-precision deterministic check exists, so this stays
324
+ // prompt-only rather than risk false-positives blocking valid UIs.
325
+ enforcement: "prompt-only"
322
326
  },
323
327
  {
324
328
  id: "use-kit-controls",
@@ -399,7 +403,7 @@ The content region is a **padded scroll area** by default \u2014 great for stack
399
403
  - Give the filling child **\`min-h-0 flex-1\`** (or \`h-full\`) so its own scroll/footer resolves \u2014 e.g. \`<TimbalChat className="min-h-0 flex-1" />\`, or a two-pane row where each pane is \`min-h-0 overflow-y-auto\`.
400
404
 
401
405
  \`\`\`tsx
402
- <AppShell contentFill topbar={<div className="flex justify-end p-4"><ModeToggle /></div>}>
406
+ <AppShell contentFill> {/* no global topbar / theme switch */}
403
407
  <Page fill> {/* headerless: omit title */}
404
408
  <TimbalChat workforceId="\u2026" className="min-h-0 flex-1" />
405
409
  </Page>
@@ -503,6 +507,8 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
503
507
 
504
508
  Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipContent\` chrome (see \`src/ui/chart.tsx\`). Series colors default to \`--chart-1..6\`; override those CSS tokens to rebrand every chart.
505
509
 
510
+ > **React 19 requirement \u2014 do not hand-roll SVG charts to work around this.** recharts under React 19 crashes (\`Cannot assign to read only property 'lanes'\`, blank route) when \`immer\` resolves to **11.0.0**. The fix is a dependency override in the app's \`package.json\` \u2014 \`"overrides": { "immer": ">=11.0.1" }\` (Yarn: \`"resolutions"\`) \u2014 **not** a code change. Always keep using \`LineAreaChart\` / \`PieChart\` / \`ChartPanel\`; never replace them with raw SVG/CSS charts.
511
+
506
512
  | Component | Use for |
507
513
  |-----------|---------|
508
514
  | \`LineAreaChart\` | Cartesian engine (shadcn-style chrome). Bar fills use theme gradients automatically. Props: \`data\`, \`xKey\`, \`series: [{ dataKey, label?, color? }]\`, \`variant\` (\`area\`\\|\`line\`\\|\`bar\`), \`orientation\` (\`horizontal\` for horizontal bars), \`stacked\`, \`curve\`, \`dots\`, \`gridLines\`, \`tooltipIndicator\`, \`layout\` (\`flush\` \u2014 hides axes by default; category + values on hover tooltip), \`showXAxis\` / \`showYAxis\` to opt back in, \`clipTicks\` (truncates long axis labels when axes are on), \`height\`, \`showLegend\`, \`formatX\`, \`formatValue\`, \`ariaLabel\`. |
@@ -683,6 +689,8 @@ var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
683
689
  var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
684
690
  var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
685
691
  var COLORED_HOVER_RE = /\bhover:(?:bg|from|to|via)-(?:primary|destructive|success|warn|danger|blue|emerald|green|amber|red|indigo|violet|purple|pink|rose|sky|cyan|teal|lime|yellow|orange|fuchsia)\b/;
692
+ var TREND_CONTEXT_RE = /\b(?:trend|delta|TrendingUp|TrendingDown|ArrowUp|ArrowDown|ArrowUpRight|ArrowDownRight|MoveUp|MoveDown)\b|[+\-]\d+(?:\.\d+)?\s*%/;
693
+ var TREND_COLOR_RE = /\b(?:text|bg|border)-(?:success|destructive|emerald|green|lime|teal|red|rose|orange|amber)(?:-\d{2,3})?(?:\/\d{1,3})?\b/;
686
694
  var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
687
695
  function stripVariants(util) {
688
696
  return util.replace(/^(?:[a-z-]+:)*/, "");
@@ -726,6 +734,16 @@ function lintGeneratedUi(source, options = {}) {
726
734
  if (cardMatch) {
727
735
  const isSelfClosing = /\/>/.test(line) && line.indexOf(cardMatch[0]) < line.indexOf("/>");
728
736
  if (!isSelfClosing) {
737
+ if (openCards.length > 0) {
738
+ const parentCard = openCards[openCards.length - 1];
739
+ findings.push({
740
+ rule: "no-card-in-card",
741
+ severity: "warn",
742
+ line: lineNo,
743
+ message: `Card inside card. A <${cardMatch[1]}> is nested inside the <${parentCard.type}> opened on L${parentCard.line}. Double borders/shadows add no information \u2014 group with spacing or a <Section> instead.`,
744
+ snippet: line.trim().slice(0, 120)
745
+ });
746
+ }
729
747
  openCards.push({ type: cardMatch[1], line: lineNo });
730
748
  }
731
749
  }
@@ -798,6 +816,15 @@ function lintGeneratedUi(source, options = {}) {
798
816
  snippet: line.trim().slice(0, 120)
799
817
  });
800
818
  }
819
+ if (TREND_CONTEXT_RE.test(line) && TREND_COLOR_RE.test(line)) {
820
+ findings.push({
821
+ rule: "neutral-trend",
822
+ severity: "warn",
823
+ line: lineNo,
824
+ message: "Colored trend indicator. House style: don't tint deltas green/red on every metric \u2014 show a trend only when the change is the point, and keep it muted (text-muted-foreground).",
825
+ snippet: line.trim().slice(0, 120)
826
+ });
827
+ }
801
828
  if (BOLD_VALUE_RE.test(line)) {
802
829
  findings.push({
803
830
  rule: "bold-metric",
package/dist/app.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AppDensity, a as AppDensityClassKey, S as StatusBadgeTone } from './chart-artifact-VAqgH-My.cjs';
2
- export { b as APP_DENSITY_CHART_HEIGHT, c as APP_DENSITY_CLASSES, d as APP_KIT_AGENT_INSTRUCTIONS, e as AppChatPanel, f as AppChatPanelProps, g as AppConfirmDialog, h as AppConfirmDialogProps, i as AppCopilotContextValue, j as AppCopilotProvider, k as AppCopilotProviderProps, l as AppPageWidth, m as AppShell, n as AppShellChatControls, o as AppShellChatTrigger, p as AppShellChatTriggerProps, q as AppShellNavControls, r as AppShellProps, s as AppShellSidebarTrigger, t as AppShellSidebarTriggerProps, B as BreadcrumbEntry, u as BreadcrumbItem, v as Breadcrumbs, w as BreadcrumbsProps, C as CHART_PALETTE, x as COLOR_UTILITY_PREFIXES, y as ChartArtifactView, z as ChartLayout, D as ChartMargin, E as ChartPanel, F as ChartPanelProps, G as ChartSeries, H as ChartTooltipIndicator, I as ChartVariant, J as ConnectionRow, K as ConnectionRowList, L as ConnectionRowListProps, M as ConnectionRowProps, N as DangerZone, O as DangerZoneAction, P as DangerZoneActionProps, Q as DangerZoneProps, R as DataTable, T as DataTableColumn, U as DataTableProps, V as DataTableSort, W as DataTableSortDirection, X as DescriptionItem, Y as DescriptionList, Z as DescriptionListProps, _ as EmptyState, $ as EmptyStateProps, a0 as ExpandableSection, a1 as ExpandableSectionProps, a2 as Field, a3 as FieldInput, a4 as FieldInputProps, a5 as FieldProps, a6 as FieldRow, a7 as FieldRowProps, a8 as FieldSelect, a9 as FieldSelectProps, aa as FieldSwitch, ab as FieldSwitchProps, ac as FieldTextarea, ad as FieldTextareaProps, ae as FilterBar, af as FilterBarProps, ag as FilterDatePreset, ah as FilterDateRangeValue, ai as FilterDropdown, aj as FilterDropdownProps, ak as FilterField, al as FilterFieldDef, am as FilterFieldProps, an as FilterFieldType, ao as FilterNumericOperatorOption, ap as FilterNumericValue, aq as FilterSelectOption, ar as FilterValue, as as FilterValues, at as FloatingUnsavedChangesBar, au as FloatingUnsavedChangesBarProps, av as FormSection, aw as FormSectionProps, ax as HOUSE_RULES, ay as HouseRule, az as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aA as InfoCard, aB as InfoCardProps, aC as InfoCardTone, aD as IntegrationCard, aE as IntegrationCardProps, aF as IntegrationCardStatus, aG as IntegrationsEmptyState, aH as IntegrationsEmptyStateProps, aI as LineAreaChart, aJ as LineAreaChartProps, aK as LintFinding, aL as LintOptions, aM as LintResult, aN as LintSeverity, aO as MetricChartCard, aP as MetricChartCardProps, aQ as MetricChartMetric, aR as MetricRow, aS as MetricRowItem, aT as MetricRowProps, aU as MetricTile, aV as MetricTileProps, aW as NumericOperator, aX as Page, aY as PageHeader, aZ as PageHeaderProps, a_ as PageProps, a$ as PieChart, b0 as PieChartProps, b1 as PlanBadge, b2 as PlanBadgeProps, b3 as PlanBadgeTone, b4 as RESERVED_GRADIENT_TOKENS, b5 as RadarChart, b6 as RadarChartProps, b7 as RadialChart, b8 as RadialChartProps, b9 as ResourceCard, ba as ResourceCardProps, bb as ReviewResult, bc as SEMANTIC_COLOR_TOKENS, bd as SLOP_BUDGETS, be as SearchInput, bf as SearchInputProps, bg as Section, bh as SectionProps, bi as SemanticColorToken, bj as SettingsSection, bk as SettingsSectionHeader, bl as SettingsSectionHeaderProps, bm as SettingsSectionProps, bn as Sparkline, bo as SparklineProps, bp as Stack, bq as StackAlign, br as StackGap, bs as StackJustify, bt as StackProps, bu as StatTile, bv as StatTileProps, bw as StatTileTone, bx as StatusBadge, by as StatusBadgeProps, bz as StatusDot, bA as StatusDotProps, bB as StatusDotTone, bC as SubNav, bD as SubNavItem, bE as SubNavProps, bF as SurfaceCard, bG as SurfaceCardProps, bH as SurfaceCardTone, bI as SurfaceCardVariant, bJ as TAILWIND_PALETTE_COLORS, bK as THEME_AGENT_INSTRUCTIONS, bL as TIMBAL_THEME_PRESETS, bM as ThemeShadow, bN as ThemeToCssOptions, bO as ThemeTokenMap, bP as TimbalThemeIntent, bQ as TimbalThemePreset, bR as TimbalThemePresetId, bS as TimbalThemeStyle, bT as TimbalThemeStyleProps, bU as TimbalThemeTokens, bV as TimbalThemeTypography, bW as UI_REVIEW_AGENT_INSTRUCTIONS, bX as UseLiveQueryOptions, bY as UseLiveQueryResult, bZ as appDensityClass, b_ as appFilterBarClass, b$ as appPageColumnClass, c0 as appSearchInputClass, c1 as appShellInsetTopClass, c2 as appShellTopbarInsetClass, c3 as appStatTileClass, c4 as appSurfaceCardClass, c5 as applyThemePreset, c6 as applyTimbalTheme, c7 as clearTimbalTheme, c8 as connectionRowListClass, c9 as createTimbalTheme, ca as ensureThemeFontLink, cb as flushBarCategoryGap, cc as flushLineAreaEdgeToEdge, cd as formatLintReport, ce as getStoredThemePreset, cf as getThemePreset, cg as lintGeneratedUi, ch as resolveChartMargin, ci as resolveTooltipCategory, cj as reviewGeneratedUi, ck as themeToCss, cl as useAppCopilotContext, cm as useAppShellChat, cn as useAppShellNav, co as useInterval, cp as useLiveQuery } from './chart-artifact-VAqgH-My.cjs';
1
+ import { A as AppDensity, a as AppDensityClassKey, S as StatusBadgeTone } from './chart-artifact-Dpt4t5sf.cjs';
2
+ export { b as APP_DENSITY_CHART_HEIGHT, c as APP_DENSITY_CLASSES, d as APP_KIT_AGENT_INSTRUCTIONS, e as AppChatPanel, f as AppChatPanelProps, g as AppConfirmDialog, h as AppConfirmDialogProps, i as AppCopilotContextValue, j as AppCopilotProvider, k as AppCopilotProviderProps, l as AppPageWidth, m as AppShell, n as AppShellChatControls, o as AppShellChatTrigger, p as AppShellChatTriggerProps, q as AppShellNavControls, r as AppShellProps, s as AppShellSidebarTrigger, t as AppShellSidebarTriggerProps, B as BreadcrumbEntry, u as BreadcrumbItem, v as Breadcrumbs, w as BreadcrumbsProps, C as CHART_PALETTE, x as COLOR_UTILITY_PREFIXES, y as ChartArtifactView, z as ChartLayout, D as ChartMargin, E as ChartPanel, F as ChartPanelProps, G as ChartSeries, H as ChartTooltipIndicator, I as ChartVariant, J as ConnectionRow, K as ConnectionRowList, L as ConnectionRowListProps, M as ConnectionRowProps, N as DangerZone, O as DangerZoneAction, P as DangerZoneActionProps, Q as DangerZoneProps, R as DataTable, T as DataTableColumn, U as DataTableProps, V as DataTableSort, W as DataTableSortDirection, X as DescriptionItem, Y as DescriptionList, Z as DescriptionListProps, _ as EmptyState, $ as EmptyStateProps, a0 as ExpandableSection, a1 as ExpandableSectionProps, a2 as Field, a3 as FieldInput, a4 as FieldInputProps, a5 as FieldProps, a6 as FieldRow, a7 as FieldRowProps, a8 as FieldSelect, a9 as FieldSelectProps, aa as FieldSwitch, ab as FieldSwitchProps, ac as FieldTextarea, ad as FieldTextareaProps, ae as FilterBar, af as FilterBarProps, ag as FilterDatePreset, ah as FilterDateRangeValue, ai as FilterDropdown, aj as FilterDropdownProps, ak as FilterField, al as FilterFieldDef, am as FilterFieldProps, an as FilterFieldType, ao as FilterNumericOperatorOption, ap as FilterNumericValue, aq as FilterSelectOption, ar as FilterValue, as as FilterValues, at as FloatingUnsavedChangesBar, au as FloatingUnsavedChangesBarProps, av as FormSection, aw as FormSectionProps, ax as HOUSE_RULES, ay as HouseRule, az as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aA as InfoCard, aB as InfoCardProps, aC as InfoCardTone, aD as IntegrationCard, aE as IntegrationCardProps, aF as IntegrationCardStatus, aG as IntegrationsEmptyState, aH as IntegrationsEmptyStateProps, aI as LineAreaChart, aJ as LineAreaChartProps, aK as LintFinding, aL as LintOptions, aM as LintResult, aN as LintSeverity, aO as MetricChartCard, aP as MetricChartCardProps, aQ as MetricChartMetric, aR as MetricRow, aS as MetricRowItem, aT as MetricRowProps, aU as MetricTile, aV as MetricTileProps, aW as NumericOperator, aX as Page, aY as PageHeader, aZ as PageHeaderProps, a_ as PageProps, a$ as PieChart, b0 as PieChartProps, b1 as PlanBadge, b2 as PlanBadgeProps, b3 as PlanBadgeTone, b4 as RESERVED_GRADIENT_TOKENS, b5 as RadarChart, b6 as RadarChartProps, b7 as RadialChart, b8 as RadialChartProps, b9 as ResourceCard, ba as ResourceCardProps, bb as ReviewResult, bc as SEMANTIC_COLOR_TOKENS, bd as SLOP_BUDGETS, be as SearchInput, bf as SearchInputProps, bg as Section, bh as SectionProps, bi as SemanticColorToken, bj as SettingsSection, bk as SettingsSectionHeader, bl as SettingsSectionHeaderProps, bm as SettingsSectionProps, bn as Sparkline, bo as SparklineProps, bp as Stack, bq as StackAlign, br as StackGap, bs as StackJustify, bt as StackProps, bu as StatTile, bv as StatTileProps, bw as StatTileTone, bx as StatusBadge, by as StatusBadgeProps, bz as StatusDot, bA as StatusDotProps, bB as StatusDotTone, bC as SubNav, bD as SubNavItem, bE as SubNavProps, bF as SurfaceCard, bG as SurfaceCardProps, bH as SurfaceCardTone, bI as SurfaceCardVariant, bJ as TAILWIND_PALETTE_COLORS, bK as THEME_AGENT_INSTRUCTIONS, bL as TIMBAL_THEME_PRESETS, bM as ThemeShadow, bN as ThemeToCssOptions, bO as ThemeTokenMap, bP as TimbalThemeIntent, bQ as TimbalThemePreset, bR as TimbalThemePresetId, bS as TimbalThemeStyle, bT as TimbalThemeStyleProps, bU as TimbalThemeTokens, bV as TimbalThemeTypography, bW as UI_REVIEW_AGENT_INSTRUCTIONS, bX as UseLiveQueryOptions, bY as UseLiveQueryResult, bZ as appDensityClass, b_ as appFilterBarClass, b$ as appPageColumnClass, c0 as appSearchInputClass, c1 as appShellInsetTopClass, c2 as appShellTopbarInsetClass, c3 as appStatTileClass, c4 as appSurfaceCardClass, c5 as applyThemePreset, c6 as applyTimbalTheme, c7 as clearTimbalTheme, c8 as connectionRowListClass, c9 as createTimbalTheme, ca as ensureThemeFontLink, cb as flushBarCategoryGap, cc as flushLineAreaEdgeToEdge, cd as formatLintReport, ce as getStoredThemePreset, cf as getThemePreset, cg as lintGeneratedUi, ch as resolveChartMargin, ci as resolveTooltipCategory, cj as reviewGeneratedUi, ck as themeToCss, cl as useAppCopilotContext, cm as useAppShellChat, cn as useAppShellNav, co as useInterval, cp as useLiveQuery } from './chart-artifact-Dpt4t5sf.cjs';
3
3
  import { FC, ReactNode } from 'react';
4
4
  export { a as Avatar, b as AvatarFallback, c as AvatarImage, D as AvatarVariant, B as Banner, d as BannerProps, h as Button, i as ButtonColor, K as Kanban, j as KanbanCardData, k as KanbanCardVariant, l as KanbanColumnData, m as KanbanDensity, n as KanbanDragHandleProps, o as KanbanLocation, p as KanbanMoveEvent, q as KanbanProps, r as KanbanRenderCardContext, s as KanbanTone, T as Timeline, t as TimelineItem, u as TimelineProps, U as UntitledButton, w as UntitledButtonColor, x as UntitledButtonProps, y as UntitledButtonSize } from './kanban-FFBeaZPS.cjs';
5
5
  export { B as ChartArtifact, D as ChartSeriesConfig, p as ThreadVariant, s as TimbalChat, t as TimbalChatProps } from './chat-DDsp-Vzz.cjs';
package/dist/app.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AppDensity, a as AppDensityClassKey, S as StatusBadgeTone } from './chart-artifact-C2pZQsaP.js';
2
- export { b as APP_DENSITY_CHART_HEIGHT, c as APP_DENSITY_CLASSES, d as APP_KIT_AGENT_INSTRUCTIONS, e as AppChatPanel, f as AppChatPanelProps, g as AppConfirmDialog, h as AppConfirmDialogProps, i as AppCopilotContextValue, j as AppCopilotProvider, k as AppCopilotProviderProps, l as AppPageWidth, m as AppShell, n as AppShellChatControls, o as AppShellChatTrigger, p as AppShellChatTriggerProps, q as AppShellNavControls, r as AppShellProps, s as AppShellSidebarTrigger, t as AppShellSidebarTriggerProps, B as BreadcrumbEntry, u as BreadcrumbItem, v as Breadcrumbs, w as BreadcrumbsProps, C as CHART_PALETTE, x as COLOR_UTILITY_PREFIXES, y as ChartArtifactView, z as ChartLayout, D as ChartMargin, E as ChartPanel, F as ChartPanelProps, G as ChartSeries, H as ChartTooltipIndicator, I as ChartVariant, J as ConnectionRow, K as ConnectionRowList, L as ConnectionRowListProps, M as ConnectionRowProps, N as DangerZone, O as DangerZoneAction, P as DangerZoneActionProps, Q as DangerZoneProps, R as DataTable, T as DataTableColumn, U as DataTableProps, V as DataTableSort, W as DataTableSortDirection, X as DescriptionItem, Y as DescriptionList, Z as DescriptionListProps, _ as EmptyState, $ as EmptyStateProps, a0 as ExpandableSection, a1 as ExpandableSectionProps, a2 as Field, a3 as FieldInput, a4 as FieldInputProps, a5 as FieldProps, a6 as FieldRow, a7 as FieldRowProps, a8 as FieldSelect, a9 as FieldSelectProps, aa as FieldSwitch, ab as FieldSwitchProps, ac as FieldTextarea, ad as FieldTextareaProps, ae as FilterBar, af as FilterBarProps, ag as FilterDatePreset, ah as FilterDateRangeValue, ai as FilterDropdown, aj as FilterDropdownProps, ak as FilterField, al as FilterFieldDef, am as FilterFieldProps, an as FilterFieldType, ao as FilterNumericOperatorOption, ap as FilterNumericValue, aq as FilterSelectOption, ar as FilterValue, as as FilterValues, at as FloatingUnsavedChangesBar, au as FloatingUnsavedChangesBarProps, av as FormSection, aw as FormSectionProps, ax as HOUSE_RULES, ay as HouseRule, az as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aA as InfoCard, aB as InfoCardProps, aC as InfoCardTone, aD as IntegrationCard, aE as IntegrationCardProps, aF as IntegrationCardStatus, aG as IntegrationsEmptyState, aH as IntegrationsEmptyStateProps, aI as LineAreaChart, aJ as LineAreaChartProps, aK as LintFinding, aL as LintOptions, aM as LintResult, aN as LintSeverity, aO as MetricChartCard, aP as MetricChartCardProps, aQ as MetricChartMetric, aR as MetricRow, aS as MetricRowItem, aT as MetricRowProps, aU as MetricTile, aV as MetricTileProps, aW as NumericOperator, aX as Page, aY as PageHeader, aZ as PageHeaderProps, a_ as PageProps, a$ as PieChart, b0 as PieChartProps, b1 as PlanBadge, b2 as PlanBadgeProps, b3 as PlanBadgeTone, b4 as RESERVED_GRADIENT_TOKENS, b5 as RadarChart, b6 as RadarChartProps, b7 as RadialChart, b8 as RadialChartProps, b9 as ResourceCard, ba as ResourceCardProps, bb as ReviewResult, bc as SEMANTIC_COLOR_TOKENS, bd as SLOP_BUDGETS, be as SearchInput, bf as SearchInputProps, bg as Section, bh as SectionProps, bi as SemanticColorToken, bj as SettingsSection, bk as SettingsSectionHeader, bl as SettingsSectionHeaderProps, bm as SettingsSectionProps, bn as Sparkline, bo as SparklineProps, bp as Stack, bq as StackAlign, br as StackGap, bs as StackJustify, bt as StackProps, bu as StatTile, bv as StatTileProps, bw as StatTileTone, bx as StatusBadge, by as StatusBadgeProps, bz as StatusDot, bA as StatusDotProps, bB as StatusDotTone, bC as SubNav, bD as SubNavItem, bE as SubNavProps, bF as SurfaceCard, bG as SurfaceCardProps, bH as SurfaceCardTone, bI as SurfaceCardVariant, bJ as TAILWIND_PALETTE_COLORS, bK as THEME_AGENT_INSTRUCTIONS, bL as TIMBAL_THEME_PRESETS, bM as ThemeShadow, bN as ThemeToCssOptions, bO as ThemeTokenMap, bP as TimbalThemeIntent, bQ as TimbalThemePreset, bR as TimbalThemePresetId, bS as TimbalThemeStyle, bT as TimbalThemeStyleProps, bU as TimbalThemeTokens, bV as TimbalThemeTypography, bW as UI_REVIEW_AGENT_INSTRUCTIONS, bX as UseLiveQueryOptions, bY as UseLiveQueryResult, bZ as appDensityClass, b_ as appFilterBarClass, b$ as appPageColumnClass, c0 as appSearchInputClass, c1 as appShellInsetTopClass, c2 as appShellTopbarInsetClass, c3 as appStatTileClass, c4 as appSurfaceCardClass, c5 as applyThemePreset, c6 as applyTimbalTheme, c7 as clearTimbalTheme, c8 as connectionRowListClass, c9 as createTimbalTheme, ca as ensureThemeFontLink, cb as flushBarCategoryGap, cc as flushLineAreaEdgeToEdge, cd as formatLintReport, ce as getStoredThemePreset, cf as getThemePreset, cg as lintGeneratedUi, ch as resolveChartMargin, ci as resolveTooltipCategory, cj as reviewGeneratedUi, ck as themeToCss, cl as useAppCopilotContext, cm as useAppShellChat, cn as useAppShellNav, co as useInterval, cp as useLiveQuery } from './chart-artifact-C2pZQsaP.js';
1
+ import { A as AppDensity, a as AppDensityClassKey, S as StatusBadgeTone } from './chart-artifact-BYl5C-dk.js';
2
+ export { b as APP_DENSITY_CHART_HEIGHT, c as APP_DENSITY_CLASSES, d as APP_KIT_AGENT_INSTRUCTIONS, e as AppChatPanel, f as AppChatPanelProps, g as AppConfirmDialog, h as AppConfirmDialogProps, i as AppCopilotContextValue, j as AppCopilotProvider, k as AppCopilotProviderProps, l as AppPageWidth, m as AppShell, n as AppShellChatControls, o as AppShellChatTrigger, p as AppShellChatTriggerProps, q as AppShellNavControls, r as AppShellProps, s as AppShellSidebarTrigger, t as AppShellSidebarTriggerProps, B as BreadcrumbEntry, u as BreadcrumbItem, v as Breadcrumbs, w as BreadcrumbsProps, C as CHART_PALETTE, x as COLOR_UTILITY_PREFIXES, y as ChartArtifactView, z as ChartLayout, D as ChartMargin, E as ChartPanel, F as ChartPanelProps, G as ChartSeries, H as ChartTooltipIndicator, I as ChartVariant, J as ConnectionRow, K as ConnectionRowList, L as ConnectionRowListProps, M as ConnectionRowProps, N as DangerZone, O as DangerZoneAction, P as DangerZoneActionProps, Q as DangerZoneProps, R as DataTable, T as DataTableColumn, U as DataTableProps, V as DataTableSort, W as DataTableSortDirection, X as DescriptionItem, Y as DescriptionList, Z as DescriptionListProps, _ as EmptyState, $ as EmptyStateProps, a0 as ExpandableSection, a1 as ExpandableSectionProps, a2 as Field, a3 as FieldInput, a4 as FieldInputProps, a5 as FieldProps, a6 as FieldRow, a7 as FieldRowProps, a8 as FieldSelect, a9 as FieldSelectProps, aa as FieldSwitch, ab as FieldSwitchProps, ac as FieldTextarea, ad as FieldTextareaProps, ae as FilterBar, af as FilterBarProps, ag as FilterDatePreset, ah as FilterDateRangeValue, ai as FilterDropdown, aj as FilterDropdownProps, ak as FilterField, al as FilterFieldDef, am as FilterFieldProps, an as FilterFieldType, ao as FilterNumericOperatorOption, ap as FilterNumericValue, aq as FilterSelectOption, ar as FilterValue, as as FilterValues, at as FloatingUnsavedChangesBar, au as FloatingUnsavedChangesBarProps, av as FormSection, aw as FormSectionProps, ax as HOUSE_RULES, ay as HouseRule, az as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aA as InfoCard, aB as InfoCardProps, aC as InfoCardTone, aD as IntegrationCard, aE as IntegrationCardProps, aF as IntegrationCardStatus, aG as IntegrationsEmptyState, aH as IntegrationsEmptyStateProps, aI as LineAreaChart, aJ as LineAreaChartProps, aK as LintFinding, aL as LintOptions, aM as LintResult, aN as LintSeverity, aO as MetricChartCard, aP as MetricChartCardProps, aQ as MetricChartMetric, aR as MetricRow, aS as MetricRowItem, aT as MetricRowProps, aU as MetricTile, aV as MetricTileProps, aW as NumericOperator, aX as Page, aY as PageHeader, aZ as PageHeaderProps, a_ as PageProps, a$ as PieChart, b0 as PieChartProps, b1 as PlanBadge, b2 as PlanBadgeProps, b3 as PlanBadgeTone, b4 as RESERVED_GRADIENT_TOKENS, b5 as RadarChart, b6 as RadarChartProps, b7 as RadialChart, b8 as RadialChartProps, b9 as ResourceCard, ba as ResourceCardProps, bb as ReviewResult, bc as SEMANTIC_COLOR_TOKENS, bd as SLOP_BUDGETS, be as SearchInput, bf as SearchInputProps, bg as Section, bh as SectionProps, bi as SemanticColorToken, bj as SettingsSection, bk as SettingsSectionHeader, bl as SettingsSectionHeaderProps, bm as SettingsSectionProps, bn as Sparkline, bo as SparklineProps, bp as Stack, bq as StackAlign, br as StackGap, bs as StackJustify, bt as StackProps, bu as StatTile, bv as StatTileProps, bw as StatTileTone, bx as StatusBadge, by as StatusBadgeProps, bz as StatusDot, bA as StatusDotProps, bB as StatusDotTone, bC as SubNav, bD as SubNavItem, bE as SubNavProps, bF as SurfaceCard, bG as SurfaceCardProps, bH as SurfaceCardTone, bI as SurfaceCardVariant, bJ as TAILWIND_PALETTE_COLORS, bK as THEME_AGENT_INSTRUCTIONS, bL as TIMBAL_THEME_PRESETS, bM as ThemeShadow, bN as ThemeToCssOptions, bO as ThemeTokenMap, bP as TimbalThemeIntent, bQ as TimbalThemePreset, bR as TimbalThemePresetId, bS as TimbalThemeStyle, bT as TimbalThemeStyleProps, bU as TimbalThemeTokens, bV as TimbalThemeTypography, bW as UI_REVIEW_AGENT_INSTRUCTIONS, bX as UseLiveQueryOptions, bY as UseLiveQueryResult, bZ as appDensityClass, b_ as appFilterBarClass, b$ as appPageColumnClass, c0 as appSearchInputClass, c1 as appShellInsetTopClass, c2 as appShellTopbarInsetClass, c3 as appStatTileClass, c4 as appSurfaceCardClass, c5 as applyThemePreset, c6 as applyTimbalTheme, c7 as clearTimbalTheme, c8 as connectionRowListClass, c9 as createTimbalTheme, ca as ensureThemeFontLink, cb as flushBarCategoryGap, cc as flushLineAreaEdgeToEdge, cd as formatLintReport, ce as getStoredThemePreset, cf as getThemePreset, cg as lintGeneratedUi, ch as resolveChartMargin, ci as resolveTooltipCategory, cj as reviewGeneratedUi, ck as themeToCss, cl as useAppCopilotContext, cm as useAppShellChat, cn as useAppShellNav, co as useInterval, cp as useLiveQuery } from './chart-artifact-BYl5C-dk.js';
3
3
  import { FC, ReactNode } from 'react';
4
4
  export { a as Avatar, b as AvatarFallback, c as AvatarImage, D as AvatarVariant, B as Banner, d as BannerProps, h as Button, i as ButtonColor, K as Kanban, j as KanbanCardData, k as KanbanCardVariant, l as KanbanColumnData, m as KanbanDensity, n as KanbanDragHandleProps, o as KanbanLocation, p as KanbanMoveEvent, q as KanbanProps, r as KanbanRenderCardContext, s as KanbanTone, T as Timeline, t as TimelineItem, u as TimelineProps, U as UntitledButton, w as UntitledButtonColor, x as UntitledButtonProps, y as UntitledButtonSize } from './kanban-FFBeaZPS.js';
5
5
  export { B as ChartArtifact, D as ChartSeriesConfig, p as ThreadVariant, s as TimbalChat, t as TimbalChatProps } from './chat-DDsp-Vzz.js';
package/dist/app.esm.js CHANGED
@@ -91,7 +91,7 @@ import {
91
91
  useAppShellNav,
92
92
  useInterval,
93
93
  useLiveQuery
94
- } from "./chunk-6SQMTBPL.esm.js";
94
+ } from "./chunk-UVPXH4MB.esm.js";
95
95
  import "./chunk-ELEY66OH.esm.js";
96
96
  import {
97
97
  CHART_PALETTE,
@@ -81,6 +81,15 @@ interface HouseRule {
81
81
  slop?: string;
82
82
  /** The tasteful equivalent. */
83
83
  good?: string;
84
+ /**
85
+ * How the rule is enforced. `"lint"` (the default) means `ui-lint.ts` has a
86
+ * deterministic check for it; `"prompt-only"` means the pattern can't be
87
+ * detected with high enough precision to gate on, so it's taught in the
88
+ * prompt but deliberately not linted (a low-precision check would false-flag
89
+ * legitimate UIs under the strict gate). Keeping this explicit lets a test
90
+ * assert every rule made a coverage decision — see `ui-lint.test.ts`.
91
+ */
92
+ enforcement?: "lint" | "prompt-only";
84
93
  }
85
94
  declare const HOUSE_RULES: readonly HouseRule[];
86
95
 
@@ -81,6 +81,15 @@ interface HouseRule {
81
81
  slop?: string;
82
82
  /** The tasteful equivalent. */
83
83
  good?: string;
84
+ /**
85
+ * How the rule is enforced. `"lint"` (the default) means `ui-lint.ts` has a
86
+ * deterministic check for it; `"prompt-only"` means the pattern can't be
87
+ * detected with high enough precision to gate on, so it's taught in the
88
+ * prompt but deliberately not linted (a low-precision check would false-flag
89
+ * legitimate UIs under the strict gate). Keeping this explicit lets a test
90
+ * assert every rule made a coverage decision — see `ui-lint.test.ts`.
91
+ */
92
+ enforcement?: "lint" | "prompt-only";
84
93
  }
85
94
  declare const HOUSE_RULES: readonly HouseRule[];
86
95
 
@@ -224,7 +224,11 @@ var HOUSE_RULES = [
224
224
  {
225
225
  id: "compose-from-blocks",
226
226
  rule: "Build from premade blocks (MetricRow, MetricChartCard, DataTable, IntegrationCard). Drop to raw primitives only when no block fits.",
227
- why: "Slop appears the moment generation falls below the curated block layer."
227
+ why: "Slop appears the moment generation falls below the curated block layer.",
228
+ // "Should have used a block" is a judgement about absence, not a textual
229
+ // pattern — no high-precision deterministic check exists, so this stays
230
+ // prompt-only rather than risk false-positives blocking valid UIs.
231
+ enforcement: "prompt-only"
228
232
  },
229
233
  {
230
234
  id: "use-kit-controls",
@@ -305,7 +309,7 @@ The content region is a **padded scroll area** by default \u2014 great for stack
305
309
  - Give the filling child **\`min-h-0 flex-1\`** (or \`h-full\`) so its own scroll/footer resolves \u2014 e.g. \`<TimbalChat className="min-h-0 flex-1" />\`, or a two-pane row where each pane is \`min-h-0 overflow-y-auto\`.
306
310
 
307
311
  \`\`\`tsx
308
- <AppShell contentFill topbar={<div className="flex justify-end p-4"><ModeToggle /></div>}>
312
+ <AppShell contentFill> {/* no global topbar / theme switch */}
309
313
  <Page fill> {/* headerless: omit title */}
310
314
  <TimbalChat workforceId="\u2026" className="min-h-0 flex-1" />
311
315
  </Page>
@@ -409,6 +413,8 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
409
413
 
410
414
  Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipContent\` chrome (see \`src/ui/chart.tsx\`). Series colors default to \`--chart-1..6\`; override those CSS tokens to rebrand every chart.
411
415
 
416
+ > **React 19 requirement \u2014 do not hand-roll SVG charts to work around this.** recharts under React 19 crashes (\`Cannot assign to read only property 'lanes'\`, blank route) when \`immer\` resolves to **11.0.0**. The fix is a dependency override in the app's \`package.json\` \u2014 \`"overrides": { "immer": ">=11.0.1" }\` (Yarn: \`"resolutions"\`) \u2014 **not** a code change. Always keep using \`LineAreaChart\` / \`PieChart\` / \`ChartPanel\`; never replace them with raw SVG/CSS charts.
417
+
412
418
  | Component | Use for |
413
419
  |-----------|---------|
414
420
  | \`LineAreaChart\` | Cartesian engine (shadcn-style chrome). Bar fills use theme gradients automatically. Props: \`data\`, \`xKey\`, \`series: [{ dataKey, label?, color? }]\`, \`variant\` (\`area\`\\|\`line\`\\|\`bar\`), \`orientation\` (\`horizontal\` for horizontal bars), \`stacked\`, \`curve\`, \`dots\`, \`gridLines\`, \`tooltipIndicator\`, \`layout\` (\`flush\` \u2014 hides axes by default; category + values on hover tooltip), \`showXAxis\` / \`showYAxis\` to opt back in, \`clipTicks\` (truncates long axis labels when axes are on), \`height\`, \`showLegend\`, \`formatX\`, \`formatValue\`, \`ariaLabel\`. |
@@ -589,6 +595,8 @@ var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
589
595
  var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
590
596
  var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
591
597
  var COLORED_HOVER_RE = /\bhover:(?:bg|from|to|via)-(?:primary|destructive|success|warn|danger|blue|emerald|green|amber|red|indigo|violet|purple|pink|rose|sky|cyan|teal|lime|yellow|orange|fuchsia)\b/;
598
+ var TREND_CONTEXT_RE = /\b(?:trend|delta|TrendingUp|TrendingDown|ArrowUp|ArrowDown|ArrowUpRight|ArrowDownRight|MoveUp|MoveDown)\b|[+\-]\d+(?:\.\d+)?\s*%/;
599
+ var TREND_COLOR_RE = /\b(?:text|bg|border)-(?:success|destructive|emerald|green|lime|teal|red|rose|orange|amber)(?:-\d{2,3})?(?:\/\d{1,3})?\b/;
592
600
  var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
593
601
  function stripVariants(util) {
594
602
  return util.replace(/^(?:[a-z-]+:)*/, "");
@@ -632,6 +640,16 @@ function lintGeneratedUi(source, options = {}) {
632
640
  if (cardMatch) {
633
641
  const isSelfClosing = /\/>/.test(line) && line.indexOf(cardMatch[0]) < line.indexOf("/>");
634
642
  if (!isSelfClosing) {
643
+ if (openCards.length > 0) {
644
+ const parentCard = openCards[openCards.length - 1];
645
+ findings.push({
646
+ rule: "no-card-in-card",
647
+ severity: "warn",
648
+ line: lineNo,
649
+ message: `Card inside card. A <${cardMatch[1]}> is nested inside the <${parentCard.type}> opened on L${parentCard.line}. Double borders/shadows add no information \u2014 group with spacing or a <Section> instead.`,
650
+ snippet: line.trim().slice(0, 120)
651
+ });
652
+ }
635
653
  openCards.push({ type: cardMatch[1], line: lineNo });
636
654
  }
637
655
  }
@@ -704,6 +722,15 @@ function lintGeneratedUi(source, options = {}) {
704
722
  snippet: line.trim().slice(0, 120)
705
723
  });
706
724
  }
725
+ if (TREND_CONTEXT_RE.test(line) && TREND_COLOR_RE.test(line)) {
726
+ findings.push({
727
+ rule: "neutral-trend",
728
+ severity: "warn",
729
+ line: lineNo,
730
+ message: "Colored trend indicator. House style: don't tint deltas green/red on every metric \u2014 show a trend only when the change is the point, and keep it muted (text-muted-foreground).",
731
+ snippet: line.trim().slice(0, 120)
732
+ });
733
+ }
707
734
  if (BOLD_VALUE_RE.test(line)) {
708
735
  findings.push({
709
736
  rule: "bold-metric",
@@ -0,0 +1,503 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/ui-lint.ts
4
+ import { readFileSync } from "fs";
5
+
6
+ // src/design/ui-vocabulary.ts
7
+ var RESERVED_GRADIENT_TOKENS = [
8
+ "primary-fill-from",
9
+ "primary-fill-to",
10
+ "primary-fill-hover-from",
11
+ "primary-fill-hover-to",
12
+ "primary-fill-active-from",
13
+ "primary-fill-active-to",
14
+ "secondary-fill-hover-from",
15
+ "secondary-fill-hover-to",
16
+ "secondary-fill-active-from",
17
+ "secondary-fill-active-to",
18
+ "destructive-fill-hover-from",
19
+ "destructive-fill-hover-to",
20
+ "destructive-fill-active-from",
21
+ "destructive-fill-active-to",
22
+ "ghost-fill-hover",
23
+ "ghost-fill-active",
24
+ "elevated-from",
25
+ "elevated-to",
26
+ "modal-from",
27
+ "modal-to",
28
+ "playground-from",
29
+ "playground-via",
30
+ "playground-to"
31
+ ];
32
+ var TAILWIND_PALETTE_COLORS = [
33
+ "slate",
34
+ "gray",
35
+ "zinc",
36
+ "neutral",
37
+ "stone",
38
+ "red",
39
+ "orange",
40
+ "amber",
41
+ "yellow",
42
+ "lime",
43
+ "green",
44
+ "emerald",
45
+ "teal",
46
+ "cyan",
47
+ "sky",
48
+ "blue",
49
+ "indigo",
50
+ "violet",
51
+ "purple",
52
+ "fuchsia",
53
+ "pink",
54
+ "rose"
55
+ ];
56
+ var COLOR_UTILITY_PREFIXES = [
57
+ "bg",
58
+ "text",
59
+ "border",
60
+ "ring",
61
+ "from",
62
+ "via",
63
+ "to",
64
+ "fill",
65
+ "stroke",
66
+ "decoration",
67
+ "outline",
68
+ "shadow",
69
+ "divide",
70
+ "accent",
71
+ "caret"
72
+ ];
73
+ var SLOP_BUDGETS = {
74
+ /** Max decorative/standalone icons rendered in a single generated file. */
75
+ maxIconsPerView: 6,
76
+ /** Max consecutive list rows separated by an explicit border/divider before
77
+ * it reads as a "ruled table" — prefer spacing or zebra instead. */
78
+ maxRowDividers: 2
79
+ };
80
+
81
+ // src/design/ui-lint.ts
82
+ var PALETTE_GROUP = TAILWIND_PALETTE_COLORS.join("|");
83
+ var PREFIX_GROUP = COLOR_UTILITY_PREFIXES.join("|");
84
+ var RAW_COLOR_RE = new RegExp(
85
+ `(?:^|[\\s"'\`:])(?:[a-z-]+:)*(?:${PREFIX_GROUP})-(?:${PALETTE_GROUP})-\\d{2,3}(?:/\\d{1,3})?`,
86
+ "g"
87
+ );
88
+ var COLOR_LITERAL_RE = /#[0-9a-fA-F]{3,8}\b|\b(?:oklch|rgba?|hsla?)\s*\(/g;
89
+ var INLINE_STYLE_COLOR_RE = /style=\{\{[^}]*\b(?:color|background|backgroundColor|borderColor|fill|stroke)\b/;
90
+ var BOLD_VALUE_RE = /text-(?:xl|2xl|3xl|4xl|5xl|6xl)[^"'`]*\bfont-(?:bold|extrabold|black|semibold)|font-(?:bold|extrabold|black|semibold)[^"'`]*text-(?:xl|2xl|3xl|4xl|5xl|6xl)/;
91
+ var GRADIENT_RE = /\bbg-(?:gradient|linear|radial|conic)-/;
92
+ var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
93
+ "t",
94
+ "tr",
95
+ "r",
96
+ "br",
97
+ "b",
98
+ "bl",
99
+ "l",
100
+ "tl"
101
+ ]);
102
+ var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
103
+ var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
104
+ var COLORED_HOVER_RE = /\bhover:(?:bg|from|to|via)-(?:primary|destructive|success|warn|danger|blue|emerald|green|amber|red|indigo|violet|purple|pink|rose|sky|cyan|teal|lime|yellow|orange|fuchsia)\b/;
105
+ var TREND_CONTEXT_RE = /\b(?:trend|delta|TrendingUp|TrendingDown|ArrowUp|ArrowDown|ArrowUpRight|ArrowDownRight|MoveUp|MoveDown)\b|[+\-]\d+(?:\.\d+)?\s*%/;
106
+ var TREND_COLOR_RE = /\b(?:text|bg|border)-(?:success|destructive|emerald|green|lime|teal|red|rose|orange|amber)(?:-\d{2,3})?(?:\/\d{1,3})?\b/;
107
+ var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
108
+ function stripVariants(util) {
109
+ return util.replace(/^(?:[a-z-]+:)*/, "");
110
+ }
111
+ function isCommentOrImport(line) {
112
+ const t = line.trim();
113
+ return t.startsWith("//") || t.startsWith("*") || t.startsWith("/*") || t.startsWith("import ") || t.startsWith("export ");
114
+ }
115
+ function lintGeneratedUi(source, options = {}) {
116
+ const maxIcons = options.maxIconsPerView ?? SLOP_BUDGETS.maxIconsPerView;
117
+ const maxRowDividers = options.maxRowDividers ?? SLOP_BUDGETS.maxRowDividers;
118
+ const findings = [];
119
+ const lines = source.split("\n");
120
+ let usesLucide = false;
121
+ let iconUsageCount = 0;
122
+ let dividerRunCount = 0;
123
+ let pageTitle = null;
124
+ const pageTitleMatch = source.match(/<Page\s+[^>]*\btitle=(?:"([^"]+)"|\{["']([^"']+)["']\})/);
125
+ if (pageTitleMatch) {
126
+ pageTitle = (pageTitleMatch[1] || pageTitleMatch[2]).trim().toLowerCase();
127
+ }
128
+ const hasChat = /\b(?:TimbalChat|AppChatPanel|Thread)\b/.test(source);
129
+ const lucideNames = /* @__PURE__ */ new Set();
130
+ const openCards = [];
131
+ for (let i = 0; i < lines.length; i++) {
132
+ const line = lines[i];
133
+ const lineNo = i + 1;
134
+ if (ICON_IMPORT_RE.test(line)) {
135
+ usesLucide = true;
136
+ const named = line.match(/\{([^}]*)\}/);
137
+ if (named) {
138
+ for (const raw of named[1].split(",")) {
139
+ const name = raw.trim().split(/\s+as\s+/)[0].trim();
140
+ if (name) lucideNames.add(name);
141
+ }
142
+ }
143
+ continue;
144
+ }
145
+ if (isCommentOrImport(line)) continue;
146
+ const cardMatch = line.match(/<(Card|SurfaceCard|ArtifactCard)\b/);
147
+ if (cardMatch) {
148
+ const isSelfClosing = /\/>/.test(line) && line.indexOf(cardMatch[0]) < line.indexOf("/>");
149
+ if (!isSelfClosing) {
150
+ if (openCards.length > 0) {
151
+ const parentCard = openCards[openCards.length - 1];
152
+ findings.push({
153
+ rule: "no-card-in-card",
154
+ severity: "warn",
155
+ line: lineNo,
156
+ message: `Card inside card. A <${cardMatch[1]}> is nested inside the <${parentCard.type}> opened on L${parentCard.line}. Double borders/shadows add no information \u2014 group with spacing or a <Section> instead.`,
157
+ snippet: line.trim().slice(0, 120)
158
+ });
159
+ }
160
+ openCards.push({ type: cardMatch[1], line: lineNo });
161
+ }
162
+ }
163
+ const closeMatch = line.match(/<\/(Card|SurfaceCard|ArtifactCard)\b/);
164
+ if (closeMatch && openCards.length > 0) {
165
+ const idx = openCards.map((c) => c.type).lastIndexOf(closeMatch[1]);
166
+ if (idx !== -1) {
167
+ openCards.splice(idx, 1);
168
+ }
169
+ }
170
+ if (openCards.length > 0) {
171
+ const tableMatch = line.match(/<(DataTable|table|Table)\b/);
172
+ if (tableMatch) {
173
+ const parentCard = openCards[openCards.length - 1];
174
+ findings.push({
175
+ rule: "no-table-in-card",
176
+ severity: "error",
177
+ line: lineNo,
178
+ message: `Table inside card. Never wrap a <${tableMatch[1]}> or table inside a <${parentCard.type}> (opened on L${parentCard.line}). Place the table directly on the Page or Section instead.`,
179
+ snippet: line.trim().slice(0, 120)
180
+ });
181
+ }
182
+ }
183
+ const rawColors = line.match(RAW_COLOR_RE);
184
+ if (rawColors) {
185
+ for (const m of rawColors) {
186
+ findings.push({
187
+ rule: "raw-color",
188
+ severity: "error",
189
+ line: lineNo,
190
+ message: "Hardcoded palette color. Use a semantic token (text-primary, bg-muted, border-border, text-muted-foreground, \u2026) so dark mode and rebranding work.",
191
+ snippet: m.trim().replace(/^["'`:\s]+/, "")
192
+ });
193
+ }
194
+ }
195
+ const literals = line.match(COLOR_LITERAL_RE);
196
+ if (literals) {
197
+ findings.push({
198
+ rule: "color-literal",
199
+ severity: "error",
200
+ line: lineNo,
201
+ message: "Hardcoded color literal. Colors must come from the theme generator (createTimbalTheme) and semantic tokens \u2014 never inline hex/oklch/rgb.",
202
+ snippet: line.trim().slice(0, 120)
203
+ });
204
+ }
205
+ if (INLINE_STYLE_COLOR_RE.test(line)) {
206
+ findings.push({
207
+ rule: "inline-style-color",
208
+ severity: "error",
209
+ line: lineNo,
210
+ message: "Inline style color. Move color to a semantic Tailwind token on className.",
211
+ snippet: line.trim().slice(0, 120)
212
+ });
213
+ }
214
+ if (RAW_CONTROL_SURFACE_RE.test(line)) {
215
+ findings.push({
216
+ rule: "raw-control-surface",
217
+ severity: "warn",
218
+ line: lineNo,
219
+ message: "Hand-rolled control surface (border-input). Use a kit control \u2014 SearchInput, Select, DropdownMenu, FieldInput, FieldSelect \u2014 so it matches every other control.",
220
+ snippet: line.trim().slice(0, 120)
221
+ });
222
+ }
223
+ if (COLORED_HOVER_RE.test(line)) {
224
+ findings.push({
225
+ rule: "no-colored-hover",
226
+ severity: "warn",
227
+ line: lineNo,
228
+ message: "Colored hover background/gradient. House style: interactive cards and list items must use neutral hover states \u2014 never hard-code colored backgrounds or borders on hover.",
229
+ snippet: line.trim().slice(0, 120)
230
+ });
231
+ }
232
+ if (TREND_CONTEXT_RE.test(line) && TREND_COLOR_RE.test(line)) {
233
+ findings.push({
234
+ rule: "neutral-trend",
235
+ severity: "warn",
236
+ line: lineNo,
237
+ message: "Colored trend indicator. House style: don't tint deltas green/red on every metric \u2014 show a trend only when the change is the point, and keep it muted (text-muted-foreground).",
238
+ snippet: line.trim().slice(0, 120)
239
+ });
240
+ }
241
+ if (BOLD_VALUE_RE.test(line)) {
242
+ findings.push({
243
+ rule: "bold-metric",
244
+ severity: "warn",
245
+ line: lineNo,
246
+ message: "Bold large value. House style: metric values use font-normal, not bold \u2014 bold giant numbers read as a template.",
247
+ snippet: line.trim().slice(0, 120)
248
+ });
249
+ }
250
+ if (GRADIENT_RE.test(line)) {
251
+ const fromTo = line.match(
252
+ new RegExp(`(?:from|via|to)-([a-z-]+)`, "g")
253
+ );
254
+ const colorStops = (fromTo ?? []).map((u) => stripVariants(u).replace(/^(?:from|via|to)-/, "")).filter((token) => !GRADIENT_DIRECTIONS.has(token));
255
+ const allReserved = colorStops.length > 0 && colorStops.every((token) => RESERVED_GRADIENT_SET.has(token));
256
+ if (!allReserved) {
257
+ findings.push({
258
+ rule: "data-gradient",
259
+ severity: "warn",
260
+ line: lineNo,
261
+ message: "Gradient outside chrome. Gradients are reserved for buttons / elevated / modal / playground \u2014 never a data card, tile, or table.",
262
+ snippet: line.trim().slice(0, 120)
263
+ });
264
+ }
265
+ }
266
+ if (/\b(?:border-t|border-b|divide-y)\b/.test(line)) {
267
+ dividerRunCount++;
268
+ if (dividerRunCount === maxRowDividers + 1) {
269
+ findings.push({
270
+ rule: "row-divider",
271
+ severity: "warn",
272
+ line: lineNo,
273
+ message: "Divider on every row. Prefer spacing (gap-*) or zebra striping over a rule under each list item.",
274
+ snippet: line.trim().slice(0, 120)
275
+ });
276
+ }
277
+ } else if (line.trim() !== "" && !line.includes("className")) {
278
+ if (!/^\s*[)>}/]/.test(line)) dividerRunCount = 0;
279
+ }
280
+ if (usesLucide && lucideNames.size > 0) {
281
+ for (const name of lucideNames) {
282
+ const usage = new RegExp(`<${name}\\b`, "g");
283
+ const hits = line.match(usage);
284
+ if (hits) iconUsageCount += hits.length;
285
+ }
286
+ }
287
+ if (pageTitle) {
288
+ const titleMatch = line.match(/<(Section|ChartPanel|Card|DataTable|SurfaceCard)\s+[^>]*\btitle=(?:"([^"]+)"|\{["']([^"']+)["']\})/);
289
+ if (titleMatch) {
290
+ const element = titleMatch[1];
291
+ const titleVal = (titleMatch[2] || titleMatch[3]).trim().toLowerCase();
292
+ if (titleVal === pageTitle || titleVal.includes(pageTitle) || pageTitle.includes(titleVal)) {
293
+ findings.push({
294
+ rule: "no-title-repetition",
295
+ severity: "warn",
296
+ line: lineNo,
297
+ message: `Title repetition. The <${element}> title "${titleVal}" repeats or is very similar to the <Page> title "${pageTitle}". Drop the title from the child element or use a title-less Section to avoid redundant headings.`,
298
+ snippet: line.trim().slice(0, 120)
299
+ });
300
+ }
301
+ }
302
+ }
303
+ if (hasChat) {
304
+ const wrappingMatch = line.match(/<(Card|Section|SurfaceCard|FormSection|SettingsSection)\b/);
305
+ if (wrappingMatch) {
306
+ findings.push({
307
+ rule: "no-chat-wrapping",
308
+ severity: "error",
309
+ line: lineNo,
310
+ message: `Chat component wrapping. Never wrap TimbalChat or AppChatPanel inside a <${wrappingMatch[1]}> or custom bordered container. Let the chat component fill the page or slot directly.`,
311
+ snippet: line.trim().slice(0, 120)
312
+ });
313
+ }
314
+ const headingMatch = line.match(/<(h[1-6])\b/);
315
+ if (headingMatch) {
316
+ findings.push({
317
+ rule: "no-chat-wrapping",
318
+ severity: "error",
319
+ line: lineNo,
320
+ message: `Custom heading in chat view. Do not render custom <${headingMatch[1]}> headings on the chat page. Pass welcome.heading to TimbalChat if you need to customize the welcome title.`,
321
+ snippet: line.trim().slice(0, 120)
322
+ });
323
+ }
324
+ }
325
+ }
326
+ if (usesLucide && iconUsageCount > maxIcons) {
327
+ findings.push({
328
+ rule: "icon-spam",
329
+ severity: "warn",
330
+ line: 1,
331
+ message: `Too many icons (${iconUsageCount} > ${maxIcons}). Icons should mark actions/nav/status \u2014 not decorate every label, tile, and card.`,
332
+ snippet: `${iconUsageCount} lucide-react icon usages`
333
+ });
334
+ }
335
+ const effectiveErrors = findings.filter(
336
+ (f) => f.severity === "error" || options.strict && f.severity === "warn"
337
+ ).length;
338
+ return {
339
+ findings,
340
+ errorCount: findings.filter((f) => f.severity === "error").length,
341
+ warnCount: findings.filter((f) => f.severity === "warn").length,
342
+ ok: effectiveErrors === 0
343
+ };
344
+ }
345
+ function formatLintReport(findings) {
346
+ if (findings.length === 0) return "";
347
+ const lines = findings.slice().sort((a, b) => a.line - b.line).map((f) => {
348
+ const tag = f.severity === "error" ? "ERROR" : "warn ";
349
+ return ` ${tag} L${f.line} [${f.rule}] ${f.message}
350
+ \u2192 ${f.snippet}`;
351
+ });
352
+ const errs = findings.filter((f) => f.severity === "error").length;
353
+ const warns = findings.filter((f) => f.severity === "warn").length;
354
+ return `Anti-slop review: ${errs} error(s), ${warns} warning(s)
355
+ ${lines.join("\n")}`;
356
+ }
357
+
358
+ // src/design/ui-review.ts
359
+ function reviewGeneratedUi(source, options = {}) {
360
+ const lint = lintGeneratedUi(source, options);
361
+ const report = formatLintReport(lint.findings);
362
+ if (lint.ok) {
363
+ return { lint, passed: true, report, revisionPrompt: null };
364
+ }
365
+ const revisionPrompt = [
366
+ "The generated UI failed the Timbal anti-slop review. Fix every issue below, then return the corrected code only.",
367
+ "",
368
+ report,
369
+ "",
370
+ "Rules: colors come only from semantic tokens (text-primary, bg-muted, border-border, text-muted-foreground, \u2026) \u2014 never palette colors, hex, or oklch. Icons mark actions/nav/status, not decoration. Metric values use font-normal. No gradients on data surfaces. No divider under every row. Do not change anything that already passed."
371
+ ].join("\n");
372
+ return { lint, passed: false, report, revisionPrompt };
373
+ }
374
+ var UI_REVIEW_AGENT_INSTRUCTIONS = `
375
+ ## Self-review before returning UI (anti-slop)
376
+
377
+ Before you output any generated UI code, silently re-read it and fix anything that matches the slop checklist \u2014 this is the same rubric an automated linter applies, so output that fails it will be rejected and sent back:
378
+
379
+ - **No hardcoded colors.** Every color is a semantic token (\`text-primary\`, \`bg-muted\`, \`border-border\`, \`text-muted-foreground\`, \`bg-destructive\`, \u2026). No \`text-blue-600\`, no \`#hex\`, no \`oklch(...)\`, no \`style={{ color }}\`.
380
+ - **No decorative icons.** An icon must mark an action, nav target, or status. Remove icons that sit beside a label that already says the thing. Aim for very few icons per view.
381
+ - **Muted, sparse trends.** No colored up/down pill on every metric. Show a trend only when the change is the point.
382
+ - **Normal-weight values.** Metric numbers use \`font-normal\`, never \`font-bold\` at large sizes.
383
+ - **No card-in-card, no per-row dividers, no gradients on data surfaces.** Group with spacing/Sections; reserve gradients for chrome.
384
+ - **Compose from blocks.** Prefer \`MetricRow\` / \`MetricChartCard\` / \`DataTable\` / \`IntegrationCard\` over hand-assembled primitives.
385
+
386
+ If a check fails, fix it and re-read once more. Only return code that would pass clean.
387
+ `.trim();
388
+
389
+ // src/cli/ui-lint.ts
390
+ function parseArgs(argv) {
391
+ const parsed = { strict: false, json: false, files: [] };
392
+ for (let i = 0; i < argv.length; i++) {
393
+ const arg = argv[i];
394
+ switch (arg) {
395
+ case "--strict":
396
+ parsed.strict = true;
397
+ break;
398
+ case "--json":
399
+ parsed.json = true;
400
+ break;
401
+ case "--max-icons":
402
+ parsed.maxIcons = Number(argv[++i]);
403
+ break;
404
+ case "--max-row-dividers":
405
+ parsed.maxRowDividers = Number(argv[++i]);
406
+ break;
407
+ case "-h":
408
+ case "--help":
409
+ printUsage();
410
+ process.exit(0);
411
+ break;
412
+ default:
413
+ if (arg.startsWith("-")) {
414
+ process.stderr.write(`timbal-ui-lint: unknown flag ${arg}
415
+ `);
416
+ process.exit(2);
417
+ }
418
+ parsed.files.push(arg);
419
+ }
420
+ }
421
+ return parsed;
422
+ }
423
+ function printUsage() {
424
+ process.stdout.write(
425
+ "Usage: timbal-ui-lint [--strict] [--max-icons N] [--max-row-dividers N] [--json] <file.tsx> [...]\n"
426
+ );
427
+ }
428
+ function main() {
429
+ const args = parseArgs(process.argv.slice(2));
430
+ if (args.files.length === 0) {
431
+ printUsage();
432
+ process.exit(2);
433
+ }
434
+ const reviewOptions = {
435
+ strict: args.strict,
436
+ maxIconsPerView: Number.isFinite(args.maxIcons) ? args.maxIcons : void 0,
437
+ maxRowDividers: Number.isFinite(args.maxRowDividers) ? args.maxRowDividers : void 0
438
+ };
439
+ const results = [];
440
+ for (const file of args.files) {
441
+ let source;
442
+ try {
443
+ source = readFileSync(file, "utf8");
444
+ } catch (e) {
445
+ results.push({
446
+ file,
447
+ passed: true,
448
+ report: "",
449
+ findings: [],
450
+ revisionPrompt: null,
451
+ error: e instanceof Error ? e.message : String(e)
452
+ });
453
+ continue;
454
+ }
455
+ const review = reviewGeneratedUi(source, reviewOptions);
456
+ results.push({
457
+ file,
458
+ passed: review.passed,
459
+ report: review.report,
460
+ findings: review.lint.findings,
461
+ revisionPrompt: review.revisionPrompt
462
+ });
463
+ }
464
+ const failed = results.filter((r) => !r.passed);
465
+ const ok = failed.length === 0;
466
+ if (args.json) {
467
+ process.stdout.write(
468
+ JSON.stringify(
469
+ {
470
+ ok,
471
+ files: results.map(({ revisionPrompt, ...rest }) => rest),
472
+ revisionPrompt: ok ? null : buildCombinedPrompt(failed)
473
+ },
474
+ null,
475
+ 2
476
+ ) + "\n"
477
+ );
478
+ process.exit(ok ? 0 : 1);
479
+ }
480
+ if (ok) {
481
+ process.stdout.write("Anti-slop review passed.\n");
482
+ process.exit(0);
483
+ }
484
+ for (const r of failed) {
485
+ process.stdout.write(`
486
+ === ${r.file} ===
487
+ ${r.report}
488
+ `);
489
+ }
490
+ process.stdout.write(`
491
+ ${buildCombinedPrompt(failed)}
492
+ `);
493
+ process.exit(1);
494
+ }
495
+ function buildCombinedPrompt(failed) {
496
+ const header = "The generated UI failed the Timbal anti-slop review. Fix every issue below, then re-check. Do not change anything that already passed.";
497
+ const perFile = failed.filter((r) => r.report).map((r) => `
498
+ --- ${r.file} ---
499
+ ${r.report}`).join("\n");
500
+ const rules = "Rules: colors come only from semantic tokens (text-primary, bg-muted, border-border, text-muted-foreground, \u2026) \u2014 never palette colors, hex, or oklch. Icons mark actions/nav/status, not decoration. Metric values use font-normal. No card-in-card, no per-row dividers, no gradients on data surfaces. Compose from kit blocks (MetricRow, DataTable, AlertCard) instead of hand-rolled primitives.";
501
+ return [header, perFile, "", rules].join("\n");
502
+ }
503
+ main();
package/dist/index.cjs CHANGED
@@ -8132,7 +8132,11 @@ var HOUSE_RULES = [
8132
8132
  {
8133
8133
  id: "compose-from-blocks",
8134
8134
  rule: "Build from premade blocks (MetricRow, MetricChartCard, DataTable, IntegrationCard). Drop to raw primitives only when no block fits.",
8135
- why: "Slop appears the moment generation falls below the curated block layer."
8135
+ why: "Slop appears the moment generation falls below the curated block layer.",
8136
+ // "Should have used a block" is a judgement about absence, not a textual
8137
+ // pattern — no high-precision deterministic check exists, so this stays
8138
+ // prompt-only rather than risk false-positives blocking valid UIs.
8139
+ enforcement: "prompt-only"
8136
8140
  },
8137
8141
  {
8138
8142
  id: "use-kit-controls",
@@ -8213,7 +8217,7 @@ The content region is a **padded scroll area** by default \u2014 great for stack
8213
8217
  - Give the filling child **\`min-h-0 flex-1\`** (or \`h-full\`) so its own scroll/footer resolves \u2014 e.g. \`<TimbalChat className="min-h-0 flex-1" />\`, or a two-pane row where each pane is \`min-h-0 overflow-y-auto\`.
8214
8218
 
8215
8219
  \`\`\`tsx
8216
- <AppShell contentFill topbar={<div className="flex justify-end p-4"><ModeToggle /></div>}>
8220
+ <AppShell contentFill> {/* no global topbar / theme switch */}
8217
8221
  <Page fill> {/* headerless: omit title */}
8218
8222
  <TimbalChat workforceId="\u2026" className="min-h-0 flex-1" />
8219
8223
  </Page>
@@ -8317,6 +8321,8 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
8317
8321
 
8318
8322
  Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipContent\` chrome (see \`src/ui/chart.tsx\`). Series colors default to \`--chart-1..6\`; override those CSS tokens to rebrand every chart.
8319
8323
 
8324
+ > **React 19 requirement \u2014 do not hand-roll SVG charts to work around this.** recharts under React 19 crashes (\`Cannot assign to read only property 'lanes'\`, blank route) when \`immer\` resolves to **11.0.0**. The fix is a dependency override in the app's \`package.json\` \u2014 \`"overrides": { "immer": ">=11.0.1" }\` (Yarn: \`"resolutions"\`) \u2014 **not** a code change. Always keep using \`LineAreaChart\` / \`PieChart\` / \`ChartPanel\`; never replace them with raw SVG/CSS charts.
8325
+
8320
8326
  | Component | Use for |
8321
8327
  |-----------|---------|
8322
8328
  | \`LineAreaChart\` | Cartesian engine (shadcn-style chrome). Bar fills use theme gradients automatically. Props: \`data\`, \`xKey\`, \`series: [{ dataKey, label?, color? }]\`, \`variant\` (\`area\`\\|\`line\`\\|\`bar\`), \`orientation\` (\`horizontal\` for horizontal bars), \`stacked\`, \`curve\`, \`dots\`, \`gridLines\`, \`tooltipIndicator\`, \`layout\` (\`flush\` \u2014 hides axes by default; category + values on hover tooltip), \`showXAxis\` / \`showYAxis\` to opt back in, \`clipTicks\` (truncates long axis labels when axes are on), \`height\`, \`showLegend\`, \`formatX\`, \`formatValue\`, \`ariaLabel\`. |
@@ -8497,6 +8503,8 @@ var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
8497
8503
  var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
8498
8504
  var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
8499
8505
  var COLORED_HOVER_RE = /\bhover:(?:bg|from|to|via)-(?:primary|destructive|success|warn|danger|blue|emerald|green|amber|red|indigo|violet|purple|pink|rose|sky|cyan|teal|lime|yellow|orange|fuchsia)\b/;
8506
+ var TREND_CONTEXT_RE = /\b(?:trend|delta|TrendingUp|TrendingDown|ArrowUp|ArrowDown|ArrowUpRight|ArrowDownRight|MoveUp|MoveDown)\b|[+\-]\d+(?:\.\d+)?\s*%/;
8507
+ var TREND_COLOR_RE = /\b(?:text|bg|border)-(?:success|destructive|emerald|green|lime|teal|red|rose|orange|amber)(?:-\d{2,3})?(?:\/\d{1,3})?\b/;
8500
8508
  var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
8501
8509
  function stripVariants(util) {
8502
8510
  return util.replace(/^(?:[a-z-]+:)*/, "");
@@ -8540,6 +8548,16 @@ function lintGeneratedUi(source, options = {}) {
8540
8548
  if (cardMatch) {
8541
8549
  const isSelfClosing = /\/>/.test(line) && line.indexOf(cardMatch[0]) < line.indexOf("/>");
8542
8550
  if (!isSelfClosing) {
8551
+ if (openCards.length > 0) {
8552
+ const parentCard = openCards[openCards.length - 1];
8553
+ findings.push({
8554
+ rule: "no-card-in-card",
8555
+ severity: "warn",
8556
+ line: lineNo,
8557
+ message: `Card inside card. A <${cardMatch[1]}> is nested inside the <${parentCard.type}> opened on L${parentCard.line}. Double borders/shadows add no information \u2014 group with spacing or a <Section> instead.`,
8558
+ snippet: line.trim().slice(0, 120)
8559
+ });
8560
+ }
8543
8561
  openCards.push({ type: cardMatch[1], line: lineNo });
8544
8562
  }
8545
8563
  }
@@ -8612,6 +8630,15 @@ function lintGeneratedUi(source, options = {}) {
8612
8630
  snippet: line.trim().slice(0, 120)
8613
8631
  });
8614
8632
  }
8633
+ if (TREND_CONTEXT_RE.test(line) && TREND_COLOR_RE.test(line)) {
8634
+ findings.push({
8635
+ rule: "neutral-trend",
8636
+ severity: "warn",
8637
+ line: lineNo,
8638
+ message: "Colored trend indicator. House style: don't tint deltas green/red on every metric \u2014 show a trend only when the change is the point, and keep it muted (text-muted-foreground).",
8639
+ snippet: line.trim().slice(0, 120)
8640
+ });
8641
+ }
8615
8642
  if (BOLD_VALUE_RE.test(line)) {
8616
8643
  findings.push({
8617
8644
  rule: "bold-metric",
package/dist/index.d.cts CHANGED
@@ -6,7 +6,7 @@ import { ToolCallMessagePartComponent } from '@assistant-ui/react';
6
6
  export { ActionBarPrimitive, AssistantRuntimeProvider, AttachmentAdapter, AuiIf, ComposerPrimitive, MessagePrimitive, ThreadPrimitive, useComposerRuntime, useMessageRuntime, useThread, useThreadRuntime } from '@assistant-ui/react';
7
7
  export { M as ModeToggle, a as ModeToggleProps, b as ModeToggleTheme, S as STUDIO_NAV_MODE, c as StudioModeSwitch, d as StudioModeSwitchProps, e as StudioNavMode, f as StudioSidebar, g as StudioSidebarProps, h as StudioWelcome, i as StudioWelcomeProps, T as TimbalChatShell, j as TimbalChatShellProps, k as TimbalMark, l as TimbalMarkProps, m as TimbalStudioShell, n as TimbalStudioShellProps } from './welcome-B00oH5Io.cjs';
8
8
  export { M as MarkdownText, T as THREAD_DEFAULT_MAX_WIDTH, a as ToolFallback, b as TooltipIconButton, c as TooltipIconButtonProps, W as WorkforceSelector, d as WorkforceSelectorProps, e as assistantMessageContentClass, f as assistantMessageRootClass, t as threadMessageColumnClass, u as useToolRunning, g as userMessageRootClass } from './layout-PzVwkJyL.cjs';
9
- export { d as APP_KIT_AGENT_INSTRUCTIONS, u as AppBreadcrumbItem, e as AppChatPanel, f as AppChatPanelProps, g as AppConfirmDialog, h as AppConfirmDialogProps, i as AppCopilotContextValue, j as AppCopilotProvider, k as AppCopilotProviderProps, l as AppPageWidth, m as AppShell, n as AppShellChatControls, o as AppShellChatTrigger, p as AppShellChatTriggerProps, q as AppShellNavControls, r as AppShellProps, s as AppShellSidebarTrigger, t as AppShellSidebarTriggerProps, B as BreadcrumbEntry, v as Breadcrumbs, w as BreadcrumbsProps, C as CHART_PALETTE, x as COLOR_UTILITY_PREFIXES, y as ChartArtifactView, E as ChartPanel, F as ChartPanelProps, G as ChartSeries, H as ChartTooltipIndicator, I as ChartVariant, J as ConnectionRow, K as ConnectionRowList, L as ConnectionRowListProps, M as ConnectionRowProps, N as DangerZone, O as DangerZoneAction, P as DangerZoneActionProps, Q as DangerZoneProps, R as DataTable, T as DataTableColumn, U as DataTableProps, V as DataTableSort, W as DataTableSortDirection, X as DescriptionItem, Y as DescriptionList, Z as DescriptionListProps, _ as EmptyState, $ as EmptyStateProps, a0 as ExpandableSection, a1 as ExpandableSectionProps, a2 as Field, a3 as FieldInput, a4 as FieldInputProps, a5 as FieldProps, a6 as FieldRow, a7 as FieldRowProps, a8 as FieldSelect, a9 as FieldSelectProps, aa as FieldSwitch, ab as FieldSwitchProps, ac as FieldTextarea, ad as FieldTextareaProps, ae as FilterBar, af as FilterBarProps, ag as FilterDatePreset, ah as FilterDateRangeValue, ai as FilterDropdown, aj as FilterDropdownProps, ak as FilterField, al as FilterFieldDef, am as FilterFieldProps, an as FilterFieldType, ao as FilterNumericOperatorOption, ap as FilterNumericValue, aq as FilterSelectOption, ar as FilterValue, as as FilterValues, at as FloatingUnsavedChangesBar, au as FloatingUnsavedChangesBarProps, av as FormSection, aw as FormSectionProps, ax as HOUSE_RULES, ay as HouseRule, az as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aA as InfoCard, aB as InfoCardProps, aC as InfoCardTone, aD as IntegrationCard, aE as IntegrationCardProps, aF as IntegrationCardStatus, aG as IntegrationsEmptyState, aH as IntegrationsEmptyStateProps, aI as LineAreaChart, aJ as LineAreaChartProps, aK as LintFinding, aL as LintOptions, aM as LintResult, aN as LintSeverity, aO as MetricChartCard, aP as MetricChartCardProps, aQ as MetricChartMetric, aR as MetricRow, aS as MetricRowItem, aT as MetricRowProps, aU as MetricTile, aV as MetricTileProps, aW as NumericOperator, aX as Page, aY as PageHeader, aZ as PageHeaderProps, a_ as PageProps, a$ as PieChart, b0 as PieChartProps, b1 as PlanBadge, b2 as PlanBadgeProps, b3 as PlanBadgeTone, b4 as RESERVED_GRADIENT_TOKENS, b5 as RadarChart, b6 as RadarChartProps, b7 as RadialChart, b8 as RadialChartProps, b9 as ResourceCard, ba as ResourceCardProps, bb as ReviewResult, bc as SEMANTIC_COLOR_TOKENS, bd as SLOP_BUDGETS, be as SearchInput, bf as SearchInputProps, bg as Section, bh as SectionProps, bi as SemanticColorToken, bj as SettingsSection, bk as SettingsSectionHeader, bl as SettingsSectionHeaderProps, bm as SettingsSectionProps, bn as Sparkline, bo as SparklineProps, bp as Stack, bq as StackAlign, br as StackGap, bs as StackJustify, bt as StackProps, bu as StatTile, bv as StatTileProps, bw as StatTileTone, bx as StatusBadge, by as StatusBadgeProps, S as StatusBadgeTone, bz as StatusDot, bA as StatusDotProps, bB as StatusDotTone, bC as SubNav, bD as SubNavItem, bE as SubNavProps, bF as SurfaceCard, bG as SurfaceCardProps, bH as SurfaceCardTone, bI as SurfaceCardVariant, bJ as TAILWIND_PALETTE_COLORS, bK as THEME_AGENT_INSTRUCTIONS, bL as TIMBAL_THEME_PRESETS, bM as ThemeShadow, bN as ThemeToCssOptions, bO as ThemeTokenMap, bP as TimbalThemeIntent, bQ as TimbalThemePreset, bR as TimbalThemePresetId, bS as TimbalThemeStyle, bT as TimbalThemeStyleProps, bU as TimbalThemeTokens, bV as TimbalThemeTypography, bW as UI_REVIEW_AGENT_INSTRUCTIONS, bX as UseLiveQueryOptions, bY as UseLiveQueryResult, c5 as applyThemePreset, c6 as applyTimbalTheme, c7 as clearTimbalTheme, c8 as connectionRowListClass, c9 as createTimbalTheme, ca as ensureThemeFontLink, cb as flushBarCategoryGap, cc as flushLineAreaEdgeToEdge, cd as formatLintReport, ce as getStoredThemePreset, cf as getThemePreset, cg as lintGeneratedUi, ch as resolveChartMargin, ci as resolveTooltipCategory, cj as reviewGeneratedUi, ck as themeToCss, cl as useAppCopilotContext, cm as useAppShellChat, cn as useAppShellNav, co as useInterval, cp as useLiveQuery } from './chart-artifact-VAqgH-My.cjs';
9
+ export { d as APP_KIT_AGENT_INSTRUCTIONS, u as AppBreadcrumbItem, e as AppChatPanel, f as AppChatPanelProps, g as AppConfirmDialog, h as AppConfirmDialogProps, i as AppCopilotContextValue, j as AppCopilotProvider, k as AppCopilotProviderProps, l as AppPageWidth, m as AppShell, n as AppShellChatControls, o as AppShellChatTrigger, p as AppShellChatTriggerProps, q as AppShellNavControls, r as AppShellProps, s as AppShellSidebarTrigger, t as AppShellSidebarTriggerProps, B as BreadcrumbEntry, v as Breadcrumbs, w as BreadcrumbsProps, C as CHART_PALETTE, x as COLOR_UTILITY_PREFIXES, y as ChartArtifactView, E as ChartPanel, F as ChartPanelProps, G as ChartSeries, H as ChartTooltipIndicator, I as ChartVariant, J as ConnectionRow, K as ConnectionRowList, L as ConnectionRowListProps, M as ConnectionRowProps, N as DangerZone, O as DangerZoneAction, P as DangerZoneActionProps, Q as DangerZoneProps, R as DataTable, T as DataTableColumn, U as DataTableProps, V as DataTableSort, W as DataTableSortDirection, X as DescriptionItem, Y as DescriptionList, Z as DescriptionListProps, _ as EmptyState, $ as EmptyStateProps, a0 as ExpandableSection, a1 as ExpandableSectionProps, a2 as Field, a3 as FieldInput, a4 as FieldInputProps, a5 as FieldProps, a6 as FieldRow, a7 as FieldRowProps, a8 as FieldSelect, a9 as FieldSelectProps, aa as FieldSwitch, ab as FieldSwitchProps, ac as FieldTextarea, ad as FieldTextareaProps, ae as FilterBar, af as FilterBarProps, ag as FilterDatePreset, ah as FilterDateRangeValue, ai as FilterDropdown, aj as FilterDropdownProps, ak as FilterField, al as FilterFieldDef, am as FilterFieldProps, an as FilterFieldType, ao as FilterNumericOperatorOption, ap as FilterNumericValue, aq as FilterSelectOption, ar as FilterValue, as as FilterValues, at as FloatingUnsavedChangesBar, au as FloatingUnsavedChangesBarProps, av as FormSection, aw as FormSectionProps, ax as HOUSE_RULES, ay as HouseRule, az as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aA as InfoCard, aB as InfoCardProps, aC as InfoCardTone, aD as IntegrationCard, aE as IntegrationCardProps, aF as IntegrationCardStatus, aG as IntegrationsEmptyState, aH as IntegrationsEmptyStateProps, aI as LineAreaChart, aJ as LineAreaChartProps, aK as LintFinding, aL as LintOptions, aM as LintResult, aN as LintSeverity, aO as MetricChartCard, aP as MetricChartCardProps, aQ as MetricChartMetric, aR as MetricRow, aS as MetricRowItem, aT as MetricRowProps, aU as MetricTile, aV as MetricTileProps, aW as NumericOperator, aX as Page, aY as PageHeader, aZ as PageHeaderProps, a_ as PageProps, a$ as PieChart, b0 as PieChartProps, b1 as PlanBadge, b2 as PlanBadgeProps, b3 as PlanBadgeTone, b4 as RESERVED_GRADIENT_TOKENS, b5 as RadarChart, b6 as RadarChartProps, b7 as RadialChart, b8 as RadialChartProps, b9 as ResourceCard, ba as ResourceCardProps, bb as ReviewResult, bc as SEMANTIC_COLOR_TOKENS, bd as SLOP_BUDGETS, be as SearchInput, bf as SearchInputProps, bg as Section, bh as SectionProps, bi as SemanticColorToken, bj as SettingsSection, bk as SettingsSectionHeader, bl as SettingsSectionHeaderProps, bm as SettingsSectionProps, bn as Sparkline, bo as SparklineProps, bp as Stack, bq as StackAlign, br as StackGap, bs as StackJustify, bt as StackProps, bu as StatTile, bv as StatTileProps, bw as StatTileTone, bx as StatusBadge, by as StatusBadgeProps, S as StatusBadgeTone, bz as StatusDot, bA as StatusDotProps, bB as StatusDotTone, bC as SubNav, bD as SubNavItem, bE as SubNavProps, bF as SurfaceCard, bG as SurfaceCardProps, bH as SurfaceCardTone, bI as SurfaceCardVariant, bJ as TAILWIND_PALETTE_COLORS, bK as THEME_AGENT_INSTRUCTIONS, bL as TIMBAL_THEME_PRESETS, bM as ThemeShadow, bN as ThemeToCssOptions, bO as ThemeTokenMap, bP as TimbalThemeIntent, bQ as TimbalThemePreset, bR as TimbalThemePresetId, bS as TimbalThemeStyle, bT as TimbalThemeStyleProps, bU as TimbalThemeTokens, bV as TimbalThemeTypography, bW as UI_REVIEW_AGENT_INSTRUCTIONS, bX as UseLiveQueryOptions, bY as UseLiveQueryResult, c5 as applyThemePreset, c6 as applyTimbalTheme, c7 as clearTimbalTheme, c8 as connectionRowListClass, c9 as createTimbalTheme, ca as ensureThemeFontLink, cb as flushBarCategoryGap, cc as flushLineAreaEdgeToEdge, cd as formatLintReport, ce as getStoredThemePreset, cf as getThemePreset, cg as lintGeneratedUi, ch as resolveChartMargin, ci as resolveTooltipCategory, cj as reviewGeneratedUi, ck as themeToCss, cl as useAppCopilotContext, cm as useAppShellChat, cn as useAppShellNav, co as useInterval, cp as useLiveQuery } from './chart-artifact-Dpt4t5sf.cjs';
10
10
  export { A as AVATAR_PRIMARY_FALLBACK_CLASS, a as Avatar, b as AvatarFallback, c as AvatarImage, B as Banner, d as BannerProps, e as BannerSize, f as BannerTone, g as BannerVariant, h as Button, i as ButtonColor, K as Kanban, j as KanbanCardData, k as KanbanCardVariant, l as KanbanColumnData, m as KanbanDensity, n as KanbanDragHandleProps, o as KanbanLocation, p as KanbanMoveEvent, q as KanbanProps, r as KanbanRenderCardContext, s as KanbanTone, T as Timeline, t as TimelineItem, u as TimelineProps, v as TimelineSize, U as UntitledButton, w as UntitledButtonColor, x as UntitledButtonProps, y as UntitledButtonSize, z as avatarChartVariantClass, C as untitledButtonVariants } from './kanban-FFBeaZPS.cjs';
11
11
  import React__default, { FC, ReactNode } from 'react';
12
12
  export { A as Accordion, a as AccordionContent, b as AccordionItem, c as AccordionTrigger, d as Alert, e as AlertDescription, f as AlertDialog, g as AlertDialogAction, h as AlertDialogCancel, i as AlertDialogContent, j as AlertDialogDescription, k as AlertDialogFooter, l as AlertDialogHeader, m as AlertDialogOverlay, n as AlertDialogPortal, o as AlertDialogTitle, p as AlertDialogTrigger, q as AlertTitle, r as AspectRatio, s as AvatarGroup, t as AvatarGroupProps, B as Badge, u as Breadcrumb, v as BreadcrumbEllipsis, w as BreadcrumbItem, x as BreadcrumbLink, y as BreadcrumbList, z as BreadcrumbPage, C as BreadcrumbSeparator, D as CONTROL_SIZE, E as Calendar, F as CalendarDayButton, G as Card, H as CardContent, I as CardDescription, J as CardFooter, K as CardHeader, L as CardTitle, M as Checkbox, N as CircularProgress, O as CircularProgressProps, P as Collapsible, Q as CollapsibleContent, R as CollapsibleTrigger, S as Combobox, T as ComboboxAnchor, U as ComboboxCommand, V as ComboboxContent, W as ComboboxEmpty, X as ComboboxGroup, Y as ComboboxInput, Z as ComboboxItem, _ as ComboboxList, $ as ComboboxSeparator, a0 as ComboboxShortcut, a1 as ComboboxTrigger, a2 as Command, a3 as CommandDialog, a4 as CommandEmpty, a5 as CommandGroup, a6 as CommandInput, a7 as CommandItem, a8 as CommandList, a9 as CommandSeparator, aa as CommandShortcut, ab as ContextMenu, ac as ContextMenuCheckboxItem, ad as ContextMenuContent, ae as ContextMenuGroup, af as ContextMenuItem, ag as ContextMenuLabel, ah as ContextMenuRadioGroup, ai as ContextMenuRadioItem, aj as ContextMenuSeparator, ak as ContextMenuShortcut, al as ContextMenuSub, am as ContextMenuSubContent, an as ContextMenuSubTrigger, ao as ContextMenuTrigger, ap as ControlClassOptions, aq as ControlShape, ar as ControlSize, as as CopyButton, at as CopyButtonProps, au as DatePicker, av as DatePickerButton, aw as DatePickerCalendar, ax as DatePickerContent, ay as DatePickerTrigger, az as Dialog, aA as DialogClose, aB as DialogContent, aC as DialogDescription, aD as DialogFooter, aE as DialogHeader, aF as DialogOverlay, aG as DialogPortal, aH as DialogTitle, aI as DialogTrigger, aJ as DropdownMenu, aK as DropdownMenuCheckboxItem, aL as DropdownMenuContent, aM as DropdownMenuGroup, aN as DropdownMenuItem, aO as DropdownMenuLabel, aP as DropdownMenuRadioGroup, aQ as DropdownMenuRadioItem, aR as DropdownMenuSeparator, aS as DropdownMenuShortcut, aT as DropdownMenuSub, aU as DropdownMenuSubContent, aV as DropdownMenuSubTrigger, aW as DropdownMenuTrigger, aX as Form, aY as FormControl, aZ as FormField, a_ as FormItem, a$ as FormLabel, b0 as FormMessage, b1 as FormSubmit, b2 as HoverCard, b3 as HoverCardContent, b4 as HoverCardTrigger, b5 as Input, b6 as InputGroup, b7 as InputGroupAddon, b8 as InputGroupInput, b9 as InputGroupText, ba as InputOTP, bb as InputOTPGroup, bc as InputOTPHiddenInput, bd as InputOTPSeparator, be as InputOTPSlot, bf as Kbd, bg as KbdGroup, bh as Label, bi as MemoPillSegmentedTabs, bj as Menubar, bk as MenubarCheckboxItem, bl as MenubarContent, bm as MenubarItem, bn as MenubarLabel, bo as MenubarMenu, bp as MenubarRadioGroup, bq as MenubarRadioItem, br as MenubarSeparator, bs as MenubarShortcut, bt as MenubarSub, bu as MenubarSubContent, bv as MenubarSubTrigger, bw as MenubarTrigger, bx as NavigationMenu, by as NavigationMenuContent, bz as NavigationMenuIndicator, bA as NavigationMenuItem, bB as NavigationMenuLink, bC as NavigationMenuList, bD as NavigationMenuTrigger, bE as NavigationMenuViewport, bF as NumberField, bG as NumberFieldProps, bH as Pagination, bI as PaginationContent, bJ as PaginationEllipsis, bK as PaginationItem, bL as PaginationLink, bM as PaginationNext, bN as PaginationPrevious, bO as PillSegmentedTab, bP as PillSegmentedTabs, bQ as PillSegmentedTabsProps, bR as Popover, bS as PopoverAnchor, bT as PopoverContent, bU as PopoverTrigger, bV as Progress, bW as RadioGroup, bX as RadioGroupItem, bY as Rating, bZ as RatingProps, b_ as RatingTone, b$ as ScrollArea, c0 as ScrollBar, c1 as Select, c2 as SelectContent, c3 as SelectGroup, c4 as SelectItem, c5 as SelectLabel, c6 as SelectScrollDownButton, c7 as SelectScrollUpButton, c8 as SelectSeparator, c9 as SelectTrigger, ca as SelectValue, cb as Separator, cc as Sheet, cd as SheetClose, ce as SheetContent, cf as SheetDescription, cg as SheetFooter, ch as SheetHeader, ci as SheetTitle, cj as SheetTrigger, ck as Shimmer, cl as Skeleton, cm as Slider, cn as Snippet, co as SnippetProps, cp as SnippetSize, cq as SnippetVariant, cr as Spinner, cs as Stepper, ct as StepperProps, cu as StepperStep, cv as Switch, cw as Table, cx as TableBody, cy as TableCaption, cz as TableCell, cA as TableFooter, cB as TableHead, cC as TableHeader, cD as TableRow, cE as TagInput, cF as TagInputProps, cG as TagInputSize, cH as TextShimmerProps, cI as Textarea, cJ as Toast, cK as ToastAction, cL as ToastClose, cM as ToastDescription, cN as ToastProps, cO as ToastProvider, cP as ToastTitle, cQ as ToastViewport, cR as Toaster, cS as Toggle, cT as ToggleGroup, cU as ToggleGroupItem, cV as Toolbar, cW as ToolbarButton, cX as ToolbarLink, cY as ToolbarSeparator, cZ as ToolbarToggleGroup, c_ as ToolbarToggleItem, c$ as Tooltip, d0 as TooltipContent, d1 as TooltipProvider, d2 as TooltipTrigger, d3 as alertVariants, d4 as badgeVariants, d5 as controlClass, d6 as controlSurfaceClass, d7 as formatPickerDate, d8 as navigationMenuTriggerStyle, d9 as overlayAnimationClass, da as overlayItemClass, db as overlayListPanelClass, dc as overlaySurfaceClass, dd as toast, de as toggleVariants, df as useToast } from './circular-progress-B9nnwzCu.cjs';
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ import { ToolCallMessagePartComponent } from '@assistant-ui/react';
6
6
  export { ActionBarPrimitive, AssistantRuntimeProvider, AttachmentAdapter, AuiIf, ComposerPrimitive, MessagePrimitive, ThreadPrimitive, useComposerRuntime, useMessageRuntime, useThread, useThreadRuntime } from '@assistant-ui/react';
7
7
  export { M as ModeToggle, a as ModeToggleProps, b as ModeToggleTheme, S as STUDIO_NAV_MODE, c as StudioModeSwitch, d as StudioModeSwitchProps, e as StudioNavMode, f as StudioSidebar, g as StudioSidebarProps, h as StudioWelcome, i as StudioWelcomeProps, T as TimbalChatShell, j as TimbalChatShellProps, k as TimbalMark, l as TimbalMarkProps, m as TimbalStudioShell, n as TimbalStudioShellProps } from './welcome-DU-4NTjZ.js';
8
8
  export { M as MarkdownText, T as THREAD_DEFAULT_MAX_WIDTH, a as ToolFallback, b as TooltipIconButton, c as TooltipIconButtonProps, W as WorkforceSelector, d as WorkforceSelectorProps, e as assistantMessageContentClass, f as assistantMessageRootClass, t as threadMessageColumnClass, u as useToolRunning, g as userMessageRootClass } from './layout-CuKeSY74.js';
9
- export { d as APP_KIT_AGENT_INSTRUCTIONS, u as AppBreadcrumbItem, e as AppChatPanel, f as AppChatPanelProps, g as AppConfirmDialog, h as AppConfirmDialogProps, i as AppCopilotContextValue, j as AppCopilotProvider, k as AppCopilotProviderProps, l as AppPageWidth, m as AppShell, n as AppShellChatControls, o as AppShellChatTrigger, p as AppShellChatTriggerProps, q as AppShellNavControls, r as AppShellProps, s as AppShellSidebarTrigger, t as AppShellSidebarTriggerProps, B as BreadcrumbEntry, v as Breadcrumbs, w as BreadcrumbsProps, C as CHART_PALETTE, x as COLOR_UTILITY_PREFIXES, y as ChartArtifactView, E as ChartPanel, F as ChartPanelProps, G as ChartSeries, H as ChartTooltipIndicator, I as ChartVariant, J as ConnectionRow, K as ConnectionRowList, L as ConnectionRowListProps, M as ConnectionRowProps, N as DangerZone, O as DangerZoneAction, P as DangerZoneActionProps, Q as DangerZoneProps, R as DataTable, T as DataTableColumn, U as DataTableProps, V as DataTableSort, W as DataTableSortDirection, X as DescriptionItem, Y as DescriptionList, Z as DescriptionListProps, _ as EmptyState, $ as EmptyStateProps, a0 as ExpandableSection, a1 as ExpandableSectionProps, a2 as Field, a3 as FieldInput, a4 as FieldInputProps, a5 as FieldProps, a6 as FieldRow, a7 as FieldRowProps, a8 as FieldSelect, a9 as FieldSelectProps, aa as FieldSwitch, ab as FieldSwitchProps, ac as FieldTextarea, ad as FieldTextareaProps, ae as FilterBar, af as FilterBarProps, ag as FilterDatePreset, ah as FilterDateRangeValue, ai as FilterDropdown, aj as FilterDropdownProps, ak as FilterField, al as FilterFieldDef, am as FilterFieldProps, an as FilterFieldType, ao as FilterNumericOperatorOption, ap as FilterNumericValue, aq as FilterSelectOption, ar as FilterValue, as as FilterValues, at as FloatingUnsavedChangesBar, au as FloatingUnsavedChangesBarProps, av as FormSection, aw as FormSectionProps, ax as HOUSE_RULES, ay as HouseRule, az as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aA as InfoCard, aB as InfoCardProps, aC as InfoCardTone, aD as IntegrationCard, aE as IntegrationCardProps, aF as IntegrationCardStatus, aG as IntegrationsEmptyState, aH as IntegrationsEmptyStateProps, aI as LineAreaChart, aJ as LineAreaChartProps, aK as LintFinding, aL as LintOptions, aM as LintResult, aN as LintSeverity, aO as MetricChartCard, aP as MetricChartCardProps, aQ as MetricChartMetric, aR as MetricRow, aS as MetricRowItem, aT as MetricRowProps, aU as MetricTile, aV as MetricTileProps, aW as NumericOperator, aX as Page, aY as PageHeader, aZ as PageHeaderProps, a_ as PageProps, a$ as PieChart, b0 as PieChartProps, b1 as PlanBadge, b2 as PlanBadgeProps, b3 as PlanBadgeTone, b4 as RESERVED_GRADIENT_TOKENS, b5 as RadarChart, b6 as RadarChartProps, b7 as RadialChart, b8 as RadialChartProps, b9 as ResourceCard, ba as ResourceCardProps, bb as ReviewResult, bc as SEMANTIC_COLOR_TOKENS, bd as SLOP_BUDGETS, be as SearchInput, bf as SearchInputProps, bg as Section, bh as SectionProps, bi as SemanticColorToken, bj as SettingsSection, bk as SettingsSectionHeader, bl as SettingsSectionHeaderProps, bm as SettingsSectionProps, bn as Sparkline, bo as SparklineProps, bp as Stack, bq as StackAlign, br as StackGap, bs as StackJustify, bt as StackProps, bu as StatTile, bv as StatTileProps, bw as StatTileTone, bx as StatusBadge, by as StatusBadgeProps, S as StatusBadgeTone, bz as StatusDot, bA as StatusDotProps, bB as StatusDotTone, bC as SubNav, bD as SubNavItem, bE as SubNavProps, bF as SurfaceCard, bG as SurfaceCardProps, bH as SurfaceCardTone, bI as SurfaceCardVariant, bJ as TAILWIND_PALETTE_COLORS, bK as THEME_AGENT_INSTRUCTIONS, bL as TIMBAL_THEME_PRESETS, bM as ThemeShadow, bN as ThemeToCssOptions, bO as ThemeTokenMap, bP as TimbalThemeIntent, bQ as TimbalThemePreset, bR as TimbalThemePresetId, bS as TimbalThemeStyle, bT as TimbalThemeStyleProps, bU as TimbalThemeTokens, bV as TimbalThemeTypography, bW as UI_REVIEW_AGENT_INSTRUCTIONS, bX as UseLiveQueryOptions, bY as UseLiveQueryResult, c5 as applyThemePreset, c6 as applyTimbalTheme, c7 as clearTimbalTheme, c8 as connectionRowListClass, c9 as createTimbalTheme, ca as ensureThemeFontLink, cb as flushBarCategoryGap, cc as flushLineAreaEdgeToEdge, cd as formatLintReport, ce as getStoredThemePreset, cf as getThemePreset, cg as lintGeneratedUi, ch as resolveChartMargin, ci as resolveTooltipCategory, cj as reviewGeneratedUi, ck as themeToCss, cl as useAppCopilotContext, cm as useAppShellChat, cn as useAppShellNav, co as useInterval, cp as useLiveQuery } from './chart-artifact-C2pZQsaP.js';
9
+ export { d as APP_KIT_AGENT_INSTRUCTIONS, u as AppBreadcrumbItem, e as AppChatPanel, f as AppChatPanelProps, g as AppConfirmDialog, h as AppConfirmDialogProps, i as AppCopilotContextValue, j as AppCopilotProvider, k as AppCopilotProviderProps, l as AppPageWidth, m as AppShell, n as AppShellChatControls, o as AppShellChatTrigger, p as AppShellChatTriggerProps, q as AppShellNavControls, r as AppShellProps, s as AppShellSidebarTrigger, t as AppShellSidebarTriggerProps, B as BreadcrumbEntry, v as Breadcrumbs, w as BreadcrumbsProps, C as CHART_PALETTE, x as COLOR_UTILITY_PREFIXES, y as ChartArtifactView, E as ChartPanel, F as ChartPanelProps, G as ChartSeries, H as ChartTooltipIndicator, I as ChartVariant, J as ConnectionRow, K as ConnectionRowList, L as ConnectionRowListProps, M as ConnectionRowProps, N as DangerZone, O as DangerZoneAction, P as DangerZoneActionProps, Q as DangerZoneProps, R as DataTable, T as DataTableColumn, U as DataTableProps, V as DataTableSort, W as DataTableSortDirection, X as DescriptionItem, Y as DescriptionList, Z as DescriptionListProps, _ as EmptyState, $ as EmptyStateProps, a0 as ExpandableSection, a1 as ExpandableSectionProps, a2 as Field, a3 as FieldInput, a4 as FieldInputProps, a5 as FieldProps, a6 as FieldRow, a7 as FieldRowProps, a8 as FieldSelect, a9 as FieldSelectProps, aa as FieldSwitch, ab as FieldSwitchProps, ac as FieldTextarea, ad as FieldTextareaProps, ae as FilterBar, af as FilterBarProps, ag as FilterDatePreset, ah as FilterDateRangeValue, ai as FilterDropdown, aj as FilterDropdownProps, ak as FilterField, al as FilterFieldDef, am as FilterFieldProps, an as FilterFieldType, ao as FilterNumericOperatorOption, ap as FilterNumericValue, aq as FilterSelectOption, ar as FilterValue, as as FilterValues, at as FloatingUnsavedChangesBar, au as FloatingUnsavedChangesBarProps, av as FormSection, aw as FormSectionProps, ax as HOUSE_RULES, ay as HouseRule, az as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aA as InfoCard, aB as InfoCardProps, aC as InfoCardTone, aD as IntegrationCard, aE as IntegrationCardProps, aF as IntegrationCardStatus, aG as IntegrationsEmptyState, aH as IntegrationsEmptyStateProps, aI as LineAreaChart, aJ as LineAreaChartProps, aK as LintFinding, aL as LintOptions, aM as LintResult, aN as LintSeverity, aO as MetricChartCard, aP as MetricChartCardProps, aQ as MetricChartMetric, aR as MetricRow, aS as MetricRowItem, aT as MetricRowProps, aU as MetricTile, aV as MetricTileProps, aW as NumericOperator, aX as Page, aY as PageHeader, aZ as PageHeaderProps, a_ as PageProps, a$ as PieChart, b0 as PieChartProps, b1 as PlanBadge, b2 as PlanBadgeProps, b3 as PlanBadgeTone, b4 as RESERVED_GRADIENT_TOKENS, b5 as RadarChart, b6 as RadarChartProps, b7 as RadialChart, b8 as RadialChartProps, b9 as ResourceCard, ba as ResourceCardProps, bb as ReviewResult, bc as SEMANTIC_COLOR_TOKENS, bd as SLOP_BUDGETS, be as SearchInput, bf as SearchInputProps, bg as Section, bh as SectionProps, bi as SemanticColorToken, bj as SettingsSection, bk as SettingsSectionHeader, bl as SettingsSectionHeaderProps, bm as SettingsSectionProps, bn as Sparkline, bo as SparklineProps, bp as Stack, bq as StackAlign, br as StackGap, bs as StackJustify, bt as StackProps, bu as StatTile, bv as StatTileProps, bw as StatTileTone, bx as StatusBadge, by as StatusBadgeProps, S as StatusBadgeTone, bz as StatusDot, bA as StatusDotProps, bB as StatusDotTone, bC as SubNav, bD as SubNavItem, bE as SubNavProps, bF as SurfaceCard, bG as SurfaceCardProps, bH as SurfaceCardTone, bI as SurfaceCardVariant, bJ as TAILWIND_PALETTE_COLORS, bK as THEME_AGENT_INSTRUCTIONS, bL as TIMBAL_THEME_PRESETS, bM as ThemeShadow, bN as ThemeToCssOptions, bO as ThemeTokenMap, bP as TimbalThemeIntent, bQ as TimbalThemePreset, bR as TimbalThemePresetId, bS as TimbalThemeStyle, bT as TimbalThemeStyleProps, bU as TimbalThemeTokens, bV as TimbalThemeTypography, bW as UI_REVIEW_AGENT_INSTRUCTIONS, bX as UseLiveQueryOptions, bY as UseLiveQueryResult, c5 as applyThemePreset, c6 as applyTimbalTheme, c7 as clearTimbalTheme, c8 as connectionRowListClass, c9 as createTimbalTheme, ca as ensureThemeFontLink, cb as flushBarCategoryGap, cc as flushLineAreaEdgeToEdge, cd as formatLintReport, ce as getStoredThemePreset, cf as getThemePreset, cg as lintGeneratedUi, ch as resolveChartMargin, ci as resolveTooltipCategory, cj as reviewGeneratedUi, ck as themeToCss, cl as useAppCopilotContext, cm as useAppShellChat, cn as useAppShellNav, co as useInterval, cp as useLiveQuery } from './chart-artifact-BYl5C-dk.js';
10
10
  export { A as AVATAR_PRIMARY_FALLBACK_CLASS, a as Avatar, b as AvatarFallback, c as AvatarImage, B as Banner, d as BannerProps, e as BannerSize, f as BannerTone, g as BannerVariant, h as Button, i as ButtonColor, K as Kanban, j as KanbanCardData, k as KanbanCardVariant, l as KanbanColumnData, m as KanbanDensity, n as KanbanDragHandleProps, o as KanbanLocation, p as KanbanMoveEvent, q as KanbanProps, r as KanbanRenderCardContext, s as KanbanTone, T as Timeline, t as TimelineItem, u as TimelineProps, v as TimelineSize, U as UntitledButton, w as UntitledButtonColor, x as UntitledButtonProps, y as UntitledButtonSize, z as avatarChartVariantClass, C as untitledButtonVariants } from './kanban-FFBeaZPS.js';
11
11
  import React__default, { FC, ReactNode } from 'react';
12
12
  export { A as Accordion, a as AccordionContent, b as AccordionItem, c as AccordionTrigger, d as Alert, e as AlertDescription, f as AlertDialog, g as AlertDialogAction, h as AlertDialogCancel, i as AlertDialogContent, j as AlertDialogDescription, k as AlertDialogFooter, l as AlertDialogHeader, m as AlertDialogOverlay, n as AlertDialogPortal, o as AlertDialogTitle, p as AlertDialogTrigger, q as AlertTitle, r as AspectRatio, s as AvatarGroup, t as AvatarGroupProps, B as Badge, u as Breadcrumb, v as BreadcrumbEllipsis, w as BreadcrumbItem, x as BreadcrumbLink, y as BreadcrumbList, z as BreadcrumbPage, C as BreadcrumbSeparator, D as CONTROL_SIZE, E as Calendar, F as CalendarDayButton, G as Card, H as CardContent, I as CardDescription, J as CardFooter, K as CardHeader, L as CardTitle, M as Checkbox, N as CircularProgress, O as CircularProgressProps, P as Collapsible, Q as CollapsibleContent, R as CollapsibleTrigger, S as Combobox, T as ComboboxAnchor, U as ComboboxCommand, V as ComboboxContent, W as ComboboxEmpty, X as ComboboxGroup, Y as ComboboxInput, Z as ComboboxItem, _ as ComboboxList, $ as ComboboxSeparator, a0 as ComboboxShortcut, a1 as ComboboxTrigger, a2 as Command, a3 as CommandDialog, a4 as CommandEmpty, a5 as CommandGroup, a6 as CommandInput, a7 as CommandItem, a8 as CommandList, a9 as CommandSeparator, aa as CommandShortcut, ab as ContextMenu, ac as ContextMenuCheckboxItem, ad as ContextMenuContent, ae as ContextMenuGroup, af as ContextMenuItem, ag as ContextMenuLabel, ah as ContextMenuRadioGroup, ai as ContextMenuRadioItem, aj as ContextMenuSeparator, ak as ContextMenuShortcut, al as ContextMenuSub, am as ContextMenuSubContent, an as ContextMenuSubTrigger, ao as ContextMenuTrigger, ap as ControlClassOptions, aq as ControlShape, ar as ControlSize, as as CopyButton, at as CopyButtonProps, au as DatePicker, av as DatePickerButton, aw as DatePickerCalendar, ax as DatePickerContent, ay as DatePickerTrigger, az as Dialog, aA as DialogClose, aB as DialogContent, aC as DialogDescription, aD as DialogFooter, aE as DialogHeader, aF as DialogOverlay, aG as DialogPortal, aH as DialogTitle, aI as DialogTrigger, aJ as DropdownMenu, aK as DropdownMenuCheckboxItem, aL as DropdownMenuContent, aM as DropdownMenuGroup, aN as DropdownMenuItem, aO as DropdownMenuLabel, aP as DropdownMenuRadioGroup, aQ as DropdownMenuRadioItem, aR as DropdownMenuSeparator, aS as DropdownMenuShortcut, aT as DropdownMenuSub, aU as DropdownMenuSubContent, aV as DropdownMenuSubTrigger, aW as DropdownMenuTrigger, aX as Form, aY as FormControl, aZ as FormField, a_ as FormItem, a$ as FormLabel, b0 as FormMessage, b1 as FormSubmit, b2 as HoverCard, b3 as HoverCardContent, b4 as HoverCardTrigger, b5 as Input, b6 as InputGroup, b7 as InputGroupAddon, b8 as InputGroupInput, b9 as InputGroupText, ba as InputOTP, bb as InputOTPGroup, bc as InputOTPHiddenInput, bd as InputOTPSeparator, be as InputOTPSlot, bf as Kbd, bg as KbdGroup, bh as Label, bi as MemoPillSegmentedTabs, bj as Menubar, bk as MenubarCheckboxItem, bl as MenubarContent, bm as MenubarItem, bn as MenubarLabel, bo as MenubarMenu, bp as MenubarRadioGroup, bq as MenubarRadioItem, br as MenubarSeparator, bs as MenubarShortcut, bt as MenubarSub, bu as MenubarSubContent, bv as MenubarSubTrigger, bw as MenubarTrigger, bx as NavigationMenu, by as NavigationMenuContent, bz as NavigationMenuIndicator, bA as NavigationMenuItem, bB as NavigationMenuLink, bC as NavigationMenuList, bD as NavigationMenuTrigger, bE as NavigationMenuViewport, bF as NumberField, bG as NumberFieldProps, bH as Pagination, bI as PaginationContent, bJ as PaginationEllipsis, bK as PaginationItem, bL as PaginationLink, bM as PaginationNext, bN as PaginationPrevious, bO as PillSegmentedTab, bP as PillSegmentedTabs, bQ as PillSegmentedTabsProps, bR as Popover, bS as PopoverAnchor, bT as PopoverContent, bU as PopoverTrigger, bV as Progress, bW as RadioGroup, bX as RadioGroupItem, bY as Rating, bZ as RatingProps, b_ as RatingTone, b$ as ScrollArea, c0 as ScrollBar, c1 as Select, c2 as SelectContent, c3 as SelectGroup, c4 as SelectItem, c5 as SelectLabel, c6 as SelectScrollDownButton, c7 as SelectScrollUpButton, c8 as SelectSeparator, c9 as SelectTrigger, ca as SelectValue, cb as Separator, cc as Sheet, cd as SheetClose, ce as SheetContent, cf as SheetDescription, cg as SheetFooter, ch as SheetHeader, ci as SheetTitle, cj as SheetTrigger, ck as Shimmer, cl as Skeleton, cm as Slider, cn as Snippet, co as SnippetProps, cp as SnippetSize, cq as SnippetVariant, cr as Spinner, cs as Stepper, ct as StepperProps, cu as StepperStep, cv as Switch, cw as Table, cx as TableBody, cy as TableCaption, cz as TableCell, cA as TableFooter, cB as TableHead, cC as TableHeader, cD as TableRow, cE as TagInput, cF as TagInputProps, cG as TagInputSize, cH as TextShimmerProps, cI as Textarea, cJ as Toast, cK as ToastAction, cL as ToastClose, cM as ToastDescription, cN as ToastProps, cO as ToastProvider, cP as ToastTitle, cQ as ToastViewport, cR as Toaster, cS as Toggle, cT as ToggleGroup, cU as ToggleGroupItem, cV as Toolbar, cW as ToolbarButton, cX as ToolbarLink, cY as ToolbarSeparator, cZ as ToolbarToggleGroup, c_ as ToolbarToggleItem, c$ as Tooltip, d0 as TooltipContent, d1 as TooltipProvider, d2 as TooltipTrigger, d3 as alertVariants, d4 as badgeVariants, d5 as controlClass, d6 as controlSurfaceClass, d7 as formatPickerDate, d8 as navigationMenuTriggerStyle, d9 as overlayAnimationClass, da as overlayItemClass, db as overlayListPanelClass, dc as overlaySurfaceClass, dd as toast, de as toggleVariants, df as useToast } from './circular-progress-B9nnwzCu.js';
package/dist/index.esm.js CHANGED
@@ -76,7 +76,7 @@ import {
76
76
  useAppShellNav,
77
77
  useInterval,
78
78
  useLiveQuery
79
- } from "./chunk-6SQMTBPL.esm.js";
79
+ } from "./chunk-UVPXH4MB.esm.js";
80
80
  import {
81
81
  THREAD_DEFAULT_MAX_WIDTH,
82
82
  assistantMessageContentClass,
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "@timbal-ai/timbal-react",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "React components and runtime for building Timbal chat and studio apps",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.esm.js",
8
8
  "types": "dist/index.d.ts",
9
+ "bin": {
10
+ "timbal-ui-lint": "dist/cli/timbal-ui-lint.mjs"
11
+ },
9
12
  "exports": {
10
13
  ".": {
11
14
  "types": "./dist/index.d.ts",
@@ -52,7 +55,7 @@
52
55
  "LICENSE"
53
56
  ],
54
57
  "scripts": {
55
- "build": "tsup",
58
+ "build": "tsup && tsup --config tsup.cli.config.ts",
56
59
  "build:watch": "tsup --watch",
57
60
  "clean": "rm -rf dist",
58
61
  "test": "bun test",
@@ -60,6 +63,7 @@
60
63
  "typecheck": "tsc --noEmit",
61
64
  "check:boundaries": "depcruise src --config .dependency-cruiser.cjs",
62
65
  "check:bundle": "bun run build && node scripts/check-bundle-sizes.mjs",
66
+ "check:deps": "node scripts/check-immer.mjs",
63
67
  "example:install": "npm install --prefix examples/mock-ui && npm install --prefix examples/app-kit",
64
68
  "example:mock": "node scripts/dev-linked.mjs examples/mock-ui vite",
65
69
  "example:app": "node scripts/dev-linked.mjs examples/app-kit vite",
@@ -90,7 +94,7 @@
90
94
  "motion": "^12.23.24",
91
95
  "radix-ui": "^1.4.3",
92
96
  "react-day-picker": "^10.0.1",
93
- "recharts": "^3.8.1",
97
+ "recharts": "3.8.1",
94
98
  "rehype-katex": "^7.0.1",
95
99
  "remark-gfm": "^4.0.1",
96
100
  "remark-math": "^6.0.0",
@@ -115,6 +119,12 @@
115
119
  "tsup": "^8.5.0",
116
120
  "typescript": "~5.8.3"
117
121
  },
122
+ "overrides": {
123
+ "immer": ">=11.0.1"
124
+ },
125
+ "resolutions": {
126
+ "immer": ">=11.0.1"
127
+ },
118
128
  "keywords": [
119
129
  "timbal",
120
130
  "react",