{
  "name": "Chart",
  "category": "Data Display",
  "subcategory": "Charts",
  "description": "Declarative chart wrapper over ECharts — 30+ types supported.",
  "thumbnail": "/preview-cards/chart.png",
  "hasDocsPage": true,
  "nugetPackage": "Lumeo.Charts",
  "files": [
    "UI/Chart/Chart.razor",
    "UI/Chart/ChartHelper.cs",
    "UI/Chart/ChartLabelHelper.cs",
    "UI/Chart/ChartPlaceholderFactory.cs",
    "UI/Chart/Charts/AreaChart.razor",
    "UI/Chart/Charts/BarChart.razor",
    "UI/Chart/Charts/BoxPlotChart.razor",
    "UI/Chart/Charts/CalendarHeatmapChart.razor",
    "UI/Chart/Charts/CandlestickChart.razor",
    "UI/Chart/Charts/DonutChart.razor",
    "UI/Chart/Charts/EffectScatterChart.razor",
    "UI/Chart/Charts/FunnelChart.razor",
    "UI/Chart/Charts/GaugeChart.razor",
    "UI/Chart/Charts/GeoMapChart.razor",
    "UI/Chart/Charts/GraphChart.razor",
    "UI/Chart/Charts/HeatmapChart.razor",
    "UI/Chart/Charts/LineChart.razor",
    "UI/Chart/Charts/LiquidFillChart.razor",
    "UI/Chart/Charts/MixedChart.razor",
    "UI/Chart/Charts/NightingaleChart.razor",
    "UI/Chart/Charts/ParallelChart.razor",
    "UI/Chart/Charts/PictorialBarChart.razor",
    "UI/Chart/Charts/PieChart.razor",
    "UI/Chart/Charts/PolarBarChart.razor",
    "UI/Chart/Charts/RadarChart.razor",
    "UI/Chart/Charts/RadialChart.razor",
    "UI/Chart/Charts/SankeyChart.razor",
    "UI/Chart/Charts/ScatterChart.razor",
    "UI/Chart/Charts/SunburstChart.razor",
    "UI/Chart/Charts/ThemeRiverChart.razor",
    "UI/Chart/Charts/TreeChart.razor",
    "UI/Chart/Charts/TreemapChart.razor",
    "UI/Chart/Charts/WaterfallChart.razor",
    "UI/Chart/Charts/WordCloudChart.razor",
    "UI/Chart/ChartSkeleton.razor",
    "UI/Chart/ChartSkeletonKind.cs",
    "UI/Chart/EChartOption.cs"
  ],
  "dependencies": [
    "gauge"
  ],
  "packageDependencies": [
    "Blazicons.Lucide"
  ],
  "cssVars": [
    "--color-background",
    "--color-border",
    "--color-destructive",
    "--color-muted",
    "--color-muted-foreground",
    "--radius"
  ],
  "api": {
    "name": "Chart",
    "category": "Data Display",
    "subcategory": "Charts",
    "description": "Declarative chart wrapper over ECharts — 30+ types supported.",
    "nugetPackage": "Lumeo.Charts",
    "files": [
      "UI/Chart/Chart.razor",
      "UI/Chart/ChartHelper.cs",
      "UI/Chart/ChartLabelHelper.cs",
      "UI/Chart/ChartPlaceholderFactory.cs",
      "UI/Chart/Charts/AreaChart.razor",
      "UI/Chart/Charts/BarChart.razor",
      "UI/Chart/Charts/BoxPlotChart.razor",
      "UI/Chart/Charts/CalendarHeatmapChart.razor",
      "UI/Chart/Charts/CandlestickChart.razor",
      "UI/Chart/Charts/DonutChart.razor",
      "UI/Chart/Charts/EffectScatterChart.razor",
      "UI/Chart/Charts/FunnelChart.razor",
      "UI/Chart/Charts/GaugeChart.razor",
      "UI/Chart/Charts/GeoMapChart.razor",
      "UI/Chart/Charts/GraphChart.razor",
      "UI/Chart/Charts/HeatmapChart.razor",
      "UI/Chart/Charts/LineChart.razor",
      "UI/Chart/Charts/LiquidFillChart.razor",
      "UI/Chart/Charts/MixedChart.razor",
      "UI/Chart/Charts/NightingaleChart.razor",
      "UI/Chart/Charts/ParallelChart.razor",
      "UI/Chart/Charts/PictorialBarChart.razor",
      "UI/Chart/Charts/PieChart.razor",
      "UI/Chart/Charts/PolarBarChart.razor",
      "UI/Chart/Charts/RadarChart.razor",
      "UI/Chart/Charts/RadialChart.razor",
      "UI/Chart/Charts/SankeyChart.razor",
      "UI/Chart/Charts/ScatterChart.razor",
      "UI/Chart/Charts/SunburstChart.razor",
      "UI/Chart/Charts/ThemeRiverChart.razor",
      "UI/Chart/Charts/TreeChart.razor",
      "UI/Chart/Charts/TreemapChart.razor",
      "UI/Chart/Charts/WaterfallChart.razor",
      "UI/Chart/Charts/WordCloudChart.razor",
      "UI/Chart/ChartSkeleton.razor",
      "UI/Chart/ChartSkeletonKind.cs",
      "UI/Chart/EChartOption.cs"
    ],
    "namespace": "Lumeo",
    "inheritsFrom": null,
    "implements": [
      "IAsyncDisposable"
    ],
    "parameters": [
      {
        "name": "Option",
        "type": "EChartOption?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "OptionJson",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Theme",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "EChartsSource",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Width",
        "type": "string",
        "default": "\"100%\"",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Height",
        "type": "string",
        "default": "\"350px\"",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "OnClick",
        "type": "EventCallback<ChartEventArgs>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "OnMouseOver",
        "type": "EventCallback<ChartEventArgs>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ShowLoading",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Group",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "AspectRatio",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "OnDoubleClick",
        "type": "EventCallback<ChartEventArgs>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "OnBrushSelected",
        "type": "EventCallback<ChartEventArgs>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "OnDataZoom",
        "type": "EventCallback<ChartEventArgs>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "OnInitError",
        "type": "EventCallback<string>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Class",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "AdditionalAttributes",
        "type": "Dictionary<string, object>?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": true
      },
      {
        "name": "IsLoading",
        "type": "bool",
        "default": null,
        "description": "When true, forces the loading state on top of the chart (consumer is refetching data). Independent of the JS-driven ECharts loading indicator controlled by ShowLoading.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ShowLoadingSkeleton",
        "type": "bool",
        "default": "true",
        "description": "When true (default), renders the loading state while the chart has not yet rendered for the first time, and whenever IsLoading is true. Set to false to opt out entirely.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "SkeletonKind",
        "type": "ChartSkeletonKind",
        "default": "ChartSkeletonKind.Generic",
        "description": "Shape family for the skeleton. Each chart wrapper presets a sensible default (bars for Bar, pie ring for Pie, etc.) — consumers only override when they want a different silhouette.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "SkeletonStyle",
        "type": "ChartSkeletonStyle",
        "default": "ChartSkeletonStyle.Phantom",
        "description": "Phantom (default) renders the real chart with synthesized placeholder data in a muted palette; ECharts animates the option swap to real data. Silhouette falls back to the legacy SVG overlay skeleton.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "PhantomCycleMs",
        "type": "int",
        "default": "1400",
        "description": "Phantom-mode refresh interval in milliseconds — each tick regenerates the placeholder dataset with a new random seed so bars/lines/slices visibly redistribute while loading. Defaults to 1400ms; clamped to ≥200ms.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "PhantomShapeOverride",
        "type": "PhantomShape?",
        "default": null,
        "description": "Explicit override for the phantom layout (category count, series count, stacking, area fill). When null (default), the shape is inferred from the real Option so the phantom mirrors the actual chart structure.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ShowLoadingLabel",
        "type": "bool",
        "default": "true",
        "description": "When true (default), overlays a small top-right pill containing a spinner and LoadingText on top of the active skeleton — a clear, always-on visual cue that the chart is loading. The pill renders independently of SkeletonStyle so phantom and silhouette both get the same hint. Set to false for bare visuals where the skeleton speaks for itself.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "LoadingText",
        "type": "string?",
        "default": null,
        "description": "Label rendered next to the spinner inside the loading pill. Set to null or empty to hide the text and show only the spinner. When not set, uses the active locale's \"Chart.Loading\" string (default: \"Loading…\").",
        "isCascading": false,
        "captureUnmatched": false
      }
    ],
    "events": [
      {
        "name": "OnClick",
        "type": "EventCallback<ChartEventArgs>",
        "description": null
      },
      {
        "name": "OnMouseOver",
        "type": "EventCallback<ChartEventArgs>",
        "description": null
      },
      {
        "name": "OnDoubleClick",
        "type": "EventCallback<ChartEventArgs>",
        "description": null
      },
      {
        "name": "OnBrushSelected",
        "type": "EventCallback<ChartEventArgs>",
        "description": null
      },
      {
        "name": "OnDataZoom",
        "type": "EventCallback<ChartEventArgs>",
        "description": null
      },
      {
        "name": "OnInitError",
        "type": "EventCallback<string>",
        "description": null
      }
    ],
    "enums": [],
    "records": [],
    "cssVars": [
      "--color-background",
      "--color-border",
      "--color-destructive",
      "--color-muted",
      "--color-muted-foreground",
      "--radius"
    ],
    "examples": [],
    "subComponents": {
      "AreaChart": {
        "componentName": "AreaChart",
        "fileName": "AreaChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Categories",
            "type": "List<string>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Series",
            "type": "List<ChartSeriesData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Smooth",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDots",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Stacked",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Gradient",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Area",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Area for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelStrategy",
            "type": "ChartLabelStrategy",
            "default": "ChartLabelStrategy.Smart",
            "description": "Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelRotate",
            "type": "int?",
            "default": null,
            "description": "Manual rotation override (degrees) for category-axis labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkLine",
            "type": "EChartMarkLine?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkPoint",
            "type": "EChartMarkPoint?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkArea",
            "type": "EChartMarkArea?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "BarChart": {
        "componentName": "BarChart",
        "fileName": "BarChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Categories",
            "type": "List<string>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Series",
            "type": "List<ChartSeriesData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Horizontal",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Stacked",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Bars",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Bars for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelStrategy",
            "type": "ChartLabelStrategy",
            "default": "ChartLabelStrategy.Smart",
            "description": "Controls how category-axis labels are rendered. Smart (default): show every label; auto-rotate -30° at 11–16 categories, -60° at 17–24, -75° above 24. ShowAll: force every label with no rotation (may overlap on dense datasets). Auto: let ECharts auto-thin labels that would overlap (original ECharts default — hides every second label when crowded).",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelRotate",
            "type": "int?",
            "default": null,
            "description": "Manual override for category-label rotation in degrees (e.g. -30, -45). When set, takes precedence over the rotation implied by LabelStrategy.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkLine",
            "type": "EChartMarkLine?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkPoint",
            "type": "EChartMarkPoint?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkArea",
            "type": "EChartMarkArea?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [
          {
            "name": "BarLabelStrategy",
            "values": [
              "Smart",
              "ShowAll",
              "Auto"
            ],
            "description": "Back-compat alias for ChartLabelStrategy. The enum was originally defined per-chart as BarChart.BarLabelStrategy; it now lives in the shared ChartLabelHelper. Existing references continue to compile via this alias."
          }
        ],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "BoxPlotChart": {
        "componentName": "BoxPlotChart",
        "fileName": "BoxPlotChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Categories",
            "type": "List<string>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Data",
            "type": "List<double[]>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Bars",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Bars for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelStrategy",
            "type": "ChartLabelStrategy",
            "default": "ChartLabelStrategy.Smart",
            "description": "Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelRotate",
            "type": "int?",
            "default": null,
            "description": "Manual rotation override (degrees) for category-axis labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkLine",
            "type": "EChartMarkLine?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkPoint",
            "type": "EChartMarkPoint?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkArea",
            "type": "EChartMarkArea?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "CalendarHeatmapChart": {
        "componentName": "CalendarHeatmapChart",
        "fileName": "CalendarHeatmapChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Data",
            "type": "object?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Year",
            "type": "int",
            "default": "DateTime.Now.Year",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "VisualMapMin",
            "type": "double",
            "default": "0",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "VisualMapMax",
            "type": "double",
            "default": "10",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Grid",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Grid for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"inside\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "CandlestickChart": {
        "componentName": "CandlestickChart",
        "fileName": "CandlestickChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Categories",
            "type": "List<string>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Data",
            "type": "List<double[]>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Candlestick",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Candlestick for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelStrategy",
            "type": "ChartLabelStrategy",
            "default": "ChartLabelStrategy.Smart",
            "description": "Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelRotate",
            "type": "int?",
            "default": null,
            "description": "Manual rotation override (degrees) for category-axis labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkLine",
            "type": "EChartMarkLine?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkPoint",
            "type": "EChartMarkPoint?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkArea",
            "type": "EChartMarkArea?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "ChartSkeleton": {
        "componentName": "ChartSkeleton",
        "fileName": "ChartSkeleton.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Kind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Generic",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "DonutChart": {
        "componentName": "DonutChart",
        "fileName": "DonutChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Data",
            "type": "List<DonutChartData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLabels",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "InnerRadius",
            "type": "string",
            "default": "\"50%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OuterRadius",
            "type": "string",
            "default": "\"70%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "CenterLabel",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Pie",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"outside\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "EffectScatterChart": {
        "componentName": "EffectScatterChart",
        "fileName": "EffectScatterChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Series",
            "type": "List<EffectScatterSeriesData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Scatter",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Scatter for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkLine",
            "type": "EChartMarkLine?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkPoint",
            "type": "EChartMarkPoint?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkArea",
            "type": "EChartMarkArea?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "FunnelChart": {
        "componentName": "FunnelChart",
        "fileName": "FunnelChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Data",
            "type": "List<FunnelData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Sort",
            "type": "string",
            "default": "\"descending\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Funnel",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Funnel for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"inside\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "GaugeChart": {
        "componentName": "GaugeChart",
        "fileName": "GaugeChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Value",
            "type": "double",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Min",
            "type": "double",
            "default": "0",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Max",
            "type": "double",
            "default": "100",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Title",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Unit",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowPointer",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Ranges",
            "type": "List<GaugeRange>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Gauge",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Gauge for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "GeoMapChart": {
        "componentName": "GeoMapChart",
        "fileName": "GeoMapChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [
          "IAsyncDisposable"
        ],
        "parameters": [
          {
            "name": "MapName",
            "type": "string",
            "default": "\"world\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MapJson",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Data",
            "type": "object?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "VisualMapMin",
            "type": "double",
            "default": "0",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "VisualMapMax",
            "type": "double",
            "default": "100",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Roam",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Generic",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Generic for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"inside\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "GraphChart": {
        "componentName": "GraphChart",
        "fileName": "GraphChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Nodes",
            "type": "List<GraphNode>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Links",
            "type": "List<GraphLink>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Categories",
            "type": "List<GraphCategory>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Layout",
            "type": "string",
            "default": "\"force\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Roam",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Graph",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Graph for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"right\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "HeatmapChart": {
        "componentName": "HeatmapChart",
        "fileName": "HeatmapChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "XCategories",
            "type": "List<string>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "YCategories",
            "type": "List<string>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Data",
            "type": "List<double[]>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Grid",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Grid for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"inside\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkLine",
            "type": "EChartMarkLine?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkPoint",
            "type": "EChartMarkPoint?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkArea",
            "type": "EChartMarkArea?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "LineChart": {
        "componentName": "LineChart",
        "fileName": "LineChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Categories",
            "type": "List<string>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Series",
            "type": "List<ChartSeriesData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Smooth",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDots",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Line",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Line for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelStrategy",
            "type": "ChartLabelStrategy",
            "default": "ChartLabelStrategy.Smart",
            "description": "Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelRotate",
            "type": "int?",
            "default": null,
            "description": "Manual rotation override (degrees) for category-axis labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkLine",
            "type": "EChartMarkLine?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkPoint",
            "type": "EChartMarkPoint?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkArea",
            "type": "EChartMarkArea?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "LiquidFillChart": {
        "componentName": "LiquidFillChart",
        "fileName": "LiquidFillChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [
          "IAsyncDisposable"
        ],
        "parameters": [
          {
            "name": "Value",
            "type": "double",
            "default": "0.5",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Title",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Shape",
            "type": "string",
            "default": "\"circle\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Color",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "BackgroundColor",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "WaveAnimation",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "EChartsSource",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Pie",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"center\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "MixedChart": {
        "componentName": "MixedChart",
        "fileName": "MixedChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Categories",
            "type": "List<string>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Series",
            "type": "List<MixedSeriesData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Line",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Line for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelStrategy",
            "type": "ChartLabelStrategy",
            "default": "ChartLabelStrategy.Smart",
            "description": "Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelRotate",
            "type": "int?",
            "default": null,
            "description": "Manual rotation override (degrees) for category-axis labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkLine",
            "type": "EChartMarkLine?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkPoint",
            "type": "EChartMarkPoint?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkArea",
            "type": "EChartMarkArea?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "NightingaleChart": {
        "componentName": "NightingaleChart",
        "fileName": "NightingaleChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Data",
            "type": "List<NightingaleData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLabels",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Pie",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"outside\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "ParallelChart": {
        "componentName": "ParallelChart",
        "fileName": "ParallelChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Dimensions",
            "type": "List<ParallelDimension>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Series",
            "type": "List<ParallelSeriesData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Parallel",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Parallel for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "PictorialBarChart": {
        "componentName": "PictorialBarChart",
        "fileName": "PictorialBarChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Categories",
            "type": "List<string>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Series",
            "type": "List<PictorialSeriesData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Bars",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Bars for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelStrategy",
            "type": "ChartLabelStrategy",
            "default": "ChartLabelStrategy.Smart",
            "description": "Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelRotate",
            "type": "int?",
            "default": null,
            "description": "Manual rotation override (degrees) for category-axis labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkLine",
            "type": "EChartMarkLine?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkPoint",
            "type": "EChartMarkPoint?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkArea",
            "type": "EChartMarkArea?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "PieChart": {
        "componentName": "PieChart",
        "fileName": "PieChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Data",
            "type": "List<PieChartData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLabels",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Pie",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"outside\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "PolarBarChart": {
        "componentName": "PolarBarChart",
        "fileName": "PolarBarChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Categories",
            "type": "List<string>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Values",
            "type": "List<double>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "false",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Polar",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Polar for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"inside\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "RadarChart": {
        "componentName": "RadarChart",
        "fileName": "RadarChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Indicators",
            "type": "List<RadarIndicator>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Series",
            "type": "List<RadarSeriesData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Shape",
            "type": "string",
            "default": "\"polygon\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "FillArea",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Radar",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Radar for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "RadialChart": {
        "componentName": "RadialChart",
        "fileName": "RadialChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Value",
            "type": "double",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Min",
            "type": "double",
            "default": "0",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Max",
            "type": "double",
            "default": "100",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Label",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Color",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowProgress",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Pie",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "SankeyChart": {
        "componentName": "SankeyChart",
        "fileName": "SankeyChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Nodes",
            "type": "List<SankeyNode>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Links",
            "type": "List<SankeyLink>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Sankey",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Sankey for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"right\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "ScatterChart": {
        "componentName": "ScatterChart",
        "fileName": "ScatterChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Series",
            "type": "List<ScatterSeriesData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "BubbleSize",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Scatter",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Scatter for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkLine",
            "type": "EChartMarkLine?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkPoint",
            "type": "EChartMarkPoint?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkArea",
            "type": "EChartMarkArea?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "SunburstChart": {
        "componentName": "SunburstChart",
        "fileName": "SunburstChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Data",
            "type": "List<SunburstData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Pie",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"inside\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "ThemeRiverChart": {
        "componentName": "ThemeRiverChart",
        "fileName": "ThemeRiverChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Data",
            "type": "List<string[]>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Generic",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Generic for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "TreeChart": {
        "componentName": "TreeChart",
        "fileName": "TreeChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Data",
            "type": "TreeData?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Orient",
            "type": "string",
            "default": "\"LR\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLabels",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Tree",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Tree for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"right\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "TreemapChart": {
        "componentName": "TreemapChart",
        "fileName": "TreemapChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Data",
            "type": "List<TreemapData>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Grid",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Grid for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"inside\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "WaterfallChart": {
        "componentName": "WaterfallChart",
        "fileName": "WaterfallChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [],
        "parameters": [
          {
            "name": "Categories",
            "type": "List<string>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Values",
            "type": "List<double>",
            "default": "new()",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "IncreaseColor",
            "type": "string",
            "default": "\"var(--color-primary)\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DecreaseColor",
            "type": "string",
            "default": "\"var(--color-destructive)\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLegend",
            "type": "bool",
            "default": "false",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Colors",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Bars",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Bars for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"top\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelStrategy",
            "type": "ChartLabelStrategy",
            "default": "ChartLabelStrategy.Smart",
            "description": "Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelRotate",
            "type": "int?",
            "default": null,
            "description": "Manual rotation override (degrees) for category-axis labels.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "DataZoom",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkLine",
            "type": "EChartMarkLine?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkPoint",
            "type": "EChartMarkPoint?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "MarkArea",
            "type": "EChartMarkArea?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      },
      "WordCloudChart": {
        "componentName": "WordCloudChart",
        "fileName": "WordCloudChart.razor",
        "namespace": "Lumeo",
        "inheritsFrom": null,
        "implements": [
          "IAsyncDisposable"
        ],
        "parameters": [
          {
            "name": "Words",
            "type": "List<WordCloudItem>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Shape",
            "type": "string",
            "default": "\"circle\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SizeRange",
            "type": "int[]",
            "default": "new[] { 12, 60 }",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "RotationRange",
            "type": "int[]",
            "default": "new[] { -90, 90 }",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "EChartsSource",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowTooltip",
            "type": "bool",
            "default": "true",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Width",
            "type": "string",
            "default": "\"100%\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Height",
            "type": "string",
            "default": "\"350px\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Theme",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Class",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AdditionalAttributes",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": true
          },
          {
            "name": "IsLoading",
            "type": "bool",
            "default": null,
            "description": "When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingSkeleton",
            "type": "bool",
            "default": "true",
            "description": "When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonKind",
            "type": "ChartSkeletonKind",
            "default": "ChartSkeletonKind.Generic",
            "description": "Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Generic for this chart type; override if you want a different silhouette.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "SkeletonStyle",
            "type": "ChartSkeletonStyle",
            "default": "ChartSkeletonStyle.Phantom",
            "description": "Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "PhantomCycleMs",
            "type": "int",
            "default": "1400",
            "description": "Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowLoadingLabel",
            "type": "bool",
            "default": "true",
            "description": "When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle.",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LoadingText",
            "type": "string?",
            "default": "\"Loading…\"",
            "description": "Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".",
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ShowDataLabels",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelPosition",
            "type": "string",
            "default": "\"center\"",
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "LabelFormat",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "Toolbox",
            "type": "bool",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "ColorPalette",
            "type": "List<string>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationDuration",
            "type": "int?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "AnimationEasing",
            "type": "string?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          },
          {
            "name": "OptionOverride",
            "type": "Dictionary<string, object>?",
            "default": null,
            "description": null,
            "isCascading": false,
            "captureUnmatched": false
          }
        ],
        "events": [],
        "enums": [],
        "records": [],
        "parseFailed": false,
        "parseError": null
      }
    },
    "parseFailed": false,
    "parseError": null
  },
  "$schema": "https://lumeo.nativ.sh/registry-component-schema.json",
  "key": "chart",
  "docsUrl": "https://lumeo.nativ.sh/components/chart",
  "sourceUrl": [
    {
      "file": "UI/Chart/Chart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Chart.razor"
    },
    {
      "file": "UI/Chart/ChartHelper.cs",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/ChartHelper.cs"
    },
    {
      "file": "UI/Chart/ChartLabelHelper.cs",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/ChartLabelHelper.cs"
    },
    {
      "file": "UI/Chart/ChartPlaceholderFactory.cs",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/ChartPlaceholderFactory.cs"
    },
    {
      "file": "UI/Chart/Charts/AreaChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/AreaChart.razor"
    },
    {
      "file": "UI/Chart/Charts/BarChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/BarChart.razor"
    },
    {
      "file": "UI/Chart/Charts/BoxPlotChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/BoxPlotChart.razor"
    },
    {
      "file": "UI/Chart/Charts/CalendarHeatmapChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/CalendarHeatmapChart.razor"
    },
    {
      "file": "UI/Chart/Charts/CandlestickChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/CandlestickChart.razor"
    },
    {
      "file": "UI/Chart/Charts/DonutChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/DonutChart.razor"
    },
    {
      "file": "UI/Chart/Charts/EffectScatterChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/EffectScatterChart.razor"
    },
    {
      "file": "UI/Chart/Charts/FunnelChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/FunnelChart.razor"
    },
    {
      "file": "UI/Chart/Charts/GaugeChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/GaugeChart.razor"
    },
    {
      "file": "UI/Chart/Charts/GeoMapChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/GeoMapChart.razor"
    },
    {
      "file": "UI/Chart/Charts/GraphChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/GraphChart.razor"
    },
    {
      "file": "UI/Chart/Charts/HeatmapChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/HeatmapChart.razor"
    },
    {
      "file": "UI/Chart/Charts/LineChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/LineChart.razor"
    },
    {
      "file": "UI/Chart/Charts/LiquidFillChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/LiquidFillChart.razor"
    },
    {
      "file": "UI/Chart/Charts/MixedChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/MixedChart.razor"
    },
    {
      "file": "UI/Chart/Charts/NightingaleChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/NightingaleChart.razor"
    },
    {
      "file": "UI/Chart/Charts/ParallelChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/ParallelChart.razor"
    },
    {
      "file": "UI/Chart/Charts/PictorialBarChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/PictorialBarChart.razor"
    },
    {
      "file": "UI/Chart/Charts/PieChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/PieChart.razor"
    },
    {
      "file": "UI/Chart/Charts/PolarBarChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/PolarBarChart.razor"
    },
    {
      "file": "UI/Chart/Charts/RadarChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/RadarChart.razor"
    },
    {
      "file": "UI/Chart/Charts/RadialChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/RadialChart.razor"
    },
    {
      "file": "UI/Chart/Charts/SankeyChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/SankeyChart.razor"
    },
    {
      "file": "UI/Chart/Charts/ScatterChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/ScatterChart.razor"
    },
    {
      "file": "UI/Chart/Charts/SunburstChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/SunburstChart.razor"
    },
    {
      "file": "UI/Chart/Charts/ThemeRiverChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/ThemeRiverChart.razor"
    },
    {
      "file": "UI/Chart/Charts/TreeChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/TreeChart.razor"
    },
    {
      "file": "UI/Chart/Charts/TreemapChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/TreemapChart.razor"
    },
    {
      "file": "UI/Chart/Charts/WaterfallChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/WaterfallChart.razor"
    },
    {
      "file": "UI/Chart/Charts/WordCloudChart.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/Charts/WordCloudChart.razor"
    },
    {
      "file": "UI/Chart/ChartSkeleton.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/ChartSkeleton.razor"
    },
    {
      "file": "UI/Chart/ChartSkeletonKind.cs",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/ChartSkeletonKind.cs"
    },
    {
      "file": "UI/Chart/EChartOption.cs",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo.Charts/UI/Chart/EChartOption.cs"
    }
  ],
  "source": [
    {
      "path": "UI/Chart/Chart.razor",
      "content": "@namespace Lumeo\n@using Blazicons\n@implements IAsyncDisposable\n\n@if (_initError is not null)\n{\n    <div class=\"@($\"flex items-center justify-center text-sm text-destructive border border-destructive/30 rounded-md bg-destructive/5 {Class}\".Trim())\" style=\"@StyleStr\" @attributes=\"AdditionalAttributes\">\n        <span>@_initError</span>\n    </div>\n}\nelse\n{\n    <div class=\"@RootCssClass\" style=\"@RootStyleStr\" @attributes=\"AdditionalAttributes\">\n        <div id=\"@_chartId\" class=\"lumeo-chart-host w-full h-full\"\n             style=\"@HostStyle\"></div>\n        @if (ShouldShowSkeleton && SkeletonStyle == ChartSkeletonStyle.Silhouette)\n        {\n            @* Silhouette mode (legacy SVG skeleton overlay). Phantom mode renders the real\n               ECharts with placeholder data instead — no DOM element needed here. *@\n            <div style=\"position: absolute; inset: 0; pointer-events: none;\">\n                <ChartSkeleton Kind=\"@SkeletonKind\" Width=\"100%\" Height=\"100%\" Class=\"!rounded-none\" />\n            </div>\n        }\n        @if (ShouldShowSkeleton && ShowLoadingLabel)\n        {\n            @* Loading label — small top-right pill (spinner + text) overlaid on TOP of\n               whichever skeleton style is active. Tells users explicitly \"this is loading\"\n               so the cycling phantom data isn't mistaken for a bug. Doesn't block the\n               chart underneath (small, corner-aligned). Disable via ShowLoadingLabel=false\n               for bare phantom/silhouette visuals. *@\n            <div role=\"status\" aria-live=\"polite\" aria-label=\"@(LoadingText ?? L[\"Chart.Loading\"])\"\n                 class=\"absolute top-2 right-2 flex items-center gap-1.5 rounded-full border border-border/60 bg-background/85 backdrop-blur-sm px-2.5 py-1 shadow-sm pointer-events-none\">\n                <Blazicon Svg=\"Lucide.LoaderCircle\" class=\"h-3.5 w-3.5 text-muted-foreground animate-spin\" />\n                @{\n                    var _chartLoadingLabel = LoadingText ?? L[\"Chart.Loading\"];\n                }\n                @if (!string.IsNullOrEmpty(_chartLoadingLabel))\n                {\n                    <span class=\"text-xs font-medium text-muted-foreground\">@_chartLoadingLabel</span>\n                }\n            </div>\n        }\n    </div>\n}\n\n@inject ComponentInteropService Interop\n@inject Lumeo.Services.Localization.ILumeoLocalizer L\n\n@code {\n    [Parameter] public EChartOption? Option { get; set; }\n    [Parameter] public string? OptionJson { get; set; }\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? EChartsSource { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public EventCallback<ChartEventArgs> OnClick { get; set; }\n    [Parameter] public EventCallback<ChartEventArgs> OnMouseOver { get; set; }\n    [Parameter] public bool ShowLoading { get; set; }\n    [Parameter] public string? Group { get; set; }\n    [Parameter] public string? AspectRatio { get; set; }\n    [Parameter] public EventCallback<ChartEventArgs> OnDoubleClick { get; set; }\n    [Parameter] public EventCallback<ChartEventArgs> OnBrushSelected { get; set; }\n    [Parameter] public EventCallback<ChartEventArgs> OnDataZoom { get; set; }\n    [Parameter] public EventCallback<string> OnInitError { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    /// <summary>When true, forces the loading state on top of the chart\n    /// (consumer is refetching data). Independent of the JS-driven ECharts loading\n    /// indicator controlled by <see cref=\"ShowLoading\"/>.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders the loading state while the chart\n    /// has not yet rendered for the first time, and whenever <see cref=\"IsLoading\"/>\n    /// is true. Set to <c>false</c> to opt out entirely.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the skeleton. Each chart wrapper presets a sensible\n    /// default (bars for Bar, pie ring for Pie, etc.) — consumers only override when\n    /// they want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Generic;\n    /// <summary>Phantom (default) renders the real chart with synthesized placeholder\n    /// data in a muted palette; ECharts animates the option swap to real data.\n    /// Silhouette falls back to the legacy SVG overlay skeleton.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds — each tick regenerates\n    /// the placeholder dataset with a new random seed so bars/lines/slices visibly\n    /// redistribute while loading. Defaults to 1400ms; clamped to ≥200ms.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>Explicit override for the phantom layout (category count, series count,\n    /// stacking, area fill). When null (default), the shape is inferred from the real\n    /// <see cref=\"Option\"/> so the phantom mirrors the actual chart structure.</summary>\n    [Parameter] public PhantomShape? PhantomShapeOverride { get; set; }\n    /// <summary>When true (default), overlays a small top-right pill containing a\n    /// spinner and <see cref=\"LoadingText\"/> on top of the active skeleton — a clear,\n    /// always-on visual cue that the chart is loading. The pill renders independently\n    /// of <see cref=\"SkeletonStyle\"/> so phantom and silhouette both get the same\n    /// hint. Set to <c>false</c> for bare visuals where the skeleton speaks for\n    /// itself.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to\n    /// <c>null</c> or empty to hide the text and show only the spinner. When not set,\n    /// uses the active locale's \"Chart.Loading\" string (default: \"Loading…\").</summary>\n    [Parameter] public string? LoadingText { get; set; }\n\n    private readonly string _chartId = $\"lumeo-chart-{Guid.NewGuid():N}\";\n    private IJSObjectReference? _module;\n    private DotNetObjectReference<Chart>? _selfRef;\n    private bool _initialized;\n    private string? _lastJson;\n    private bool _lastShowLoading;\n    private string? _lastTheme;\n    private string? _lastGroup;\n    private string? _initError;\n\n    private string CssClass => $\"{Class}\".Trim();\n    private string StyleStr => AspectRatio is not null\n        ? $\"width: {Width}; aspect-ratio: {AspectRatio};\"\n        : $\"width: {Width}; height: {Height};\";\n\n    private string RootCssClass => $\"relative {Class}\".Trim();\n    private string RootStyleStr => $\"position: relative; {StyleStr}\";\n\n    /// <summary>True when we should be showing a loading state. Keyed strictly on\n    /// <c>IsLoading</c> — we do NOT show phantom/silhouette on initial mount, because\n    /// ECharts initializes synchronously and a ready chart should render its real data\n    /// on first paint. The loading state is reserved for explicit consumer refetches.</summary>\n    private bool ShouldShowSkeleton => ShowLoadingSkeleton && IsLoading;\n\n    // Silhouette mode hides the real chart while the SVG overlay takes its place.\n    // Phantom mode keeps the chart host fully visible — the \"skeleton\" IS the chart\n    // with placeholder data. To give phantom a \"loading\" look without polluting the\n    // option.color palette (which would bleed through the merge into the real chart),\n    // we apply a CSS grayscale+dim filter while phantom is active. The filter cleanly\n    // transitions off when real data arrives — bars keep their real hue throughout.\n    private string ChartHostOpacity => (ShouldShowSkeleton && SkeletonStyle == ChartSkeletonStyle.Silhouette) ? \"0\" : \"1\";\n\n    private bool IsPhantomStyled =>\n        ShouldShowSkeleton && SkeletonStyle == ChartSkeletonStyle.Phantom && ShowLoadingSkeleton;\n\n    private string HostStyle => IsPhantomStyled\n        ? $\"transition: opacity 350ms ease-out, filter 500ms ease-out; opacity: {ChartHostOpacity}; filter: grayscale(0.85) saturate(0.15) opacity(0.78);\"\n        : $\"transition: opacity 350ms ease-out, filter 500ms ease-out; opacity: {ChartHostOpacity}; filter: none;\";\n\n    // Phantom-mode state: cycling random placeholder data while loading, then one\n    // clean \"notMerge=true\" swap to real data so the muted palette doesn't bleed.\n    // Driven by a CancellationTokenSource + PeriodicTimer loop (replaces the older\n    // System.Threading.Timer) so DisposeAsync can deterministically cancel and\n    // await any in-flight callback before returning.\n    private CancellationTokenSource? _phantomCts;\n    private Task? _phantomLoopTask;\n    private int _phantomTick;\n    private bool _lastWasPhantom;\n    private int _activePhantomCycleMs;\n    private volatile bool _disposed;\n    private int EffectivePhantomCycleMs => Math.Max(200, PhantomCycleMs);\n\n    // Components whose phantom styling (muted palette, transparent text, skeleton-box\n    // labels) must be wiped during the phantom↔real transition — otherwise they'd\n    // persist under setOption merge semantics and bleed into the real chart.\n    private const string PhantomTransitionReplaceMergeJson =\n        \"[\\\"xAxis\\\",\\\"yAxis\\\",\\\"legend\\\",\\\"polar\\\",\\\"angleAxis\\\",\\\"radiusAxis\\\",\\\"radar\\\",\\\"parallel\\\",\\\"parallelAxis\\\",\\\"visualMap\\\",\\\"grid\\\"]\";\n\n    private bool IsPhantomActive =>\n        ShouldShowSkeleton && SkeletonStyle == ChartSkeletonStyle.Phantom && ShowLoadingSkeleton;\n\n    /// <summary>Returns the JSON that actually goes to ECharts. Phantom mode uses\n    /// the current cycling tick so each frame redistributes the random data, and\n    /// mirrors the real chart's structure (category count, series count, stacking)\n    /// via <see cref=\"ChartPlaceholderFactory.InferShape\"/>.</summary>\n    private string GetEffectiveJson()\n    {\n        if (IsPhantomActive)\n        {\n            var shape = PhantomShapeOverride ?? ChartPlaceholderFactory.InferShape(Option);\n            return ChartPlaceholderFactory.CreateJson(SkeletonKind, _phantomTick, shape);\n        }\n        return GetOptionsJson();\n    }\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (firstRender)\n        {\n            _module = await Interop.ImportModuleAsync(\"./_content/Lumeo.Charts/js/echarts-interop.js\");\n\n            var json = GetEffectiveJson();\n            _lastJson = json;\n            _lastTheme = Theme;\n            _lastGroup = Group;\n\n            try\n            {\n                await _module.InvokeVoidAsync(\"initChart\", _chartId, json, Theme, EChartsSource);\n            }\n            catch (JSException ex)\n            {\n                _initError = $\"Chart initialization failed: {ex.Message}\";\n                Console.Error.WriteLine($\"[Lumeo Chart] Init failed: {ex.Message}\");\n                if (OnInitError.HasDelegate)\n                    await OnInitError.InvokeAsync(ex.Message);\n                StateHasChanged();\n                return;\n            }\n            _initialized = true;\n\n            await RegisterChartEventsAsync();\n\n            if (ShowLoading)\n            {\n                _lastShowLoading = true;\n                await _module.InvokeVoidAsync(\"showLoading\", _chartId, null);\n            }\n\n            // Capture the phantom state that was actually passed to ECharts. Must be\n            // the value at init time (before any subsequent state change), so we can\n            // correctly decide notMerge on the next update.\n            _lastWasPhantom = IsPhantomActive;\n            UpdatePhantomTimer();\n            StateHasChanged();\n        }\n        else if (_initialized)\n        {\n            UpdatePhantomTimer();\n\n            var json = GetEffectiveJson();\n            var isPhantom = IsPhantomActive;\n            if (json != _lastJson)\n            {\n                _lastJson = json;\n                // Phantom↔real transition: keep notMerge=false so SERIES values tween\n                // (bars move from phantom heights to real heights — feels like data\n                // loading, not a snap). Use replaceMerge to fully replace layout\n                // components (axes, legend, radar, polar, parallel, visualMap) so the\n                // muted skeleton-box styling doesn't bleed into the real chart.\n                var phantomFlipped = _lastWasPhantom != isPhantom;\n                string? replaceMergeJson = phantomFlipped ? PhantomTransitionReplaceMergeJson : null;\n                await _module!.InvokeVoidAsync(\"updateChart\", _chartId, json, false, replaceMergeJson);\n            }\n            _lastWasPhantom = isPhantom;\n\n            if (ShowLoading != _lastShowLoading)\n            {\n                _lastShowLoading = ShowLoading;\n                if (ShowLoading)\n                    await _module!.InvokeVoidAsync(\"showLoading\", _chartId, null);\n                else\n                    await _module!.InvokeVoidAsync(\"hideLoading\", _chartId);\n            }\n\n            if (Theme != _lastTheme)\n            {\n                _lastTheme = Theme;\n                await _module!.InvokeVoidAsync(\"disposeChart\", _chartId);\n                var currentJson = _lastJson ?? GetEffectiveJson();\n                await _module!.InvokeVoidAsync(\"initChart\", _chartId, currentJson, Theme, EChartsSource);\n                await RegisterChartEventsAsync();\n            }\n\n            if (Group != _lastGroup)\n            {\n                _lastGroup = Group;\n                if (!string.IsNullOrEmpty(Group))\n                    await _module!.InvokeVoidAsync(\"connectCharts\", Group, new[] { _chartId });\n            }\n        }\n    }\n\n    private void UpdatePhantomTimer()\n    {\n        if (_disposed) return;\n        var cycle = EffectivePhantomCycleMs;\n        if (IsPhantomActive)\n        {\n            if (_phantomLoopTask is null || _activePhantomCycleMs != cycle)\n            {\n                StopPhantomLoop();\n                _activePhantomCycleMs = cycle;\n                _phantomCts = new CancellationTokenSource();\n                var token = _phantomCts.Token;\n                _phantomLoopTask = RunPhantomLoopAsync(cycle, token);\n            }\n        }\n        else if (_phantomLoopTask is not null)\n        {\n            StopPhantomLoop();\n            _activePhantomCycleMs = 0;\n        }\n    }\n\n    private void StopPhantomLoop()\n    {\n        try { _phantomCts?.Cancel(); } catch (ObjectDisposedException) { }\n        _phantomCts?.Dispose();\n        _phantomCts = null;\n        // Note: we deliberately do NOT await the loop task here to avoid blocking\n        // OnAfterRenderAsync. DisposeAsync awaits any final loop task to ensure\n        // no callback runs after dispose returns.\n        _phantomLoopTask = null;\n    }\n\n    private async Task RunPhantomLoopAsync(int cycle, CancellationToken token)\n    {\n        // PeriodicTimer ticks on a fixed cadence; cancellation breaks the loop\n        // immediately and lets DisposeAsync await completion deterministically.\n        using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(cycle));\n        try\n        {\n            while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))\n            {\n                if (_disposed || token.IsCancellationRequested) return;\n                _phantomTick++;\n                // Dispatch via SafeAsyncDispatcher so any outer-task fault (circuit\n                // disposed mid-tick) is caught — InvokeAsync itself can throw\n                // synchronously after dispose.\n                SafeAsyncDispatcher.FireAndForget(\n                    fn => InvokeAsync(fn),\n                    async () =>\n                    {\n                        if (_disposed || token.IsCancellationRequested) return;\n                        if (!_initialized || _module is null || !IsPhantomActive) return;\n                        var shape = PhantomShapeOverride ?? ChartPlaceholderFactory.InferShape(Option);\n                        var json = ChartPlaceholderFactory.CreateJson(SkeletonKind, _phantomTick, shape);\n                        _lastJson = json;\n                        try\n                        {\n                            await _module.InvokeVoidAsync(\"updateChart\", _chartId, json, false);\n                        }\n                        catch (JSDisconnectedException) { }\n                        catch (ObjectDisposedException) { }\n                    },\n                    \"Lumeo.Chart.Phantom\");\n            }\n        }\n        catch (OperationCanceledException) { /* expected on Dispose */ }\n    }\n\n    private async Task RegisterChartEventsAsync()\n    {\n        if (_module is null || !_initialized) return;\n\n        if (OnClick.HasDelegate || OnMouseOver.HasDelegate || OnDoubleClick.HasDelegate || OnBrushSelected.HasDelegate || OnDataZoom.HasDelegate)\n        {\n            _selfRef ??= DotNetObjectReference.Create(this);\n            if (OnClick.HasDelegate)\n                await _module.InvokeVoidAsync(\"registerChartEvent\", _chartId, \"click\", _selfRef);\n            if (OnMouseOver.HasDelegate)\n                await _module.InvokeVoidAsync(\"registerChartEvent\", _chartId, \"mouseover\", _selfRef);\n            if (OnDoubleClick.HasDelegate)\n                await _module.InvokeVoidAsync(\"registerChartEvent\", _chartId, \"dblclick\", _selfRef);\n            if (OnBrushSelected.HasDelegate)\n                await _module.InvokeVoidAsync(\"registerChartEvent\", _chartId, \"brushSelected\", _selfRef);\n            if (OnDataZoom.HasDelegate)\n                await _module.InvokeVoidAsync(\"registerChartEvent\", _chartId, \"dataZoom\", _selfRef);\n        }\n\n        if (!string.IsNullOrEmpty(Group))\n            await _module.InvokeVoidAsync(\"connectCharts\", Group, new[] { _chartId });\n    }\n\n    private string GetOptionsJson()\n    {\n        if (!string.IsNullOrEmpty(OptionJson)) return OptionJson;\n        try\n        {\n            return Option?.ToJson() ?? \"{}\";\n        }\n        catch (Exception ex)\n        {\n            Console.Error.WriteLine($\"[Lumeo Chart] Serialization failed: {ex.Message}\");\n            return \"{}\";\n        }\n    }\n\n    [JSInvokable]\n    public async Task OnChartEvent(string eventName, string dataJson)\n    {\n        var args = System.Text.Json.JsonSerializer.Deserialize<ChartEventArgs>(dataJson) ?? new();\n        if (eventName == \"click\") await OnClick.InvokeAsync(args);\n        else if (eventName == \"mouseover\") await OnMouseOver.InvokeAsync(args);\n        else if (eventName == \"dblclick\") await OnDoubleClick.InvokeAsync(args);\n        else if (eventName == \"brushSelected\") await OnBrushSelected.InvokeAsync(args);\n        else if (eventName == \"dataZoom\") await OnDataZoom.InvokeAsync(args);\n    }\n\n    public async Task<string?> GetImageAsync(string type = \"png\", int pixelRatio = 2)\n    {\n        if (_module is null || !_initialized) return null;\n        return await _module.InvokeAsync<string?>(\"getDataURL\", _chartId,\n            new { type, pixelRatio, backgroundColor = \"#fff\" });\n    }\n\n    public async Task AppendDataAsync(int seriesIndex, string dataJson)\n    {\n        if (_module is null || !_initialized) return;\n        await _module.InvokeVoidAsync(\"appendData\", _chartId, seriesIndex, dataJson);\n    }\n\n    public async Task DispatchActionAsync(string actionJson)\n    {\n        if (_module is null || !_initialized) return;\n        await _module.InvokeVoidAsync(\"dispatchAction\", _chartId, actionJson);\n    }\n\n    public async Task ResizeAsync()\n    {\n        if (_module is null || !_initialized) return;\n        await _module.InvokeVoidAsync(\"resizeChart\", _chartId);\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        // Set the disposed flag BEFORE any await so the loop's flag check can\n        // short-circuit even if Dispose runs concurrently with a tick.\n        _disposed = true;\n\n        // Cancel and await the phantom loop so no callback can run after this\n        // method returns — Timer.Dispose() did NOT give us this guarantee.\n        try { _phantomCts?.Cancel(); } catch (ObjectDisposedException) { }\n        var loop = _phantomLoopTask;\n        _phantomLoopTask = null;\n        if (loop is not null)\n        {\n            try { await loop.ConfigureAwait(false); }\n            catch (OperationCanceledException) { /* covers TaskCanceledException too */ }\n        }\n        _phantomCts?.Dispose();\n        _phantomCts = null;\n\n        if (_module is not null && _initialized)\n        {\n            try\n            {\n                await _module.InvokeVoidAsync(\"disposeChart\", _chartId);\n                await _module.DisposeAsync();\n            }\n            catch (JSDisconnectedException) { }\n        }\n        _selfRef?.Dispose();\n    }\n\n    public class ChartEventArgs\n    {\n        public string Name { get; set; } = \"\";\n        public string SeriesName { get; set; } = \"\";\n        public int SeriesIndex { get; set; } = -1;\n        public int DataIndex { get; set; } = -1;\n        public string Value { get; set; } = \"\";\n        public string ComponentType { get; set; } = \"\";\n    }\n}\n"
    },
    {
      "path": "UI/Chart/ChartHelper.cs",
      "content": "using System.Text.Json;\n\nnamespace Lumeo;\n\n/// <summary>\n/// Shared helpers for chart convenience components.\n/// </summary>\ninternal static class ChartHelper\n{\n    /// <summary>\n    /// Merges OptionOverride entries into the option's ExtensionData.\n    /// </summary>\n    public static void ApplyOptionOverride(EChartOption option, Dictionary<string, object>? overrides)\n    {\n        if (overrides is null || overrides.Count == 0) return;\n\n        option.ExtensionData ??= new Dictionary<string, JsonElement>();\n        foreach (var kvp in overrides)\n        {\n            var json = JsonSerializer.Serialize(kvp.Value);\n            var element = JsonSerializer.Deserialize<JsonElement>(json);\n            option.ExtensionData[kvp.Key] = element;\n        }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/ChartLabelHelper.cs",
      "content": "namespace Lumeo;\n\n/// <summary>Strategy for rendering category-axis labels on cartesian charts.</summary>\npublic enum ChartLabelStrategy\n{\n    /// <summary>Show every label; auto-rotate as density grows so labels stay readable.</summary>\n    Smart,\n    /// <summary>Force every label with no automatic rotation (may overlap on dense datasets).</summary>\n    ShowAll,\n    /// <summary>Let ECharts auto-thin overlapping labels (original default — hides some labels on dense sets).</summary>\n    Auto,\n}\n\n/// <summary>Shared helper used by every cartesian chart (Bar, Line, Area, Candlestick,\n/// BoxPlot, Waterfall, PictorialBar, Mixed) so category-axis label rendering is\n/// consistent across the library.</summary>\npublic static class ChartLabelHelper\n{\n    /// <summary>Computes the axisLabel interval + rotation for a category axis\n    /// given the chosen strategy, category count, and manual rotate override.\n    /// Returns (null, null) if no axisLabel customization is needed (strategy = Auto\n    /// and no manual rotate).</summary>\n    public static (int? interval, int? rotate) Resolve(\n        ChartLabelStrategy strategy,\n        int categoryCount,\n        int? manualRotate)\n    {\n        int? interval = null;\n        int? autoRotate = null;\n        switch (strategy)\n        {\n            case ChartLabelStrategy.Smart:\n                interval = 0;\n                autoRotate = categoryCount switch\n                {\n                    <= 10 => null,\n                    <= 16 => -30,\n                    <= 24 => -60,\n                    _ => -75,\n                };\n                break;\n            case ChartLabelStrategy.ShowAll:\n                interval = 0;\n                break;\n            case ChartLabelStrategy.Auto:\n                break;\n        }\n        return (interval, manualRotate ?? autoRotate);\n    }\n\n    /// <summary>Applies the strategy to the given axis, creating <see cref=\"EChartAxisLabel\"/>\n    /// when needed. Leaves the axis untouched for <see cref=\"ChartLabelStrategy.Auto\"/>\n    /// with no manual rotate.</summary>\n    public static void ApplyTo(EChartAxis axis, ChartLabelStrategy strategy, int categoryCount, int? manualRotate)\n    {\n        var (interval, rotate) = Resolve(strategy, categoryCount, manualRotate);\n        if (interval.HasValue || rotate.HasValue)\n        {\n            axis.AxisLabel ??= new EChartAxisLabel();\n            if (interval.HasValue) axis.AxisLabel.Interval = interval;\n            if (rotate.HasValue) axis.AxisLabel.Rotate = rotate;\n        }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/ChartPlaceholderFactory.cs",
      "content": "namespace Lumeo;\n\n/// <summary>Describes the layout the phantom should mimic — the real chart's category count,\n/// number of series, stacking, etc. When absent, defaults per chart kind are used.</summary>\npublic sealed record PhantomShape\n{\n    public int Categories { get; init; } = 8;\n    public int SeriesCount { get; init; } = 1;\n    public bool Stacked { get; init; }\n    public bool FilledArea { get; init; }\n    /// <summary>For pie/donut/funnel: slice count overrides <see cref=\"Categories\"/>.</summary>\n    public int? SliceCount { get; init; }\n}\n\n/// <summary>\n/// Builds \"phantom\" ECharts options — a real chart rendered with synthesized placeholder data\n/// in a muted palette. Each <c>Create(kind, tick, shape)</c> produces a fresh random variation\n/// seeded on <c>tick</c> so Chart.razor can cycle through frames every ~1.4s while loading.\n/// When a <see cref=\"PhantomShape\"/> is supplied, the placeholder mirrors the real chart's\n/// structure (same category count, same number of series, matching stack mode) — so the\n/// phantom looks like the real thing with randomized values.\n/// </summary>\ninternal static class ChartPlaceholderFactory\n{\n    private static readonly List<string> MutedPalette = new()\n    {\n        \"rgba(148, 163, 184, 0.55)\",\n        \"rgba(148, 163, 184, 0.40)\",\n        \"rgba(148, 163, 184, 0.28)\",\n        \"rgba(148, 163, 184, 0.20)\",\n        \"rgba(148, 163, 184, 0.14)\",\n    };\n\n    /// <summary>Derive a <see cref=\"PhantomShape\"/> from the real chart option so the phantom\n    /// mirrors the actual layout (category count, series count, stacking, area fill).</summary>\n    public static PhantomShape? InferShape(EChartOption? option)\n    {\n        if (option is null) return null;\n\n        int categories = option.XAxis?.FirstOrDefault()?.Data?.Count\n                         ?? option.YAxis?.FirstOrDefault()?.Data?.Count\n                         ?? 0;\n        int seriesCount = option.Series?.Count ?? 0;\n        bool stacked = option.Series?.Any(s => !string.IsNullOrEmpty(s.Stack)) ?? false;\n        bool filled = option.Series?.Any(s => s.AreaStyle is not null) ?? false;\n\n        int? sliceCount = null;\n        var firstSeries = option.Series?.FirstOrDefault();\n        if (firstSeries?.Type is \"pie\" or \"funnel\" or \"rosetype\")\n        {\n            if (firstSeries.Data is System.Collections.ICollection coll) sliceCount = coll.Count;\n        }\n\n        if (categories == 0 && seriesCount == 0 && sliceCount is null) return null;\n\n        return new PhantomShape\n        {\n            Categories = categories > 0 ? categories : 8,\n            SeriesCount = Math.Max(1, seriesCount),\n            Stacked = stacked,\n            FilledArea = filled,\n            SliceCount = sliceCount\n        };\n    }\n\n    public static EChartOption Create(ChartSkeletonKind kind, int tick = 0, PhantomShape? shape = null)\n    {\n        shape ??= new PhantomShape();\n        var rng = new Random(unchecked(kind.GetHashCode() * 397 ^ tick));\n\n        return kind switch\n        {\n            ChartSkeletonKind.Bars => BuildBars(rng, shape),\n            ChartSkeletonKind.Line => BuildLine(rng, shape, filled: shape.FilledArea),\n            ChartSkeletonKind.Area => BuildLine(rng, shape, filled: true),\n            ChartSkeletonKind.Pie => BuildPie(rng, shape),\n            ChartSkeletonKind.Scatter => BuildScatter(rng, shape),\n            ChartSkeletonKind.Grid => BuildHeatmap(rng, shape),\n            ChartSkeletonKind.Radar => BuildRadar(rng, shape),\n            ChartSkeletonKind.Sankey => BuildSankey(rng),\n            ChartSkeletonKind.Graph => BuildGraph(rng),\n            ChartSkeletonKind.Tree => BuildTree(rng),\n            ChartSkeletonKind.Parallel => BuildParallel(rng, shape),\n            ChartSkeletonKind.Funnel => BuildFunnel(rng, shape),\n            ChartSkeletonKind.Gauge => BuildGauge(rng),\n            ChartSkeletonKind.Polar => BuildPolar(rng, shape),\n            ChartSkeletonKind.Candlestick => BuildCandlestick(rng, shape),\n            _ => BuildBars(rng, shape)\n        };\n    }\n\n    public static string CreateJson(ChartSkeletonKind kind, int tick = 0, PhantomShape? shape = null) =>\n        Create(kind, tick, shape).ToJson();\n\n    // ---- builders ----\n\n    private static List<string> CategoryLabels(int count)\n    {\n        var list = new List<string>(count);\n        for (int i = 0; i < count; i++) list.Add(\" \");\n        return list;\n    }\n\n    private static EChartOption BuildBars(Random rng, PhantomShape shape)\n    {\n        int categories = Math.Max(2, shape.Categories);\n        int seriesCount = Math.Max(1, shape.SeriesCount);\n\n        var series = new List<EChartSeries>(seriesCount);\n        for (int s = 0; s < seriesCount; s++)\n        {\n            var values = new List<double>(categories);\n            for (int i = 0; i < categories; i++) values.Add(20 + rng.Next(80));\n            series.Add(new EChartSeries\n            {\n                Name = \"·\",\n                Type = \"bar\",\n                Data = values,\n                Stack = shape.Stacked ? \"_stack_\" : null,\n                BarMaxWidth = 28,\n                ItemStyle = new() { BorderRadius = 4 }\n            });\n        }\n\n        return new EChartOption\n        {\n            Grid = new EChartGrid { Left = \"3%\", Right = \"4%\", Bottom = \"8%\", Top = \"14%\", ContainLabel = true },\n            Legend = BuildLegendHint(seriesCount),\n            XAxis = new() { new EChartAxis { Type = \"category\", Data = CategoryLabels(categories), AxisLabel = SkeletonBoxAxisLabel() } },\n            YAxis = new() { new EChartAxis { Type = \"value\", AxisLabel = SkeletonBoxAxisLabel() } },\n            Series = series\n        };\n    }\n\n    private static EChartOption BuildLine(Random rng, PhantomShape shape, bool filled)\n    {\n        int categories = Math.Max(3, shape.Categories);\n        int seriesCount = Math.Max(1, shape.SeriesCount);\n\n        List<double> Walk(double start, double drift, double noise)\n        {\n            var result = new List<double>(categories);\n            double value = start;\n            for (int i = 0; i < categories; i++)\n            {\n                value += drift + (rng.NextDouble() - 0.5) * noise;\n                result.Add(Math.Round(Math.Clamp(value, 5, 95), 1));\n            }\n            return result;\n        }\n\n        var series = new List<EChartSeries>(seriesCount);\n        for (int s = 0; s < seriesCount; s++)\n        {\n            double start = 15 + s * 10;\n            series.Add(new EChartSeries\n            {\n                Name = \"·\",\n                Type = \"line\",\n                Data = Walk(start, 3 + s, 10 + s * 2),\n                Smooth = true,\n                ShowSymbol = false,\n                Stack = shape.Stacked ? \"_stack_\" : null,\n                LineStyle = new() { Width = 2 },\n                AreaStyle = filled ? new EChartAreaStyle { Opacity = 0.18 } : null\n            });\n        }\n\n        return new EChartOption\n        {\n            Grid = new EChartGrid { Left = \"3%\", Right = \"4%\", Bottom = \"8%\", Top = \"14%\", ContainLabel = true },\n            Legend = BuildLegendHint(seriesCount),\n            XAxis = new() { new EChartAxis { Type = \"category\", Data = CategoryLabels(categories), BoundaryGap = false, AxisLabel = SkeletonBoxAxisLabel() } },\n            YAxis = new() { new EChartAxis { Type = \"value\", AxisLabel = SkeletonBoxAxisLabel() } },\n            Series = series\n        };\n    }\n\n    private static EChartOption BuildPie(Random rng, PhantomShape shape)\n    {\n        int slices = shape.SliceCount ?? Math.Max(3, Math.Min(8, shape.Categories));\n        var weights = new double[slices];\n        double total = 0;\n        for (int i = 0; i < slices; i++) { weights[i] = 10 + rng.NextDouble() * 40; total += weights[i]; }\n\n        var data = new List<EChartPieData>(slices);\n        for (int i = 0; i < slices; i++)\n        {\n            data.Add(new EChartPieData { Name = \" \", Value = Math.Round(weights[i] / total * 100, 1) });\n        }\n\n        return new EChartOption\n        {\n            Legend = BuildLegendHint(slices),\n            Series = new()\n            {\n                new EChartSeries\n                {\n                    Name = \"·\",\n                    Type = \"pie\",\n                    Radius = \"58%\",\n                    Center = \"50%,58%\",\n                    Data = data,\n                    Label = new() { Show = false },\n                    ItemStyle = new() { BorderColor = \"rgba(0,0,0,0)\", BorderWidth = 2 }\n                }\n            }\n        };\n    }\n\n    private static EChartOption BuildScatter(Random rng, PhantomShape shape)\n    {\n        int points = Math.Max(8, Math.Min(60, shape.Categories > 8 ? shape.Categories * 2 : 20));\n        var pts = new List<double[]>(points);\n        for (int i = 0; i < points; i++)\n        {\n            pts.Add(new[] { Math.Round(5 + rng.NextDouble() * 90, 1), Math.Round(10 + rng.NextDouble() * 85, 1) });\n        }\n\n        return new EChartOption\n        {\n            Grid = new EChartGrid { Left = \"3%\", Right = \"4%\", Bottom = \"8%\", Top = \"14%\", ContainLabel = true },\n            Legend = BuildLegendHint(1),\n            XAxis = new() { new EChartAxis { Type = \"value\", AxisLabel = SkeletonBoxAxisLabel() } },\n            YAxis = new() { new EChartAxis { Type = \"value\", AxisLabel = SkeletonBoxAxisLabel() } },\n            Series = new()\n            {\n                new EChartSeries { Name = \"·\", Type = \"scatter\", Data = pts, SymbolSize = 12 }\n            }\n        };\n    }\n\n    private static EChartOption BuildHeatmap(Random rng, PhantomShape shape)\n    {\n        int cols = Math.Max(3, Math.Min(24, shape.Categories));\n        int rows = Math.Max(3, Math.Min(12, shape.SeriesCount > 1 ? shape.SeriesCount : 5));\n\n        var points = new List<int[]>(cols * rows);\n        for (int x = 0; x < cols; x++)\n        {\n            for (int y = 0; y < rows; y++)\n            {\n                points.Add(new[] { x, y, 15 + rng.Next(75) });\n            }\n        }\n\n        return new EChartOption\n        {\n            Grid = new EChartGrid { Left = \"3%\", Right = \"4%\", Bottom = \"8%\", Top = \"14%\", ContainLabel = true },\n            XAxis = new() { new EChartAxis { Type = \"category\", Data = CategoryLabels(cols), AxisLabel = SkeletonBoxAxisLabel() } },\n            YAxis = new() { new EChartAxis { Type = \"category\", Data = CategoryLabels(rows), AxisLabel = SkeletonBoxAxisLabel() } },\n            VisualMap = new EChartVisualMap\n            {\n                Min = 0,\n                Max = 100,\n                Show = false,\n                InRange = new EChartVisualMapInRange { Color = new() { \"rgba(148,163,184,0.15)\", \"rgba(148,163,184,0.55)\" } }\n            },\n            Series = new()\n            {\n                new EChartSeries { Name = \"·\", Type = \"heatmap\", Data = points, Label = new() { Show = false } }\n            }\n        };\n    }\n\n    // Muted box color for axis/legend skeleton rectangles — a shade lighter than series bars\n    // so the \"text boxes\" don't compete with the data but still read as placeholders.\n    private const string SkeletonBoxColor = \"rgba(148, 163, 184, 0.35)\";\n\n    /// <summary>Axis label styled as a skeleton box — label text is transparent but the\n    /// label's backgroundColor / padding / borderRadius draw a muted rectangle. Width\n    /// tuned for typical tick labels; consumers can override if needed.</summary>\n    private static EChartAxisLabel SkeletonBoxAxisLabel(int width = 22, int height = 8) => new()\n    {\n        Color = \"transparent\",\n        BackgroundColor = SkeletonBoxColor,\n        Padding = new[] { 0, 0, 0, 0 },\n        BorderRadius = 2,\n        Width = width,\n        Height = height\n    };\n\n    private static EChartLegend BuildLegendHint(int entries)\n    {\n        var data = new List<string>(entries);\n        for (int i = 0; i < entries; i++) data.Add(\" \");\n        return new EChartLegend\n        {\n            Data = data,\n            Top = \"3%\",\n            // Transparent text + muted pill per legend item reads as \"loading legend entries\".\n            TextStyle = new()\n            {\n                Color = \"transparent\",\n                BackgroundColor = SkeletonBoxColor,\n                BorderRadius = 3,\n                Width = 28,\n                Height = 8\n            }\n        };\n    }\n\n    // ---- dedicated phantom builders for complex chart types ----\n\n    private static EChartOption BuildRadar(Random rng, PhantomShape shape)\n    {\n        int dims = 5;\n        var indicators = new List<EChartRadarIndicator>(dims);\n        for (int i = 0; i < dims; i++) indicators.Add(new EChartRadarIndicator { Name = \" \", Max = 100 });\n\n        int seriesCount = Math.Max(1, Math.Min(3, shape.SeriesCount));\n        var radarData = new List<EChartRadarData>(seriesCount);\n        for (int s = 0; s < seriesCount; s++)\n        {\n            var values = new List<double>(dims);\n            for (int i = 0; i < dims; i++) values.Add(Math.Round(30 + rng.NextDouble() * 60, 1));\n            radarData.Add(new EChartRadarData { Name = \" \", Value = values, AreaStyle = new() { Opacity = 0.18 } });\n        }\n\n        return new EChartOption\n        {\n            Legend = BuildLegendHint(seriesCount),\n            Radar = new EChartRadar { Indicator = indicators, Shape = \"polygon\" },\n            Series = new()\n            {\n                new EChartSeries\n                {\n                    Name = \"·\",\n                    Type = \"radar\",\n                    Data = radarData\n                }\n            }\n        };\n    }\n\n    private static EChartOption BuildSankey(Random rng)\n    {\n        var nodes = new List<EChartSankeyNode>();\n        for (int i = 0; i < 7; i++) nodes.Add(new EChartSankeyNode { Name = $\"n{i}\" });\n\n        var links = new List<EChartSankeyLink>\n        {\n            new() { Source = \"n0\", Target = \"n3\", Value = Math.Round(20 + rng.NextDouble() * 30, 1) },\n            new() { Source = \"n0\", Target = \"n4\", Value = Math.Round(15 + rng.NextDouble() * 25, 1) },\n            new() { Source = \"n1\", Target = \"n3\", Value = Math.Round(10 + rng.NextDouble() * 20, 1) },\n            new() { Source = \"n1\", Target = \"n5\", Value = Math.Round(15 + rng.NextDouble() * 25, 1) },\n            new() { Source = \"n2\", Target = \"n4\", Value = Math.Round(20 + rng.NextDouble() * 30, 1) },\n            new() { Source = \"n3\", Target = \"n6\", Value = Math.Round(18 + rng.NextDouble() * 22, 1) },\n            new() { Source = \"n4\", Target = \"n6\", Value = Math.Round(15 + rng.NextDouble() * 25, 1) },\n            new() { Source = \"n5\", Target = \"n6\", Value = Math.Round(12 + rng.NextDouble() * 18, 1) },\n        };\n\n        return new EChartOption\n        {\n            Series = new()\n            {\n                new EChartSeries\n                {\n                    Name = \"·\",\n                    Type = \"sankey\",\n                    Left = \"5%\",\n                    Right = \"10%\",\n                    Top = \"5%\",\n                    Bottom = \"5%\",\n                    Nodes = nodes,\n                    Links = links,\n                    Label = new() { Show = false },\n                    ItemStyle = new() { BorderColor = \"rgba(0,0,0,0)\" }\n                }\n            }\n        };\n    }\n\n    private static EChartOption BuildGraph(Random rng)\n    {\n        int nodeCount = 10;\n        var nodes = new List<EChartGraphNode>();\n        for (int i = 0; i < nodeCount; i++)\n        {\n            nodes.Add(new EChartGraphNode\n            {\n                Name = $\"g{i}\",\n                X = Math.Round(rng.NextDouble() * 100, 1),\n                Y = Math.Round(rng.NextDouble() * 100, 1),\n                SymbolSize = 10 + rng.Next(18)\n            });\n        }\n\n        var links = new List<EChartGraphLink>();\n        for (int i = 0; i < nodeCount - 1; i++)\n        {\n            links.Add(new EChartGraphLink { Source = $\"g{i}\", Target = $\"g{i + 1}\" });\n        }\n        for (int extra = 0; extra < 4; extra++)\n        {\n            int a = rng.Next(nodeCount), b = rng.Next(nodeCount);\n            if (a != b) links.Add(new EChartGraphLink { Source = $\"g{a}\", Target = $\"g{b}\" });\n        }\n\n        return new EChartOption\n        {\n            Series = new()\n            {\n                new EChartSeries\n                {\n                    Name = \"·\",\n                    Type = \"graph\",\n                    Layout = \"none\",\n                    Nodes = nodes,\n                    Links = links,\n                    Roam = false,\n                    Label = new() { Show = false }\n                }\n            }\n        };\n    }\n\n    private static EChartOption BuildTree(Random rng)\n    {\n        // 3-level tree with random branching factor.\n        var root = new EChartTreeData\n        {\n            Name = \" \",\n            Children = new()\n            {\n                new() { Name = \" \", Children = new()\n                {\n                    new() { Name = \" \" },\n                    new() { Name = \" \" },\n                    new() { Name = \" \" }\n                }},\n                new() { Name = \" \", Children = new()\n                {\n                    new() { Name = \" \" },\n                    new() { Name = \" \" }\n                }},\n                new() { Name = \" \", Children = new()\n                {\n                    new() { Name = \" \" },\n                    new() { Name = \" \" },\n                    new() { Name = \" \" }\n                }}\n            }\n        };\n\n        return new EChartOption\n        {\n            Series = new()\n            {\n                new EChartSeries\n                {\n                    Name = \"·\",\n                    Type = \"tree\",\n                    Data = new List<EChartTreeData> { root },\n                    Orient = \"LR\",\n                    Left = \"8%\",\n                    Right = \"18%\",\n                    Top = \"5%\",\n                    Bottom = \"5%\",\n                    Label = new() { Show = false }\n                }\n            }\n        };\n    }\n\n    private static EChartOption BuildParallel(Random rng, PhantomShape shape)\n    {\n        int axes = Math.Max(3, Math.Min(8, shape.Categories > 3 ? shape.Categories : 5));\n        var parallelAxis = new List<EChartParallelAxis>(axes);\n        for (int i = 0; i < axes; i++) parallelAxis.Add(new EChartParallelAxis { Dim = i, Name = \" \", Min = 0, Max = 100 });\n\n        int lines = 8;\n        var data = new List<double[]>(lines);\n        for (int i = 0; i < lines; i++)\n        {\n            var row = new double[axes];\n            for (int j = 0; j < axes; j++) row[j] = Math.Round(10 + rng.NextDouble() * 80, 1);\n            data.Add(row);\n        }\n\n        return new EChartOption\n        {\n            Parallel = new EChartParallel { Left = \"6%\", Right = \"14%\", Top = \"10%\", Bottom = \"10%\" },\n            ParallelAxis = parallelAxis,\n            Series = new()\n            {\n                new EChartSeries\n                {\n                    Name = \"·\",\n                    Type = \"parallel\",\n                    Data = data\n                }\n            }\n        };\n    }\n\n    private static EChartOption BuildFunnel(Random rng, PhantomShape shape)\n    {\n        int steps = Math.Max(3, Math.Min(6, shape.SliceCount ?? 5));\n        var data = new List<EChartPieData>(steps);\n        double value = 100;\n        for (int i = 0; i < steps; i++)\n        {\n            value = Math.Max(8, value - (10 + rng.NextDouble() * 18));\n            data.Add(new EChartPieData { Name = \" \", Value = Math.Round(value, 1) });\n        }\n\n        return new EChartOption\n        {\n            Series = new()\n            {\n                new EChartSeries\n                {\n                    Name = \"·\",\n                    Type = \"funnel\",\n                    Left = \"10%\",\n                    Right = \"10%\",\n                    Top = \"8%\",\n                    Bottom = \"8%\",\n                    Data = data,\n                    Label = new() { Show = false },\n                    ItemStyle = new() { BorderColor = \"rgba(0,0,0,0)\", BorderWidth = 2 }\n                }\n            }\n        };\n    }\n\n    private static EChartOption BuildGauge(Random rng)\n    {\n        double value = Math.Round(10 + rng.NextDouble() * 80, 0);\n\n        return new EChartOption\n        {\n            Series = new()\n            {\n                new EChartSeries\n                {\n                    Name = \"·\",\n                    Type = \"gauge\",\n                    StartAngle = 210,\n                    EndAngle = -30,\n                    Min = 0,\n                    Max = 100,\n                    Progress = new EChartSeriesProgress { Show = true, Width = 10 },\n                    Pointer = new EChartPointer { Show = true },\n                    Detail = new EChartSeriesDetail { FontSize = 0, Formatter = \" \" },\n                    Data = new List<EChartPieData> { new() { Name = \" \", Value = value } }\n                }\n            }\n        };\n    }\n\n    private static EChartOption BuildPolar(Random rng, PhantomShape shape)\n    {\n        int bars = Math.Max(6, Math.Min(12, shape.Categories > 0 ? shape.Categories : 8));\n        var values = new List<double>(bars);\n        for (int i = 0; i < bars; i++) values.Add(20 + rng.Next(70));\n\n        return new EChartOption\n        {\n            Polar = new { radius = new[] { \"20%\", \"75%\" } },\n            AngleAxis = new() { new { type = \"category\", data = CategoryLabels(bars), axisLabel = new { color = \"transparent\", backgroundColor = SkeletonBoxColor, width = 18, height = 8, borderRadius = 2 } } },\n            RadiusAxis = new() { new { axisLabel = new { color = \"transparent\", backgroundColor = SkeletonBoxColor, width = 18, height = 8, borderRadius = 2 } } },\n            Series = new()\n            {\n                new EChartSeries\n                {\n                    Name = \"·\",\n                    Type = \"bar\",\n                    CoordinateSystem = \"polar\",\n                    Data = values,\n                    ItemStyle = new() { BorderRadius = 4 }\n                }\n            }\n        };\n    }\n\n    private static EChartOption BuildCandlestick(Random rng, PhantomShape shape)\n    {\n        int candles = Math.Max(8, Math.Min(40, shape.Categories > 0 ? shape.Categories : 16));\n        var data = new List<double[]>(candles);\n        double prev = 50;\n        for (int i = 0; i < candles; i++)\n        {\n            double open = prev;\n            double close = Math.Round(Math.Clamp(prev + (rng.NextDouble() - 0.5) * 18, 10, 90), 1);\n            double low = Math.Round(Math.Min(open, close) - rng.NextDouble() * 6, 1);\n            double high = Math.Round(Math.Max(open, close) + rng.NextDouble() * 6, 1);\n            data.Add(new[] { open, close, low, high });\n            prev = close;\n        }\n\n        return new EChartOption\n        {\n            Grid = new EChartGrid { Left = \"3%\", Right = \"4%\", Bottom = \"8%\", Top = \"10%\", ContainLabel = true },\n            XAxis = new() { new EChartAxis { Type = \"category\", Data = CategoryLabels(candles), AxisLabel = SkeletonBoxAxisLabel() } },\n            YAxis = new() { new EChartAxis { Type = \"value\", AxisLabel = SkeletonBoxAxisLabel() } },\n            Series = new()\n            {\n                new EChartSeries\n                {\n                    Name = \"·\",\n                    Type = \"candlestick\",\n                    Data = data\n                }\n            }\n        };\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/AreaChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<string> Categories { get; set; } = new();\n    [Parameter] public List<ChartSeriesData> Series { get; set; } = new();\n    [Parameter] public bool Smooth { get; set; }\n    [Parameter] public bool ShowDots { get; set; } = true;\n    [Parameter] public bool Stacked { get; set; }\n    [Parameter] public bool Gradient { get; set; } = true;\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Area</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Area;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n\n    /// <summary>Controls category-axis label rendering. Smart auto-rotates at higher\n    /// densities; ShowAll forces all horizontal; Auto thins overlapping labels.</summary>\n    [Parameter] public ChartLabelStrategy LabelStrategy { get; set; } = ChartLabelStrategy.Smart;\n\n    /// <summary>Manual rotation override (degrees) for category-axis labels.</summary>\n    [Parameter] public int? LabelRotate { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public EChartMarkLine? MarkLine { get; set; }\n    [Parameter] public EChartMarkPoint? MarkPoint { get; set; }\n    [Parameter] public EChartMarkArea? MarkArea { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var areaOpacity = Gradient ? 0.3 : 0.7;\n\n        var seriesList = Series.Select(s => new EChartSeries\n        {\n            Name = s.Name,\n            Type = \"line\",\n            Data = s.Values,\n            Smooth = Smooth ? true : null,\n            ShowSymbol = ShowDots ? null : false,\n            SymbolSize = ShowDots ? 6 : null,\n            Stack = Stacked ? \"total\" : null,\n            AreaStyle = new EChartAreaStyle { Opacity = areaOpacity }\n        }).ToList();\n\n        if (ShowDataLabels)\n            foreach (var s in seriesList)\n                s.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        if (seriesList.Count > 0)\n        {\n            if (MarkLine is not null) seriesList[0].MarkLine = MarkLine;\n            if (MarkPoint is not null) seriesList[0].MarkPoint = MarkPoint;\n            if (MarkArea is not null) seriesList[0].MarkArea = MarkArea;\n        }\n\n        var categoryAxis = new EChartAxis { Type = \"category\", Data = Categories, BoundaryGap = false };\n        ChartLabelHelper.ApplyTo(categoryAxis, LabelStrategy, Categories.Count, LabelRotate);\n\n        _option = new EChartOption\n        {\n            XAxis = new List<EChartAxis> { categoryAxis },\n            YAxis = new List<EChartAxis>\n            {\n                new() { Type = \"value\" }\n            },\n            Series = seriesList,\n            Grid = new EChartGrid\n            {\n                Left = \"3%\", Right = \"4%\",\n                Top = ShowLegend ? \"14%\" : \"5%\",\n                Bottom = DataZoom ? \"15%\" : \"3%\",\n                ContainLabel = true\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"axis\" };\n\n        if (ShowLegend && Series.Count > 0)\n            _option.Legend = new EChartLegend { Data = Series.Select(s => s.Name).ToList() };\n\n        if (DataZoom)\n            _option.DataZoom = new List<EChartDataZoom> { new() { Type = \"slider\", Show = true }, new() { Type = \"inside\" } };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true }, DataZoom = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class ChartSeriesData\n    {\n        public string Name { get; set; } = \"\";\n        public List<double> Values { get; set; } = new();\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/BarChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<string> Categories { get; set; } = new();\n    [Parameter] public List<ChartSeriesData> Series { get; set; } = new();\n    [Parameter] public bool Horizontal { get; set; }\n    [Parameter] public bool Stacked { get; set; }\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Bars</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Bars;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n\n    /// <summary>Controls how category-axis labels are rendered.\n    /// <list type=\"bullet\">\n    ///   <item><c>Smart</c> (default): show every label; auto-rotate -30° at 11–16 categories, -60° at 17–24, -75° above 24.</item>\n    ///   <item><c>ShowAll</c>: force every label with no rotation (may overlap on dense datasets).</item>\n    ///   <item><c>Auto</c>: let ECharts auto-thin labels that would overlap (original ECharts default — hides every second label when crowded).</item>\n    /// </list>\n    /// </summary>\n    [Parameter] public ChartLabelStrategy LabelStrategy { get; set; } = ChartLabelStrategy.Smart;\n\n    /// <summary>Manual override for category-label rotation in degrees (e.g. -30, -45). When set,\n    /// takes precedence over the rotation implied by <see cref=\"LabelStrategy\"/>.</summary>\n    [Parameter] public int? LabelRotate { get; set; }\n\n    /// <summary>Back-compat alias for <see cref=\"ChartLabelStrategy\"/>. The enum was\n    /// originally defined per-chart as <c>BarChart.BarLabelStrategy</c>; it now lives\n    /// in the shared <see cref=\"ChartLabelHelper\"/>. Existing references continue to\n    /// compile via this alias.</summary>\n    public enum BarLabelStrategy\n    {\n        Smart = ChartLabelStrategy.Smart,\n        ShowAll = ChartLabelStrategy.ShowAll,\n        Auto = ChartLabelStrategy.Auto,\n    }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public EChartMarkLine? MarkLine { get; set; }\n    [Parameter] public EChartMarkPoint? MarkPoint { get; set; }\n    [Parameter] public EChartMarkArea? MarkArea { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var categoryAxis = new EChartAxis { Type = \"category\", Data = Categories };\n        ChartLabelHelper.ApplyTo(categoryAxis, LabelStrategy, Categories.Count, LabelRotate);\n        var valueAxis = new EChartAxis { Type = \"value\" };\n\n        var seriesList = Series.Select(s => new EChartSeries\n        {\n            Name = s.Name,\n            Type = \"bar\",\n            Data = s.Values,\n            Stack = Stacked ? \"total\" : null\n        }).ToList();\n\n        // When stacked, the theme's rounded corners on every segment make\n        // the stack look like floating pills (each segment rounds its own\n        // 4 corners, so adjacent segments meet at the arc). We flatten\n        // the inner segments and round only the outermost end of the stack:\n        //\n        //   Vertical stacked   → last series (top) rounds top corners\n        //   Horizontal stacked → last series (right) rounds right corners\n        //\n        // Radius is derived from --radius (fallback 6 px if unavailable\n        // from the CSS side since we're in C#). Consumers who want a\n        // different value can override via OptionOverride on the series.\n        if (Stacked && seriesList.Count > 0)\n        {\n            const int r = 6;\n            int[] outerCorners = Horizontal\n                ? new[] { 0, r, r, 0 }   // round right side\n                : new[] { r, r, 0, 0 };  // round top side\n            int[] flat = { 0, 0, 0, 0 };\n\n            // Use the array form everywhere — ECharts merges theme + series\n            // itemStyle and a scalar `borderRadius: 0` doesn't reliably replace\n            // the theme's array default; an all-zero array always wins.\n            for (int i = 0; i < seriesList.Count; i++)\n            {\n                var isOuter = i == seriesList.Count - 1;\n                seriesList[i].ItemStyle = new EChartItemStyle\n                {\n                    BorderRadiusCorners = isOuter ? outerCorners : flat\n                };\n            }\n        }\n        else if (Horizontal)\n        {\n            // Non-stacked horizontal: theme default `[r, r, 0, 0]` rounds the\n            // TOP edge (rectangle corners), which looks wrong for a horizontal\n            // bar. Override so every bar's right end is rounded instead.\n            const int r = 6;\n            foreach (var s in seriesList)\n                s.ItemStyle = new EChartItemStyle { BorderRadiusCorners = new[] { 0, r, r, 0 } };\n        }\n\n        if (ShowDataLabels)\n            foreach (var s in seriesList)\n                s.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        if (seriesList.Count > 0)\n        {\n            if (MarkLine is not null) seriesList[0].MarkLine = MarkLine;\n            if (MarkPoint is not null) seriesList[0].MarkPoint = MarkPoint;\n            if (MarkArea is not null) seriesList[0].MarkArea = MarkArea;\n        }\n\n        _option = new EChartOption\n        {\n            XAxis = new List<EChartAxis> { Horizontal ? valueAxis : categoryAxis },\n            YAxis = new List<EChartAxis> { Horizontal ? categoryAxis : valueAxis },\n            Series = seriesList,\n            Grid = new EChartGrid\n            {\n                Left = \"3%\", Right = \"4%\",\n                Top = ShowLegend ? \"14%\" : \"5%\",\n                Bottom = DataZoom ? \"15%\" : \"3%\",\n                ContainLabel = true\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"axis\", AxisPointer = new EChartAxisPointer { Type = \"shadow\" } };\n\n        if (ShowLegend && Series.Count > 0)\n            _option.Legend = new EChartLegend { Data = Series.Select(s => s.Name).ToList() };\n\n        if (DataZoom)\n            _option.DataZoom = new List<EChartDataZoom> { new() { Type = \"slider\", Show = true }, new() { Type = \"inside\" } };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true }, DataZoom = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class ChartSeriesData\n    {\n        public string Name { get; set; } = \"\";\n        public List<double> Values { get; set; } = new();\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/BoxPlotChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<string> Categories { get; set; } = new();\n    [Parameter] public List<double[]> Data { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Bars</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Bars;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n\n    /// <summary>Controls category-axis label rendering. Smart auto-rotates at higher\n    /// densities; ShowAll forces all horizontal; Auto thins overlapping labels.</summary>\n    [Parameter] public ChartLabelStrategy LabelStrategy { get; set; } = ChartLabelStrategy.Smart;\n\n    /// <summary>Manual rotation override (degrees) for category-axis labels.</summary>\n    [Parameter] public int? LabelRotate { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public EChartMarkLine? MarkLine { get; set; }\n    [Parameter] public EChartMarkPoint? MarkPoint { get; set; }\n    [Parameter] public EChartMarkArea? MarkArea { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var series = new EChartSeries\n        {\n            Type = \"boxplot\",\n            Data = Data\n        };\n\n        if (ShowDataLabels)\n            series.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        if (MarkLine is not null) series.MarkLine = MarkLine;\n        if (MarkPoint is not null) series.MarkPoint = MarkPoint;\n        if (MarkArea is not null) series.MarkArea = MarkArea;\n\n        var categoryAxis = new EChartAxis { Type = \"category\", Data = Categories };\n        ChartLabelHelper.ApplyTo(categoryAxis, LabelStrategy, Categories.Count, LabelRotate);\n\n        _option = new EChartOption\n        {\n            XAxis = new List<EChartAxis> { categoryAxis },\n            YAxis = new List<EChartAxis> { new() { Type = \"value\" } },\n            Series = new List<EChartSeries> { series },\n            Grid = new EChartGrid\n            {\n                Left = \"3%\", Right = \"4%\",\n                Top = ShowLegend ? \"14%\" : \"5%\",\n                Bottom = DataZoom ? \"15%\" : \"3%\",\n                ContainLabel = true\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend)\n            _option.Legend = new EChartLegend();\n\n        if (DataZoom)\n            _option.DataZoom = new List<EChartDataZoom> { new() { Type = \"slider\", Show = true }, new() { Type = \"inside\" } };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true }, DataZoom = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/CalendarHeatmapChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public object? Data { get; set; }\n    [Parameter] public int Year { get; set; } = DateTime.Now.Year;\n    [Parameter] public double VisualMapMin { get; set; } = 0;\n    [Parameter] public double VisualMapMax { get; set; } = 10;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Grid</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Grid;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"inside\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var series = new EChartSeries\n        {\n            Type = \"heatmap\",\n            CoordinateSystem = \"calendar\",\n            CalendarIndex = 0,\n            Data = Data\n        };\n\n        if (ShowDataLabels)\n            series.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        var visualColors = Colors is { Count: > 0 }\n            ? Colors\n            : new List<string> { \"#ebedf0\", \"#c6e48b\", \"#7bc96f\", \"#239a3b\", \"#196127\" };\n\n        _option = new EChartOption\n        {\n            Calendar = new EChartCalendar\n            {\n                Range = Year.ToString(),\n                Left = \"10%\",\n                Right = \"5%\",\n                Top = \"15%\",\n                CellSize = 13\n            },\n            VisualMap = new EChartVisualMap\n            {\n                Min = VisualMapMin,\n                Max = VisualMapMax,\n                Calculable = true,\n                Orient = \"horizontal\",\n                Left = \"center\",\n                Top = \"3%\",\n                InRange = new EChartVisualMapInRange { Color = visualColors }\n            },\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/CandlestickChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<string> Categories { get; set; } = new();\n    [Parameter] public List<double[]> Data { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Candlestick</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Candlestick;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n\n    /// <summary>Controls category-axis label rendering. Smart auto-rotates at higher\n    /// densities; ShowAll forces all horizontal; Auto thins overlapping labels.</summary>\n    [Parameter] public ChartLabelStrategy LabelStrategy { get; set; } = ChartLabelStrategy.Smart;\n\n    /// <summary>Manual rotation override (degrees) for category-axis labels.</summary>\n    [Parameter] public int? LabelRotate { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public EChartMarkLine? MarkLine { get; set; }\n    [Parameter] public EChartMarkPoint? MarkPoint { get; set; }\n    [Parameter] public EChartMarkArea? MarkArea { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var series = new EChartSeries\n        {\n            Type = \"candlestick\",\n            Data = Data\n        };\n\n        if (ShowDataLabels)\n            series.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        if (MarkLine is not null) series.MarkLine = MarkLine;\n        if (MarkPoint is not null) series.MarkPoint = MarkPoint;\n        if (MarkArea is not null) series.MarkArea = MarkArea;\n\n        var categoryAxis = new EChartAxis { Type = \"category\", Data = Categories };\n        ChartLabelHelper.ApplyTo(categoryAxis, LabelStrategy, Categories.Count, LabelRotate);\n\n        _option = new EChartOption\n        {\n            XAxis = new List<EChartAxis> { categoryAxis },\n            YAxis = new List<EChartAxis> { new() { Type = \"value\" } },\n            Series = new List<EChartSeries> { series },\n            Grid = new EChartGrid\n            {\n                Left = \"3%\", Right = \"4%\",\n                Top = ShowLegend ? \"14%\" : \"5%\",\n                Bottom = DataZoom ? \"15%\" : \"3%\",\n                ContainLabel = true\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"axis\", AxisPointer = new EChartAxisPointer { Type = \"cross\" } };\n\n        if (ShowLegend)\n            _option.Legend = new EChartLegend();\n\n        if (DataZoom)\n            _option.DataZoom = new List<EChartDataZoom> { new() { Type = \"slider\", Show = true }, new() { Type = \"inside\" } };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true }, DataZoom = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/DonutChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<DonutChartData> Data { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public bool ShowLabels { get; set; } = true;\n    [Parameter] public string InnerRadius { get; set; } = \"50%\";\n    [Parameter] public string OuterRadius { get; set; } = \"70%\";\n    [Parameter] public string? CenterLabel { get; set; }\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Pie</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Pie;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"outside\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var pieData = Data.Select(d => new EChartPieData\n        {\n            Name = d.Name,\n            Value = d.Value\n        }).ToList();\n\n        var series = new EChartSeries\n        {\n            Type = \"pie\",\n            RadiusList = new List<string> { InnerRadius, OuterRadius },\n            CenterList = new List<string> { \"50%\", \"50%\" },\n            Data = pieData,\n            Label = new EChartLabel\n            {\n                Show = ShowLabels || ShowDataLabels,\n                Position = ShowDataLabels ? LabelPosition : \"outside\",\n                Formatter = ShowDataLabels && LabelFormat is not null ? LabelFormat : \"{b}: {d}%\"\n            },\n            Emphasis = new EChartEmphasis\n            {\n                Focus = \"self\",\n                ItemStyle = new EChartItemStyle { ShadowBlur = 10 }\n            }\n        };\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (!string.IsNullOrEmpty(CenterLabel))\n        {\n            _option.Title = new EChartTitle\n            {\n                Text = CenterLabel,\n                Left = \"center\",\n                Top = \"center\",\n                TextStyle = new EChartTextStyle { FontSize = 18, FontWeight = \"bold\" }\n            };\n        }\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend)\n            _option.Legend = new EChartLegend { Orient = \"horizontal\", Bottom = \"0%\", Data = Data.Select(d => d.Name).ToList() };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class DonutChartData\n    {\n        public string Name { get; set; } = \"\";\n        public double Value { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/EffectScatterChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<EffectScatterSeriesData> Series { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Scatter</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Scatter;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public EChartMarkLine? MarkLine { get; set; }\n    [Parameter] public EChartMarkPoint? MarkPoint { get; set; }\n    [Parameter] public EChartMarkArea? MarkArea { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var seriesList = Series.Select(s => new EChartSeries\n        {\n            Name = s.Name,\n            Type = \"effectScatter\",\n            Data = s.Points,\n            SymbolSize = s.SymbolSize ?? 20,\n            RippleEffect = new EChartRippleEffect\n            {\n                BrushType = \"stroke\",\n                Scale = 3.5,\n                Period = 4\n            }\n        }).ToList();\n\n        if (ShowDataLabels)\n            foreach (var s in seriesList)\n                s.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        if (seriesList.Count > 0)\n        {\n            if (MarkLine is not null) seriesList[0].MarkLine = MarkLine;\n            if (MarkPoint is not null) seriesList[0].MarkPoint = MarkPoint;\n            if (MarkArea is not null) seriesList[0].MarkArea = MarkArea;\n        }\n\n        _option = new EChartOption\n        {\n            XAxis = new List<EChartAxis> { new() { Type = \"value\" } },\n            YAxis = new List<EChartAxis> { new() { Type = \"value\" } },\n            Series = seriesList,\n            Grid = new EChartGrid\n            {\n                Left = \"3%\", Right = \"4%\",\n                Top = ShowLegend ? \"14%\" : \"5%\",\n                Bottom = DataZoom ? \"15%\" : \"3%\",\n                ContainLabel = true\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend && Series.Count > 1)\n            _option.Legend = new EChartLegend { Data = Series.Select(s => s.Name).ToList() };\n\n        if (DataZoom)\n            _option.DataZoom = new List<EChartDataZoom> { new() { Type = \"slider\", Show = true }, new() { Type = \"inside\" } };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true }, DataZoom = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class EffectScatterSeriesData\n    {\n        public string Name { get; set; } = \"\";\n        public List<double[]> Points { get; set; } = new();\n        public int? SymbolSize { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/FunnelChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<FunnelData> Data { get; set; } = new();\n    [Parameter] public string Sort { get; set; } = \"descending\";\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Funnel</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Funnel;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"inside\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var funnelData = Data.Select(d => new EChartPieData\n        {\n            Name = d.Name,\n            Value = d.Value\n        }).ToList();\n\n        var series = new EChartSeries\n        {\n            Type = \"funnel\",\n            Data = funnelData,\n            Sort = Sort,\n            Left = \"10%\", Top = \"10%\", Right = \"10%\", Bottom = \"10%\",\n            Label = new EChartLabel\n            {\n                Show = true,\n                Position = ShowDataLabels ? LabelPosition : \"inside\",\n                Formatter = ShowDataLabels && LabelFormat is not null ? LabelFormat : \"{b}: {c}\"\n            },\n            Emphasis = new EChartEmphasis\n            {\n                Focus = \"self\",\n                ItemStyle = new EChartItemStyle { ShadowBlur = 10 }\n            }\n        };\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend)\n            _option.Legend = new EChartLegend { Orient = \"horizontal\", Bottom = \"0%\", Data = Data.Select(d => d.Name).ToList() };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class FunnelData\n    {\n        public string Name { get; set; } = \"\";\n        public double Value { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/GaugeChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public double Value { get; set; }\n    [Parameter] public double Min { get; set; } = 0;\n    [Parameter] public double Max { get; set; } = 100;\n    [Parameter] public string? Title { get; set; }\n    [Parameter] public string? Unit { get; set; }\n    [Parameter] public bool ShowPointer { get; set; } = true;\n    [Parameter] public List<GaugeRange>? Ranges { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Gauge</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Gauge;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var formatter = Unit is not null ? $\"{{value}} {Unit}\" : \"{value}\";\n\n        var series = new EChartSeries\n        {\n            Type = \"gauge\",\n            Min = Min,\n            Max = Max,\n            Pointer = new EChartPointer { Show = ShowPointer },\n            Detail = new EChartSeriesDetail\n            {\n                FontSize = 24,\n                Formatter = formatter\n            },\n            Data = new List<Dictionary<string, object>>\n            {\n                new() { [\"value\"] = Value, [\"name\"] = Title ?? \"\" }\n            }\n        };\n\n        if (!string.IsNullOrEmpty(Title))\n            series.Title = new EChartTitle { Text = Title };\n\n        if (Ranges is { Count: > 0 })\n        {\n            // Build axis line color stops from ranges\n            var colorStops = new List<object>();\n            foreach (var range in Ranges.OrderBy(r => r.Threshold))\n            {\n                var normalizedPos = (range.Threshold - Min) / (Max - Min);\n                colorStops.Add(new object[] { normalizedPos, range.Color });\n            }\n            // Ensure we reach 1.0\n            var maxThreshold = Ranges.Max(r => r.Threshold);\n            if (maxThreshold < Max)\n            {\n                var lastColor = Ranges.OrderBy(r => r.Threshold).Last().Color;\n                colorStops.Add(new object[] { 1.0, lastColor });\n            }\n\n            series.ExtensionData = new Dictionary<string, System.Text.Json.JsonElement>\n            {\n                [\"axisLine\"] = System.Text.Json.JsonSerializer.Deserialize<System.Text.Json.JsonElement>(\n                    System.Text.Json.JsonSerializer.Serialize(new\n                    {\n                        lineStyle = new { width = 15, color = colorStops }\n                    }))\n            };\n        }\n\n        if (ShowDataLabels)\n            series.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette\n        };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class GaugeRange\n    {\n        public double Threshold { get; set; }\n        public string Color { get; set; } = \"\";\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/GeoMapChart.razor",
      "content": "@namespace Lumeo\n@implements IAsyncDisposable\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@inject ComponentInteropService Interop\n\n@code {\n    [Parameter] public string MapName { get; set; } = \"world\";\n    [Parameter] public string? MapJson { get; set; }\n    [Parameter] public object? Data { get; set; }\n    [Parameter] public double VisualMapMin { get; set; } = 0;\n    [Parameter] public double VisualMapMax { get; set; } = 100;\n    [Parameter] public bool Roam { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Generic</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Generic;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"inside\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n    private IJSObjectReference? _module;\n    private bool _mapRegistered;\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (!_mapRegistered && !string.IsNullOrEmpty(MapJson))\n        {\n            _module = await Interop.ImportModuleAsync(\"./_content/Lumeo.Charts/js/echarts-interop.js\");\n            await _module.InvokeVoidAsync(\"registerMap\", MapName, MapJson);\n            _mapRegistered = true;\n            BuildOption();\n            StateHasChanged();\n        }\n    }\n\n    protected override void OnParametersSet()\n    {\n        if (_mapRegistered)\n            BuildOption();\n    }\n\n    private void BuildOption()\n    {\n        var visualColors = Colors is { Count: > 0 }\n            ? Colors\n            : new List<string> { \"#e0f3f8\", \"#abd9e9\", \"#74add1\", \"#4575b4\", \"#313695\" };\n\n        var series = new EChartSeries\n        {\n            Type = \"map\",\n            Data = Data,\n            Roam = Roam\n        };\n\n        series.ExtensionData = new Dictionary<string, System.Text.Json.JsonElement>\n        {\n            [\"map\"] = System.Text.Json.JsonSerializer.Deserialize<System.Text.Json.JsonElement>(\n                System.Text.Json.JsonSerializer.Serialize(MapName))\n        };\n\n        if (ShowDataLabels)\n            series.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        _option = new EChartOption\n        {\n            VisualMap = new EChartVisualMap\n            {\n                Min = VisualMapMin,\n                Max = VisualMapMax,\n                Calculable = true,\n                Orient = \"horizontal\",\n                Left = \"center\",\n                Bottom = \"5%\",\n                InRange = new EChartVisualMapInRange { Color = visualColors }\n            },\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        if (_module is not null)\n        {\n            try { await _module.DisposeAsync(); }\n            catch (JSDisconnectedException) { }\n        }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/GraphChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<GraphNode> Nodes { get; set; } = new();\n    [Parameter] public List<GraphLink> Links { get; set; } = new();\n    [Parameter] public List<GraphCategory> Categories { get; set; } = new();\n    [Parameter] public string Layout { get; set; } = \"force\";\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public bool Roam { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Graph</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Graph;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"right\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var nodes = Nodes.Select(n => new EChartGraphNode\n        {\n            Name = n.Name, X = n.X, Y = n.Y,\n            Value = n.Value, Category = n.Category,\n            SymbolSize = n.SymbolSize ?? 20\n        }).ToList();\n\n        var links = Links.Select(l => new EChartGraphLink\n        {\n            Source = l.Source, Target = l.Target, Value = l.Value\n        }).ToList();\n\n        var categories = Categories.Select(c => new EChartGraphCategory { Name = c.Name }).ToList();\n\n        var series = new EChartSeries\n        {\n            Type = \"graph\",\n            Layout = Layout,\n            Roam = Roam,\n            Nodes = nodes,\n            Links = links,\n            Categories = categories,\n            Label = new EChartLabel\n            {\n                Show = true,\n                Position = ShowDataLabels ? LabelPosition : \"right\",\n                Formatter = ShowDataLabels ? LabelFormat : null\n            },\n            Emphasis = new EChartEmphasis { Focus = \"adjacency\" }\n        };\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend && Categories.Count > 0)\n            _option.Legend = new EChartLegend { Data = Categories.Select(c => c.Name).ToList() };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class GraphNode\n    {\n        public string Name { get; set; } = \"\";\n        public double? X { get; set; }\n        public double? Y { get; set; }\n        public double? Value { get; set; }\n        public string? Category { get; set; }\n        public int? SymbolSize { get; set; }\n    }\n\n    public class GraphLink\n    {\n        public string Source { get; set; } = \"\";\n        public string Target { get; set; } = \"\";\n        public double? Value { get; set; }\n    }\n\n    public class GraphCategory\n    {\n        public string Name { get; set; } = \"\";\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/HeatmapChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<string> XCategories { get; set; } = new();\n    [Parameter] public List<string> YCategories { get; set; } = new();\n    [Parameter] public List<double[]> Data { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Grid</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Grid;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"inside\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public EChartMarkLine? MarkLine { get; set; }\n    [Parameter] public EChartMarkPoint? MarkPoint { get; set; }\n    [Parameter] public EChartMarkArea? MarkArea { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var maxValue = Data.Count > 0 ? Data.Max(d => d.Length > 2 ? d[2] : 0) : 10;\n\n        var series = new EChartSeries\n        {\n            Type = \"heatmap\",\n            Data = Data,\n            Label = new EChartLabel { Show = true },\n            Emphasis = new EChartEmphasis\n            {\n                ItemStyle = new EChartItemStyle { ShadowBlur = 10 }\n            }\n        };\n\n        if (ShowDataLabels)\n            series.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        if (MarkLine is not null) series.MarkLine = MarkLine;\n        if (MarkPoint is not null) series.MarkPoint = MarkPoint;\n        if (MarkArea is not null) series.MarkArea = MarkArea;\n\n        _option = new EChartOption\n        {\n            XAxis = new List<EChartAxis> { new() { Type = \"category\", Data = XCategories } },\n            YAxis = new List<EChartAxis> { new() { Type = \"category\", Data = YCategories } },\n            Series = new List<EChartSeries> { series },\n            Grid = new EChartGrid\n            {\n                Left = \"3%\", Right = \"10%\",\n                Top = \"5%\",\n                Bottom = DataZoom ? \"15%\" : \"3%\",\n                ContainLabel = true\n            },\n            VisualMap = new EChartVisualMap\n            {\n                Min = 0,\n                Max = maxValue,\n                Calculable = true,\n                Orient = \"vertical\",\n                Left = \"right\",\n                Bottom = \"center\",\n                InRange = Colors != null && Colors.Count > 0\n                    ? new EChartVisualMapInRange { Color = Colors }\n                    : new EChartVisualMapInRange { Color = new List<string> { \"#313695\", \"#4575b4\", \"#74add1\", \"#abd9e9\", \"#fee090\", \"#fdae61\", \"#f46d43\", \"#d73027\", \"#a50026\" } }\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend)\n            _option.Legend = new EChartLegend();\n\n        if (DataZoom)\n            _option.DataZoom = new List<EChartDataZoom> { new() { Type = \"slider\", Show = true }, new() { Type = \"inside\" } };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true }, DataZoom = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/LineChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<string> Categories { get; set; } = new();\n    [Parameter] public List<ChartSeriesData> Series { get; set; } = new();\n    [Parameter] public bool Smooth { get; set; }\n    [Parameter] public bool ShowDots { get; set; } = true;\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Line</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Line;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n\n    /// <summary>Controls category-axis label rendering. Smart auto-rotates at higher\n    /// densities; ShowAll forces all horizontal; Auto thins overlapping labels.</summary>\n    [Parameter] public ChartLabelStrategy LabelStrategy { get; set; } = ChartLabelStrategy.Smart;\n\n    /// <summary>Manual rotation override (degrees) for category-axis labels.</summary>\n    [Parameter] public int? LabelRotate { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public EChartMarkLine? MarkLine { get; set; }\n    [Parameter] public EChartMarkPoint? MarkPoint { get; set; }\n    [Parameter] public EChartMarkArea? MarkArea { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var seriesList = Series.Select(s => new EChartSeries\n        {\n            Name = s.Name,\n            Type = \"line\",\n            Data = s.Values,\n            Smooth = Smooth ? true : null,\n            ShowSymbol = ShowDots ? null : false,\n            SymbolSize = ShowDots ? 6 : null\n        }).ToList();\n\n        if (ShowDataLabels)\n            foreach (var s in seriesList)\n                s.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        if (seriesList.Count > 0)\n        {\n            if (MarkLine is not null) seriesList[0].MarkLine = MarkLine;\n            if (MarkPoint is not null) seriesList[0].MarkPoint = MarkPoint;\n            if (MarkArea is not null) seriesList[0].MarkArea = MarkArea;\n        }\n\n        var categoryAxis = new EChartAxis { Type = \"category\", Data = Categories, BoundaryGap = false };\n        ChartLabelHelper.ApplyTo(categoryAxis, LabelStrategy, Categories.Count, LabelRotate);\n\n        _option = new EChartOption\n        {\n            XAxis = new List<EChartAxis> { categoryAxis },\n            YAxis = new List<EChartAxis>\n            {\n                new() { Type = \"value\" }\n            },\n            Series = seriesList,\n            Grid = new EChartGrid\n            {\n                Left = \"3%\", Right = \"4%\",\n                Top = ShowLegend ? \"14%\" : \"5%\",\n                Bottom = DataZoom ? \"15%\" : \"3%\",\n                ContainLabel = true\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"axis\" };\n\n        if (ShowLegend && Series.Count > 0)\n            _option.Legend = new EChartLegend { Data = Series.Select(s => s.Name).ToList() };\n\n        if (DataZoom)\n            _option.DataZoom = new List<EChartDataZoom> { new() { Type = \"slider\", Show = true }, new() { Type = \"inside\" } };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true }, DataZoom = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class ChartSeriesData\n    {\n        public string Name { get; set; } = \"\";\n        public List<double> Values { get; set; } = new();\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/LiquidFillChart.razor",
      "content": "@namespace Lumeo\n@implements IAsyncDisposable\n\n<Chart OptionJson=\"@_optionJson\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\"\n       EChartsSource=\"@EChartsSource\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@inject ComponentInteropService Interop\n\n@code {\n    [Parameter] public double Value { get; set; } = 0.5;\n    [Parameter] public string? Title { get; set; }\n    [Parameter] public string Shape { get; set; } = \"circle\";\n    [Parameter] public string? Color { get; set; }\n    [Parameter] public string? BackgroundColor { get; set; }\n    [Parameter] public bool WaveAnimation { get; set; } = true;\n    [Parameter] public string? EChartsSource { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Pie</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Pie;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"center\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private string _optionJson = \"{}\";\n    private IJSObjectReference? _module;\n    private bool _extensionLoaded;\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (firstRender && !_extensionLoaded)\n        {\n            _module = await Interop.ImportModuleAsync(\"./_content/Lumeo.Charts/js/echarts-interop.js\");\n            await _module.InvokeVoidAsync(\"loadExtension\",\n                \"https://cdn.jsdelivr.net/npm/echarts-liquidfill@3/dist/echarts-liquidfill.min.js\");\n            _extensionLoaded = true;\n            BuildOptionJson();\n            StateHasChanged();\n        }\n    }\n\n    protected override void OnParametersSet()\n    {\n        if (_extensionLoaded)\n            BuildOptionJson();\n    }\n\n    private void BuildOptionJson()\n    {\n        var option = new Dictionary<string, object?>();\n\n        var seriesData = new List<object> { Value };\n        var series = new Dictionary<string, object?>\n        {\n            [\"type\"] = \"liquidFill\",\n            [\"data\"] = seriesData,\n            [\"shape\"] = Shape,\n            [\"waveAnimation\"] = WaveAnimation\n        };\n\n        if (!string.IsNullOrEmpty(Color))\n            series[\"color\"] = new[] { Color };\n\n        if (!string.IsNullOrEmpty(BackgroundColor))\n            series[\"backgroundStyle\"] = new Dictionary<string, object?> { [\"color\"] = BackgroundColor };\n\n        if (ShowDataLabels || !string.IsNullOrEmpty(Title))\n        {\n            var label = new Dictionary<string, object?> { [\"show\"] = true };\n            if (LabelFormat is not null) label[\"formatter\"] = LabelFormat;\n            series[\"label\"] = label;\n        }\n\n        if (!string.IsNullOrEmpty(Title))\n            option[\"title\"] = new Dictionary<string, object?> { [\"text\"] = Title, [\"left\"] = \"center\", [\"top\"] = \"5%\" };\n\n        option[\"series\"] = new[] { series };\n\n        if (ColorPalette is { Count: > 0 })\n            option[\"color\"] = ColorPalette;\n\n        if (AnimationDuration.HasValue)\n            option[\"animationDuration\"] = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing))\n            option[\"animationEasing\"] = AnimationEasing;\n\n        if (Toolbox)\n            option[\"toolbox\"] = new Dictionary<string, object?>\n            {\n                [\"show\"] = true,\n                [\"feature\"] = new Dictionary<string, object?>\n                {\n                    [\"saveAsImage\"] = new Dictionary<string, object?> { [\"show\"] = true }\n                }\n            };\n\n        if (OptionOverride is { Count: > 0 })\n            foreach (var kvp in OptionOverride)\n                option[kvp.Key] = kvp.Value;\n\n        _optionJson = System.Text.Json.JsonSerializer.Serialize(option);\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        if (_module is not null)\n        {\n            try { await _module.DisposeAsync(); }\n            catch (JSDisconnectedException) { }\n        }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/MixedChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<string> Categories { get; set; } = new();\n    [Parameter] public List<MixedSeriesData> Series { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Line</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Line;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n\n    /// <summary>Controls category-axis label rendering. Smart auto-rotates at higher\n    /// densities; ShowAll forces all horizontal; Auto thins overlapping labels.</summary>\n    [Parameter] public ChartLabelStrategy LabelStrategy { get; set; } = ChartLabelStrategy.Smart;\n\n    /// <summary>Manual rotation override (degrees) for category-axis labels.</summary>\n    [Parameter] public int? LabelRotate { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public EChartMarkLine? MarkLine { get; set; }\n    [Parameter] public EChartMarkPoint? MarkPoint { get; set; }\n    [Parameter] public EChartMarkArea? MarkArea { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var seriesList = Series.Select(s => new EChartSeries\n        {\n            Name = s.Name,\n            Type = s.Type ?? \"bar\",\n            Data = s.Values,\n            Smooth = s.Type == \"line\" ? true : null\n        }).ToList();\n\n        if (ShowDataLabels)\n            foreach (var s in seriesList)\n                s.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        if (seriesList.Count > 0)\n        {\n            if (MarkLine is not null) seriesList[0].MarkLine = MarkLine;\n            if (MarkPoint is not null) seriesList[0].MarkPoint = MarkPoint;\n            if (MarkArea is not null) seriesList[0].MarkArea = MarkArea;\n        }\n\n        var categoryAxis = new EChartAxis { Type = \"category\", Data = Categories };\n        ChartLabelHelper.ApplyTo(categoryAxis, LabelStrategy, Categories.Count, LabelRotate);\n\n        _option = new EChartOption\n        {\n            XAxis = new List<EChartAxis> { categoryAxis },\n            YAxis = new List<EChartAxis>\n            {\n                new() { Type = \"value\" }\n            },\n            Series = seriesList,\n            Grid = new EChartGrid\n            {\n                Left = \"3%\", Right = \"4%\",\n                Top = ShowLegend ? \"14%\" : \"5%\",\n                Bottom = DataZoom ? \"15%\" : \"3%\",\n                ContainLabel = true\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"axis\", AxisPointer = new EChartAxisPointer { Type = \"cross\" } };\n\n        if (ShowLegend && Series.Count > 0)\n            _option.Legend = new EChartLegend { Data = Series.Select(s => s.Name).ToList() };\n\n        if (DataZoom)\n            _option.DataZoom = new List<EChartDataZoom> { new() { Type = \"slider\", Show = true }, new() { Type = \"inside\" } };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true }, DataZoom = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class MixedSeriesData\n    {\n        public string Name { get; set; } = \"\";\n        public List<double> Values { get; set; } = new();\n        public string? Type { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/NightingaleChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<NightingaleData> Data { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public bool ShowLabels { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Pie</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Pie;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"outside\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var pieData = Data.Select(d => new EChartPieData\n        {\n            Name = d.Name,\n            Value = d.Value\n        }).ToList();\n\n        var series = new EChartSeries\n        {\n            Type = \"pie\",\n            RoseType = \"area\",\n            RadiusList = new List<string> { \"10%\", \"70%\" },\n            CenterList = new List<string> { \"50%\", \"50%\" },\n            Data = pieData,\n            Label = new EChartLabel\n            {\n                Show = ShowLabels || ShowDataLabels,\n                Position = ShowDataLabels ? LabelPosition : \"outside\",\n                Formatter = ShowDataLabels && LabelFormat is not null ? LabelFormat : \"{b}: {d}%\"\n            },\n            Emphasis = new EChartEmphasis\n            {\n                Focus = \"self\",\n                ItemStyle = new EChartItemStyle { ShadowBlur = 10 }\n            }\n        };\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend)\n            _option.Legend = new EChartLegend { Orient = \"horizontal\", Bottom = \"0%\", Data = Data.Select(d => d.Name).ToList() };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class NightingaleData\n    {\n        public string Name { get; set; } = \"\";\n        public double Value { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/ParallelChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<ParallelDimension> Dimensions { get; set; } = new();\n    [Parameter] public List<ParallelSeriesData> Series { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Parallel</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Parallel;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var parallelAxes = Dimensions.Select((d, i) => new EChartParallelAxis\n        {\n            Dim = i, Name = d.Name,\n            Min = d.Min, Max = d.Max,\n            Type = d.Type ?? \"value\"\n        }).ToList();\n\n        var seriesList = Series.Select(s => new EChartSeries\n        {\n            Name = s.Name,\n            Type = \"parallel\",\n            Data = s.Data\n        }).ToList();\n\n        if (ShowDataLabels)\n            foreach (var s in seriesList)\n                s.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        _option = new EChartOption\n        {\n            Parallel = new EChartParallel\n            {\n                Left = \"5%\", Right = \"13%\",\n                Top = ShowLegend ? \"15%\" : \"5%\",\n                Bottom = \"10%\"\n            },\n            ParallelAxis = parallelAxes,\n            Series = seriesList,\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend && Series.Count > 0)\n            _option.Legend = new EChartLegend { Data = Series.Select(s => s.Name).ToList(), Top = \"3%\" };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class ParallelDimension\n    {\n        public string Name { get; set; } = \"\";\n        public double? Min { get; set; }\n        public double? Max { get; set; }\n        public string? Type { get; set; }\n    }\n\n    public class ParallelSeriesData\n    {\n        public string Name { get; set; } = \"\";\n        public List<double[]> Data { get; set; } = new();\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/PictorialBarChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<string> Categories { get; set; } = new();\n    [Parameter] public List<PictorialSeriesData> Series { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Bars</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Bars;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n\n    /// <summary>Controls category-axis label rendering. Smart auto-rotates at higher\n    /// densities; ShowAll forces all horizontal; Auto thins overlapping labels.</summary>\n    [Parameter] public ChartLabelStrategy LabelStrategy { get; set; } = ChartLabelStrategy.Smart;\n\n    /// <summary>Manual rotation override (degrees) for category-axis labels.</summary>\n    [Parameter] public int? LabelRotate { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public EChartMarkLine? MarkLine { get; set; }\n    [Parameter] public EChartMarkPoint? MarkPoint { get; set; }\n    [Parameter] public EChartMarkArea? MarkArea { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var seriesList = Series.Select(s => new EChartSeries\n        {\n            Name = s.Name,\n            Type = \"pictorialBar\",\n            Data = s.Values,\n            Symbol = s.Symbol ?? \"circle\",\n            SymbolSize = s.SymbolSize ?? 20\n        }).ToList();\n\n        if (ShowDataLabels)\n            foreach (var s in seriesList)\n                s.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        if (seriesList.Count > 0)\n        {\n            if (MarkLine is not null) seriesList[0].MarkLine = MarkLine;\n            if (MarkPoint is not null) seriesList[0].MarkPoint = MarkPoint;\n            if (MarkArea is not null) seriesList[0].MarkArea = MarkArea;\n        }\n\n        var categoryAxis = new EChartAxis { Type = \"category\", Data = Categories };\n        ChartLabelHelper.ApplyTo(categoryAxis, LabelStrategy, Categories.Count, LabelRotate);\n\n        _option = new EChartOption\n        {\n            XAxis = new List<EChartAxis> { categoryAxis },\n            YAxis = new List<EChartAxis>\n            {\n                new() { Type = \"value\" }\n            },\n            Series = seriesList,\n            Grid = new EChartGrid\n            {\n                Left = \"3%\", Right = \"4%\",\n                Top = ShowLegend ? \"14%\" : \"5%\",\n                Bottom = DataZoom ? \"15%\" : \"3%\",\n                ContainLabel = true\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"axis\", AxisPointer = new EChartAxisPointer { Type = \"shadow\" } };\n\n        if (ShowLegend && Series.Count > 0)\n            _option.Legend = new EChartLegend { Data = Series.Select(s => s.Name).ToList() };\n\n        if (DataZoom)\n            _option.DataZoom = new List<EChartDataZoom> { new() { Type = \"slider\", Show = true }, new() { Type = \"inside\" } };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true }, DataZoom = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class PictorialSeriesData\n    {\n        public string Name { get; set; } = \"\";\n        public List<double> Values { get; set; } = new();\n        public string? Symbol { get; set; }\n        public int? SymbolSize { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/PieChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<PieChartData> Data { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public bool ShowLabels { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Pie</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Pie;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"outside\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var pieData = Data.Select(d => new EChartPieData\n        {\n            Name = d.Name,\n            Value = d.Value\n        }).ToList();\n\n        var series = new EChartSeries\n        {\n            Type = \"pie\",\n            RadiusList = new List<string> { \"0%\", \"70%\" },\n            CenterList = new List<string> { \"50%\", \"50%\" },\n            Data = pieData,\n            Label = new EChartLabel\n            {\n                Show = ShowLabels || ShowDataLabels,\n                Position = ShowDataLabels ? LabelPosition : \"outside\",\n                Formatter = ShowDataLabels && LabelFormat is not null ? LabelFormat : \"{b}: {d}%\"\n            },\n            Emphasis = new EChartEmphasis\n            {\n                Focus = \"self\",\n                ItemStyle = new EChartItemStyle { ShadowBlur = 10 }\n            }\n        };\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend)\n            _option.Legend = new EChartLegend { Orient = \"horizontal\", Bottom = \"0%\", Data = Data.Select(d => d.Name).ToList() };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class PieChartData\n    {\n        public string Name { get; set; } = \"\";\n        public double Value { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/PolarBarChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<string> Categories { get; set; } = new();\n    [Parameter] public List<double> Values { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = false;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Polar</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Polar;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"inside\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var series = new EChartSeries\n        {\n            Type = \"bar\",\n            Data = Values,\n            CoordinateSystem = \"polar\"\n        };\n\n        if (ShowDataLabels)\n            series.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        _option = new EChartOption\n        {\n            Polar = new Dictionary<string, object?>(),\n            AngleAxis = new List<object>\n            {\n                new Dictionary<string, object?> { [\"type\"] = \"category\", [\"data\"] = Categories }\n            },\n            RadiusAxis = new List<object>\n            {\n                new Dictionary<string, object?>()\n            },\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend)\n            _option.Legend = new EChartLegend();\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/RadarChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<RadarIndicator> Indicators { get; set; } = new();\n    [Parameter] public List<RadarSeriesData> Series { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public string Shape { get; set; } = \"polygon\";\n    [Parameter] public bool FillArea { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Radar</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Radar;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var radarData = Series.Select(s => new EChartRadarData\n        {\n            Name = s.Name,\n            Value = s.Values,\n            AreaStyle = FillArea ? new EChartAreaStyle { Opacity = 0.3 } : null\n        }).ToList();\n\n        var series = new EChartSeries\n        {\n            Type = \"radar\",\n            Data = radarData\n        };\n\n        if (ShowDataLabels)\n            series.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        _option = new EChartOption\n        {\n            Radar = new EChartRadar\n            {\n                Indicator = Indicators.Select(i => new EChartRadarIndicator\n                {\n                    Name = i.Name,\n                    Max = i.Max\n                }).ToList(),\n                Shape = Shape\n            },\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend && Series.Count > 0)\n            _option.Legend = new EChartLegend { Data = Series.Select(s => s.Name).ToList() };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class RadarIndicator\n    {\n        public string Name { get; set; } = \"\";\n        public double Max { get; set; }\n    }\n\n    public class RadarSeriesData\n    {\n        public string Name { get; set; } = \"\";\n        public List<double> Values { get; set; } = new();\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/RadialChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public double Value { get; set; }\n    [Parameter] public double Min { get; set; } = 0;\n    [Parameter] public double Max { get; set; } = 100;\n    [Parameter] public string? Label { get; set; }\n    [Parameter] public string? Color { get; set; }\n    [Parameter] public bool ShowProgress { get; set; } = true;\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Pie</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Pie;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var series = new EChartSeries\n        {\n            Type = \"gauge\",\n            Min = Min,\n            Max = Max,\n            StartAngle = 225,\n            EndAngle = -45,\n            Progress = new EChartSeriesProgress\n            {\n                Show = ShowProgress,\n                Width = 18\n            },\n            Pointer = new EChartPointer { Show = false },\n            Detail = new EChartSeriesDetail\n            {\n                FontSize = 24,\n                Formatter = \"{value}%\"\n            },\n            Data = new List<Dictionary<string, object>>\n            {\n                new() { [\"value\"] = Value, [\"name\"] = Label ?? \"\" }\n            }\n        };\n\n        if (!string.IsNullOrEmpty(Color))\n            series.ItemStyle = new EChartItemStyle { Color = Color };\n\n        if (!string.IsNullOrEmpty(Label))\n            series.Title = new EChartTitle { Text = Label };\n\n        if (ShowDataLabels)\n            series.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette\n        };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/SankeyChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<SankeyNode> Nodes { get; set; } = new();\n    [Parameter] public List<SankeyLink> Links { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Sankey</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Sankey;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"right\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var nodes = Nodes.Select(n => new EChartSankeyNode { Name = n.Name }).ToList();\n        var links = Links.Select(l => new EChartSankeyLink\n        {\n            Source = l.Source,\n            Target = l.Target,\n            Value = l.Value\n        }).ToList();\n\n        var series = new EChartSeries\n        {\n            Type = \"sankey\",\n            Nodes = nodes,\n            Links = links,\n            Emphasis = new EChartEmphasis { Focus = \"adjacency\" },\n            Label = new EChartLabel\n            {\n                Show = true,\n                Position = ShowDataLabels ? LabelPosition : null,\n                Formatter = ShowDataLabels ? LabelFormat : null\n            }\n        };\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend)\n            _option.Legend = new EChartLegend();\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class SankeyNode\n    {\n        public string Name { get; set; } = \"\";\n    }\n\n    public class SankeyLink\n    {\n        public string Source { get; set; } = \"\";\n        public string Target { get; set; } = \"\";\n        public double Value { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/ScatterChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<ScatterSeriesData> Series { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public int? BubbleSize { get; set; }\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Scatter</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Scatter;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public EChartMarkLine? MarkLine { get; set; }\n    [Parameter] public EChartMarkPoint? MarkPoint { get; set; }\n    [Parameter] public EChartMarkArea? MarkArea { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var seriesList = Series.Select(s => new EChartSeries\n        {\n            Name = s.Name,\n            Type = \"scatter\",\n            Data = s.Points,\n            SymbolSize = BubbleSize\n        }).ToList();\n\n        if (ShowDataLabels)\n            foreach (var s in seriesList)\n                s.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        if (seriesList.Count > 0)\n        {\n            if (MarkLine is not null) seriesList[0].MarkLine = MarkLine;\n            if (MarkPoint is not null) seriesList[0].MarkPoint = MarkPoint;\n            if (MarkArea is not null) seriesList[0].MarkArea = MarkArea;\n        }\n\n        _option = new EChartOption\n        {\n            XAxis = new List<EChartAxis> { new() { Type = \"value\" } },\n            YAxis = new List<EChartAxis> { new() { Type = \"value\" } },\n            Series = seriesList,\n            Grid = new EChartGrid\n            {\n                Left = \"3%\", Right = \"4%\",\n                Top = ShowLegend ? \"14%\" : \"5%\",\n                Bottom = DataZoom ? \"15%\" : \"3%\",\n                ContainLabel = true\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend && Series.Count > 1)\n            _option.Legend = new EChartLegend { Data = Series.Select(s => s.Name).ToList() };\n\n        if (DataZoom)\n            _option.DataZoom = new List<EChartDataZoom> { new() { Type = \"slider\", Show = true }, new() { Type = \"inside\" } };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true }, DataZoom = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class ScatterSeriesData\n    {\n        public string Name { get; set; } = \"\";\n        public List<double[]> Points { get; set; } = new();\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/SunburstChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<SunburstData> Data { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Pie</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Pie;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"inside\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var sunburstData = Data.Select(MapSunburstData).ToList();\n\n        var series = new EChartSeries\n        {\n            Type = \"sunburst\",\n            Data = sunburstData,\n            RadiusList = new List<string> { \"0%\", \"90%\" },\n            Label = new EChartLabel\n            {\n                Show = true,\n                Position = ShowDataLabels ? LabelPosition : null,\n                Formatter = ShowDataLabels ? LabelFormat : null\n            }\n        };\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend)\n            _option.Legend = new EChartLegend();\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    private static EChartTreemapData MapSunburstData(SunburstData d) => new()\n    {\n        Name = d.Name,\n        Value = d.Value,\n        Children = d.Children?.Select(MapSunburstData).ToList()\n    };\n\n    public class SunburstData\n    {\n        public string Name { get; set; } = \"\";\n        public double Value { get; set; }\n        public List<SunburstData>? Children { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/ThemeRiverChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<string[]> Data { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Generic</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Generic;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var series = new EChartSeries\n        {\n            Type = \"themeRiver\",\n            Data = Data\n        };\n\n        if (ShowDataLabels)\n            series.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        var legendNames = Data\n            .Where(d => d.Length >= 3)\n            .Select(d => d[2])\n            .Distinct()\n            .ToList();\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            SingleAxis = new EChartSingleAxis\n            {\n                Type = \"time\",\n                Left = \"5%\", Right = \"5%\",\n                Top = ShowLegend ? \"15%\" : \"5%\",\n                Bottom = \"10%\", Height = \"60%\"\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"axis\" };\n\n        if (ShowLegend && legendNames.Count > 0)\n            _option.Legend = new EChartLegend { Data = legendNames, Top = \"3%\" };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/TreeChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public TreeData? Data { get; set; }\n    [Parameter] public string Orient { get; set; } = \"LR\";\n    [Parameter] public bool ShowLabels { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Tree</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Tree;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"right\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        EChartTreeData? BuildTree(TreeData? node)\n        {\n            if (node is null) return null;\n            return new EChartTreeData\n            {\n                Name = node.Name,\n                Value = node.Value,\n                Children = node.Children?.Select(c => BuildTree(c)).Where(c => c is not null).Select(c => c!).ToList()\n            };\n        }\n\n        var treeData = BuildTree(Data);\n\n        var series = new EChartSeries\n        {\n            Type = \"tree\",\n            Data = treeData is not null ? new List<EChartTreeData> { treeData } : null,\n            Orient = Orient,\n            Label = new EChartLabel\n            {\n                Show = ShowLabels || ShowDataLabels,\n                Position = ShowDataLabels ? LabelPosition : null,\n                Formatter = ShowDataLabels ? LabelFormat : null\n            }\n        };\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    public class TreeData\n    {\n        public string Name { get; set; } = \"\";\n        public double? Value { get; set; }\n        public List<TreeData>? Children { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/TreemapChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<TreemapData> Data { get; set; } = new();\n    [Parameter] public bool ShowLegend { get; set; } = true;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Grid</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Grid;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"inside\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var treemapData = Data.Select(MapTreemapData).ToList();\n\n        var series = new EChartSeries\n        {\n            Type = \"treemap\",\n            Data = treemapData\n        };\n\n        if (ShowDataLabels)\n            series.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        _option = new EChartOption\n        {\n            Series = new List<EChartSeries> { series },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"item\" };\n\n        if (ShowLegend)\n            _option.Legend = new EChartLegend();\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n\n    private static EChartTreemapData MapTreemapData(TreemapData d) => new()\n    {\n        Name = d.Name,\n        Value = d.Value,\n        Children = d.Children?.Select(MapTreemapData).ToList()\n    };\n\n    public class TreemapData\n    {\n        public string Name { get; set; } = \"\";\n        public double Value { get; set; }\n        public List<TreemapData>? Children { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/WaterfallChart.razor",
      "content": "@namespace Lumeo\n\n<Chart Option=\"@_option\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n\n@code {\n    [Parameter] public List<string> Categories { get; set; } = new();\n    [Parameter] public List<double> Values { get; set; } = new();\n    [Parameter] public string IncreaseColor { get; set; } = \"var(--color-primary)\";\n    [Parameter] public string DecreaseColor { get; set; } = \"var(--color-destructive)\";\n    [Parameter] public bool ShowLegend { get; set; } = false;\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public List<string>? Colors { get; set; }\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Bars</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Bars;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"top\";\n    [Parameter] public string? LabelFormat { get; set; }\n\n    /// <summary>Controls category-axis label rendering. Smart auto-rotates at higher\n    /// densities; ShowAll forces all horizontal; Auto thins overlapping labels.</summary>\n    [Parameter] public ChartLabelStrategy LabelStrategy { get; set; } = ChartLabelStrategy.Smart;\n\n    /// <summary>Manual rotation override (degrees) for category-axis labels.</summary>\n    [Parameter] public int? LabelRotate { get; set; }\n    [Parameter] public bool DataZoom { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public EChartMarkLine? MarkLine { get; set; }\n    [Parameter] public EChartMarkPoint? MarkPoint { get; set; }\n    [Parameter] public EChartMarkArea? MarkArea { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private EChartOption _option = new();\n\n    protected override void OnParametersSet()\n    {\n        var baseData = new List<double>();\n        var barData = new List<object>();\n        double running = 0;\n\n        for (int i = 0; i < Values.Count; i++)\n        {\n            double val = Values[i];\n            double baseVal = val >= 0 ? running : running + val;\n            baseData.Add(baseVal);\n            running += val;\n            barData.Add(new Dictionary<string, object?>\n            {\n                [\"value\"] = Math.Abs(val),\n                [\"itemStyle\"] = new Dictionary<string, object?> { [\"color\"] = val >= 0 ? IncreaseColor : DecreaseColor }\n            });\n        }\n\n        var transparentSeries = new EChartSeries\n        {\n            Type = \"bar\",\n            Stack = \"waterfall\",\n            Data = baseData,\n            ItemStyle = new EChartItemStyle { Color = \"transparent\", BorderColor = \"transparent\" }\n        };\n\n        var barSeries = new EChartSeries\n        {\n            Type = \"bar\",\n            Stack = \"waterfall\",\n            Data = barData\n        };\n\n        if (ShowDataLabels)\n            barSeries.Label = new EChartLabel { Show = true, Position = LabelPosition, Formatter = LabelFormat };\n\n        if (MarkLine is not null) barSeries.MarkLine = MarkLine;\n        if (MarkPoint is not null) barSeries.MarkPoint = MarkPoint;\n        if (MarkArea is not null) barSeries.MarkArea = MarkArea;\n\n        var categoryAxis = new EChartAxis { Type = \"category\", Data = Categories };\n        ChartLabelHelper.ApplyTo(categoryAxis, LabelStrategy, Categories.Count, LabelRotate);\n\n        _option = new EChartOption\n        {\n            XAxis = new List<EChartAxis> { categoryAxis },\n            YAxis = new List<EChartAxis>\n            {\n                new() { Type = \"value\" }\n            },\n            Series = new List<EChartSeries> { transparentSeries, barSeries },\n            Grid = new EChartGrid\n            {\n                Left = \"3%\", Right = \"4%\",\n                Top = \"5%\",\n                Bottom = DataZoom ? \"15%\" : \"3%\",\n                ContainLabel = true\n            },\n            Color = ColorPalette ?? Colors\n        };\n\n        if (ShowTooltip)\n            _option.Tooltip = new EChartTooltip { Trigger = \"axis\", AxisPointer = new EChartAxisPointer { Type = \"shadow\" } };\n\n        if (DataZoom)\n            _option.DataZoom = new List<EChartDataZoom> { new() { Type = \"slider\", Show = true }, new() { Type = \"inside\" } };\n\n        if (Toolbox)\n            _option.Toolbox = new EChartToolbox { Show = true, Feature = new EChartToolboxFeature { SaveAsImage = new() { Show = true }, Restore = new() { Show = true }, DataZoom = new() { Show = true } } };\n\n        if (AnimationDuration.HasValue) _option.AnimationDuration = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing)) _option.AnimationEasing = AnimationEasing;\n\n        ChartHelper.ApplyOptionOverride(_option, OptionOverride);\n    }\n}\n"
    },
    {
      "path": "UI/Chart/Charts/WordCloudChart.razor",
      "content": "@namespace Lumeo\n@implements IAsyncDisposable\n\n@if (_extensionLoaded)\n{\n    <Chart OptionJson=\"@_optionJson\" Width=\"@Width\" Height=\"@Height\" Theme=\"@Theme\"\n           EChartsSource=\"@EChartsSource\" Class=\"@Class\" IsLoading=\"@IsLoading\" ShowLoadingSkeleton=\"@ShowLoadingSkeleton\" SkeletonKind=\"@SkeletonKind\" SkeletonStyle=\"@SkeletonStyle\" ShowLoadingLabel=\"@ShowLoadingLabel\" LoadingText=\"@LoadingText\" PhantomCycleMs=\"@PhantomCycleMs\" @attributes=\"AdditionalAttributes\" />\n}\nelse\n{\n    <div style=\"width: @Width; height: @Height;\" class=\"@Class\"></div>\n}\n\n@inject ComponentInteropService Interop\n\n@code {\n    [Parameter] public List<WordCloudItem>? Words { get; set; }\n    [Parameter] public string Shape { get; set; } = \"circle\";\n    [Parameter] public int[] SizeRange { get; set; } = new[] { 12, 60 };\n    [Parameter] public int[] RotationRange { get; set; } = new[] { -90, 90 };\n    [Parameter] public string? EChartsSource { get; set; }\n    [Parameter] public bool ShowTooltip { get; set; } = true;\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Theme { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n    /// <summary>When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false.</summary>\n    [Parameter] public bool IsLoading { get; set; }\n    /// <summary>When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever <see cref=\"IsLoading\"/> is true. Set to <c>false</c> to opt out.</summary>\n    [Parameter] public bool ShowLoadingSkeleton { get; set; } = true;\n    /// <summary>Shape family for the loading skeleton. Defaults to <c>ChartSkeletonKind.Generic</c> for this chart type; override if you want a different silhouette.</summary>\n    [Parameter] public ChartSkeletonKind SkeletonKind { get; set; } = ChartSkeletonKind.Generic;\n    /// <summary>Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay.</summary>\n    [Parameter] public ChartSkeletonStyle SkeletonStyle { get; set; } = ChartSkeletonStyle.Phantom;\n    /// <summary>Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400.</summary>\n    [Parameter] public int PhantomCycleMs { get; set; } = 1400;\n    /// <summary>When true (default), shows a small \"Loading…\" pill in the top-right corner whenever <see cref=\"IsLoading\"/> is active. Works on top of any <see cref=\"SkeletonStyle\"/>.</summary>\n    [Parameter] public bool ShowLoadingLabel { get; set; } = true;\n    /// <summary>Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\".</summary>\n    [Parameter] public string? LoadingText { get; set; } = \"Loading…\";\n\n    [Parameter] public bool ShowDataLabels { get; set; }\n    [Parameter] public string LabelPosition { get; set; } = \"center\";\n    [Parameter] public string? LabelFormat { get; set; }\n    [Parameter] public bool Toolbox { get; set; }\n    [Parameter] public List<string>? ColorPalette { get; set; }\n    [Parameter] public int? AnimationDuration { get; set; }\n    [Parameter] public string? AnimationEasing { get; set; }\n    [Parameter] public Dictionary<string, object>? OptionOverride { get; set; }\n\n    private string _optionJson = \"{}\";\n    private IJSObjectReference? _module;\n    private bool _extensionLoaded;\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (firstRender && !_extensionLoaded)\n        {\n            _module = await Interop.ImportModuleAsync(\"./_content/Lumeo.Charts/js/echarts-interop.js\");\n            await _module.InvokeVoidAsync(\"loadExtension\",\n                \"https://cdn.jsdelivr.net/npm/echarts-wordcloud@2.1.0/dist/echarts-wordcloud.min.js\");\n            // Wait for the extension to fully register its series type and renderers\n            await Task.Delay(200);\n            _extensionLoaded = true;\n            BuildOptionJson();\n            StateHasChanged();\n        }\n    }\n\n    protected override void OnParametersSet()\n    {\n        if (_extensionLoaded)\n            BuildOptionJson();\n    }\n\n    private void BuildOptionJson()\n    {\n        var option = new Dictionary<string, object?>();\n\n        var wordData = Words?.Select(w => new Dictionary<string, object?>\n        {\n            [\"name\"] = w.Text,\n            [\"value\"] = w.Weight\n        }).ToList() ?? new List<Dictionary<string, object?>>();\n\n        var series = new Dictionary<string, object?>\n        {\n            [\"type\"] = \"wordCloud\",\n            [\"shape\"] = Shape,\n            [\"sizeRange\"] = SizeRange,\n            [\"rotationRange\"] = RotationRange,\n            [\"rotationStep\"] = 45,\n            [\"gridSize\"] = 8,\n            [\"drawOutOfBound\"] = false,\n            [\"data\"] = wordData,\n            [\"textStyle\"] = new Dictionary<string, object?>\n            {\n                [\"fontFamily\"] = \"sans-serif\",\n                [\"fontWeight\"] = \"bold\"\n            }\n        };\n\n        option[\"series\"] = new[] { series };\n\n        if (ShowTooltip)\n            option[\"tooltip\"] = new Dictionary<string, object?> { [\"trigger\"] = \"item\" };\n\n        if (ColorPalette is { Count: > 0 })\n            option[\"color\"] = ColorPalette;\n\n        if (AnimationDuration.HasValue)\n            option[\"animationDuration\"] = AnimationDuration;\n        if (!string.IsNullOrEmpty(AnimationEasing))\n            option[\"animationEasing\"] = AnimationEasing;\n\n        if (Toolbox)\n            option[\"toolbox\"] = new Dictionary<string, object?>\n            {\n                [\"show\"] = true,\n                [\"feature\"] = new Dictionary<string, object?>\n                {\n                    [\"saveAsImage\"] = new Dictionary<string, object?> { [\"show\"] = true }\n                }\n            };\n\n        if (OptionOverride is { Count: > 0 })\n            foreach (var kvp in OptionOverride)\n                option[kvp.Key] = kvp.Value;\n\n        _optionJson = System.Text.Json.JsonSerializer.Serialize(option);\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        if (_module is not null)\n        {\n            try { await _module.DisposeAsync(); }\n            catch (JSDisconnectedException) { }\n        }\n    }\n\n    public class WordCloudItem\n    {\n        public string Text { get; set; } = \"\";\n        public double Weight { get; set; }\n    }\n}\n"
    },
    {
      "path": "UI/Chart/ChartSkeleton.razor",
      "content": "@namespace Lumeo\n@using System.Globalization\n\n<div class=\"@CssClass\" style=\"@InlineStyle\" role=\"status\" aria-live=\"polite\" aria-label=\"Loading chart\" data-lumeo-chart-skeleton=\"@Kind.ToString().ToLowerInvariant()\" @attributes=\"AdditionalAttributes\">\n    <svg class=\"lumeo-chart-skel-svg w-full h-full\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\" aria-hidden=\"true\" focusable=\"false\">\n        @* Legend/axis hints use opacity 0.35 so they're subtle background structure.\n           Plot area matches where ECharts renders: no layout jump on swap. *@\n        @if (IsCartesian)\n        {\n            @foreach (var (x, w) in LegendPills) { <rect data-skel-axis=\"legend\" x=\"@F(x)\" y=\"5\" width=\"@F(w)\" height=\"3\" rx=\"1.5\" ry=\"1.5\" fill=\"@MutedFill\" opacity=\"0.35\" /> }\n            @foreach (var y in YTickYs)         { <rect data-skel-axis=\"y\" x=\"2\" y=\"@F(y)\" width=\"5\" height=\"1.4\" rx=\"0.4\" ry=\"0.4\" fill=\"@MutedFill\" opacity=\"0.35\" /> }\n            @foreach (var x in XTickXs)         { <rect data-skel-axis=\"x\" x=\"@F(x)\" y=\"92\" width=\"8\" height=\"1.6\" rx=\"0.4\" ry=\"0.4\" fill=\"@MutedFill\" opacity=\"0.35\" /> }\n        }\n\n        @* Guide lines at 25/50/75% of inner plot area. *@\n        @if (Kind == ChartSkeletonKind.Bars || Kind == ChartSkeletonKind.Line || Kind == ChartSkeletonKind.Area)\n        {\n            <line x1=\"10\" y1=\"32.5\" x2=\"98\" y2=\"32.5\" stroke=\"@GridFill\" stroke-width=\"0.3\" stroke-dasharray=\"1 2\" vector-effect=\"non-scaling-stroke\" />\n            <line x1=\"10\" y1=\"51\"   x2=\"98\" y2=\"51\"   stroke=\"@GridFill\" stroke-width=\"0.3\" stroke-dasharray=\"1 2\" vector-effect=\"non-scaling-stroke\" />\n            <line x1=\"10\" y1=\"69.5\" x2=\"98\" y2=\"69.5\" stroke=\"@GridFill\" stroke-width=\"0.3\" stroke-dasharray=\"1 2\" vector-effect=\"non-scaling-stroke\" />\n        }\n        @if (Kind == ChartSkeletonKind.Bars)\n        {\n            @* 8 bars inside plot area, floor at y=PlotBottom. *@\n            @for (int i = 0; i < BarHeights.Length; i++)\n            {\n                var hRatio = BarHeights[i] / 100.0;\n                var h = hRatio * PlotH;\n                var minH = h * 0.35;\n                var x = F(PlotX + 1 + i * (BarWidth + 1));\n                var delay = F(i * 0.12) + \"s\";\n                var hValues = $\"{F(h)};{F(minH)};{F(h)}\";\n                var yValues = $\"{F(PlotBottom - h)};{F(PlotBottom - minH)};{F(PlotBottom - h)}\";\n                <rect class=\"lumeo-chart-skel-shape\" data-skel-shape=\"bar\" x=\"@x\" y=\"@F(PlotBottom - h)\" width=\"@F(BarWidth)\" height=\"@F(h)\" rx=\"1.2\" ry=\"1.2\" fill=\"@MutedFill\" style=\"animation-delay: @delay;\">\n                    <animate attributeName=\"height\" values=\"@hValues\" dur=\"1.6s\" begin=\"@delay\" repeatCount=\"indefinite\" />\n                    <animate attributeName=\"y\"      values=\"@yValues\" dur=\"1.6s\" begin=\"@delay\" repeatCount=\"indefinite\" />\n                </rect>\n            }\n        }\n        else if (Kind == ChartSkeletonKind.Line || Kind == ChartSkeletonKind.Area)\n        {\n            @if (Kind == ChartSkeletonKind.Area)\n            {\n                <polygon class=\"lumeo-chart-skel-shape\" points=\"@(LinePoints + $\" {F(PlotRight)},{F(PlotBottom)} {F(PlotX)},{F(PlotBottom)}\")\" fill=\"@SoftFill\" />\n            }\n            <polyline class=\"lumeo-chart-skel-shape\" data-skel-shape=\"line\" points=\"@LineSecondaryPoints\" fill=\"none\" stroke=\"@SoftFill\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" vector-effect=\"non-scaling-stroke\" style=\"animation-delay: 0.4s;\" />\n            <polyline class=\"lumeo-chart-skel-shape\" data-skel-shape=\"line\" points=\"@LineTertiaryPoints\" fill=\"none\" stroke=\"@SoftFill\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" vector-effect=\"non-scaling-stroke\" style=\"animation-delay: 0.8s;\" />\n            <polyline class=\"lumeo-chart-skel-shape\" data-skel-shape=\"line\" points=\"@LinePoints\" fill=\"none\" stroke=\"@MutedFill\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" vector-effect=\"non-scaling-stroke\" />\n        }\n        else if (Kind == ChartSkeletonKind.Pie)\n        {\n            @* Pie: top 10% reserves legend (3 pills), pie shifts down 5 and uses radius 26. *@\n            @foreach (var (x, w) in LegendPills) { <rect data-skel-axis=\"legend\" x=\"@F(x)\" y=\"3\" width=\"@F(w)\" height=\"3\" rx=\"1.5\" ry=\"1.5\" fill=\"@MutedFill\" opacity=\"0.35\" /> }\n\n            @* External label-anchor lines at 45°/135°/225°/315° — radius 30 to 45 (after translate). *@\n            <g transform=\"translate(0 5)\">\n                @foreach (var (x1, y1, x2, y2) in PieLabelLines) { <line data-skel-axis=\"pie-label\" x1=\"@F(x1)\" y1=\"@F(y1)\" x2=\"@F(x2)\" y2=\"@F(y2)\" stroke=\"@MutedFill\" stroke-width=\"0.5\" opacity=\"0.35\" vector-effect=\"non-scaling-stroke\" /> }\n\n                <g>\n                    @for (int i = 0; i < PieSlices.Length; i++)\n                    {\n                        var d = PieSlices[i];\n                        var delay = F(i * 0.2) + \"s\";\n                        <path class=\"lumeo-chart-skel-shape\" data-skel-shape=\"pie-slice\" d=\"@d\" fill=\"@MutedFill\" style=\"animation-delay: @delay;\" />\n                    }\n                    <animateTransform attributeName=\"transform\" type=\"rotate\"\n                                      values=\"0 50 50; 360 50 50\"\n                                      dur=\"6s\" repeatCount=\"indefinite\" />\n                </g>\n            </g>\n        }\n        else if (Kind == ChartSkeletonKind.Scatter)\n        {\n            @for (int i = 0; i < ScatterDots.Length; i++)\n            {\n                var (x, y, r) = ScatterDots[i];\n                // Map dots from 0..100 space into the plot area (x 10..98, y 14..88).\n                var px = PlotX + x * (PlotW / 100.0);\n                var py = 14 + y * (PlotH / 100.0);\n                <circle class=\"lumeo-chart-skel-shape\" data-skel-shape=\"dot\" cx=\"@F(px)\" cy=\"@F(py)\" r=\"@F(r)\" fill=\"@MutedFill\" style=\"animation-delay: @(F(i * 0.07))s, @(F(i * 0.07))s;\" />\n            }\n        }\n        else if (Kind == ChartSkeletonKind.Grid)\n        {\n            // Grid cells also live inside the plot area so heatmaps leave room for legend + axis ticks.\n            var cellW = (PlotW - 1.2 * (GridCols - 1)) / GridCols;\n            var cellH = (PlotH - 1.2 * (GridRows - 1)) / GridRows;\n            @for (int r = 0; r < GridRows; r++)\n            {\n                @for (int c = 0; c < GridCols; c++)\n                {\n                    var x = F(PlotX + c * (cellW + 1.2));\n                    var y = F(14 + r * (cellH + 1.2));\n                    var delay = F(((r + c) % 5) * 0.1) + \"s\";\n                    var op = F(GridOpacities[(r * GridCols + c) % GridOpacities.Length]);\n                    <rect class=\"lumeo-chart-skel-shape\" x=\"@x\" y=\"@y\" width=\"@F(cellW)\" height=\"@F(cellH)\" rx=\"0.6\" ry=\"0.6\" fill=\"@MutedFill\" style=\"animation-delay: @delay; opacity: @op;\" />\n                }\n            }\n        }\n        else\n        {\n            <rect class=\"lumeo-chart-skel-shape\" x=\"6\" y=\"6\" width=\"88\" height=\"88\" rx=\"2\" ry=\"2\" fill=\"@SoftFill\" />\n            <circle class=\"lumeo-chart-skel-shape\" cx=\"50\" cy=\"50\" r=\"10\" fill=\"none\" stroke=\"@MutedFill\" stroke-width=\"3\" vector-effect=\"non-scaling-stroke\" />\n        }\n    </svg>\n</div>\n\n@code {\n    [Parameter] public ChartSkeletonKind Kind { get; set; } = ChartSkeletonKind.Generic;\n    [Parameter] public string Width { get; set; } = \"100%\";\n    [Parameter] public string Height { get; set; } = \"350px\";\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    // Muted fills — 10%/6%/8% of the foreground token so light/dark mode both work.\n    private const string MutedFill = \"color-mix(in oklab, var(--color-foreground) 10%, transparent)\";\n    private const string SoftFill = \"color-mix(in oklab, var(--color-foreground) 6%, transparent)\";\n    private const string GridFill = \"color-mix(in oklab, var(--color-foreground) 8%, transparent)\";\n\n    // Inner plot area (matches real ECharts render box): 10% left, 12% bottom, 14% top, 2% right padding.\n    private const double PlotX = 10;\n    private const double PlotRight = 98;\n    private const double PlotTop = 14;\n    private const double PlotBottom = 88;\n    private const double PlotW = PlotRight - PlotX;   // 88\n    private const double PlotH = PlotBottom - PlotTop; // 74\n\n    // Polylines inside plot area — percentages map into the 14..88 vertical range.\n    private static readonly string LinePoints = BuildLine(new double[] { 72, 48, 62, 34, 54, 28, 46, 22, 40 });\n    private static readonly string LineSecondaryPoints = BuildLine(new double[] { 55, 70, 58, 52, 68, 50, 64, 56, 66 });\n    private static readonly string LineTertiaryPoints  = BuildLine(new double[] { 82, 78, 80, 72, 84, 76, 82, 74, 78 });\n\n    private static string BuildLine(double[] pct)\n    {\n        var sb = new System.Text.StringBuilder();\n        for (int i = 0; i < pct.Length; i++)\n        {\n            var x = PlotX + (i / (double)(pct.Length - 1)) * PlotW;\n            var y = PlotTop + (pct[i] / 100.0) * PlotH;\n            if (i > 0) sb.Append(' ');\n            sb.Append(x.ToString(\"0.##\", CultureInfo.InvariantCulture)).Append(',').Append(y.ToString(\"0.##\", CultureInfo.InvariantCulture));\n        }\n        return sb.ToString();\n    }\n\n    // 8 bars, 1-unit gaps inside PlotW.\n    private static readonly double[] BarHeights = { 38, 62, 48, 78, 56, 82, 44, 70 };\n    private const double BarWidth = (PlotW - 2 - 1 * (8 - 1)) / 8.0;\n\n    // 5x5 heatmap grid.\n    private const int GridRows = 5;\n    private const int GridCols = 5;\n    private static readonly double[] GridOpacities = { 0.4,0.7,0.5,0.9,0.55,0.8,0.45,0.65,0.35,0.75,0.6,0.5,0.85,0.4,0.7,0.55,0.95,0.45,0.6,0.8,0.5,0.7,0.4,0.65,0.55 };\n\n    // 4 pie wedges centered at (50,50), radius 26.\n    private static readonly string[] PieSlices =\n    {\n        \"M 50 50 L 76 50 A 26 26 0 0 1 50 76 Z\",\n        \"M 50 50 L 50 76 A 26 26 0 0 1 24 50 Z\",\n        \"M 50 50 L 24 50 A 26 26 0 0 1 50 24 Z\",\n        \"M 50 50 L 50 24 A 26 26 0 0 1 76 50 Z\",\n    };\n\n    // Seeded scatter dots (0..100 space, remapped into plot area at render).\n    private static readonly (double X, double Y, double R)[] ScatterDots =\n    {\n        (12, 78, 2.4), (22, 62, 3.1), (18, 88, 2.0), (32, 48, 2.8), (40, 70, 2.2),\n        (48, 35, 3.0), (54, 58, 2.6), (62, 82, 2.3), (70, 40, 3.2), (76, 66, 2.5),\n        (84, 28, 2.7), (90, 52, 2.1), (28, 25, 2.9), (58, 18, 2.4), (78, 12, 2.6),\n    };\n\n    // Axis / legend hints.\n    private static readonly (double X, double W)[] LegendPills = { (32, 12), (46, 10), (58, 11) };\n    private static readonly double[] YTickYs = { 22, 40, 58, 76 };\n    private static readonly double[] XTickXs = { 14, 28, 42, 56, 70, 84 };\n    // Pie external label anchors at 45°/135°/225°/315°, r=30 → 45 relative to (50,50).\n    private static readonly (double X1, double Y1, double X2, double Y2)[] PieLabelLines =\n    {\n        (71.21, 28.79, 81.82, 18.18),\n        (28.79, 28.79, 18.18, 18.18),\n        (28.79, 71.21, 18.18, 81.82),\n        (71.21, 71.21, 81.82, 81.82),\n    };\n\n    private bool IsCartesian => Kind == ChartSkeletonKind.Bars\n                             || Kind == ChartSkeletonKind.Line\n                             || Kind == ChartSkeletonKind.Area\n                             || Kind == ChartSkeletonKind.Scatter\n                             || Kind == ChartSkeletonKind.Grid;\n\n    private string CssClass => $\"lumeo-chart-skeleton inline-block w-full rounded-lg overflow-hidden bg-muted/30 relative {Class}\".Trim();\n    private string InlineStyle => $\"width: {Width}; height: {Height};\";\n\n    // Invariant culture: SVG attributes require `.` as decimal separator.\n    private static string F(double v) => v.ToString(\"0.##\", CultureInfo.InvariantCulture);\n}\n"
    },
    {
      "path": "UI/Chart/ChartSkeletonKind.cs",
      "content": "namespace Lumeo;\n\n/// <summary>Shape family for <see cref=\"ChartSkeleton\"/>. Each value renders a different\n/// SVG silhouette that matches the shape of an actual chart, so loading states don't cause\n/// layout shift or empty whitespace flicker before ECharts mounts.</summary>\npublic enum ChartSkeletonKind\n{\n    /// <summary>Row of vertical bars with staggered pulse — for Bar/Waterfall/Candlestick/BoxPlot.</summary>\n    Bars,\n    /// <summary>Zigzag polyline — for LineChart / MixedChart.</summary>\n    Line,\n    /// <summary>Zigzag polyline with filled area beneath — for AreaChart.</summary>\n    Area,\n    /// <summary>Donut ring — for Pie/Donut/Gauge/Radial/Sunburst/Funnel/Nightingale/LiquidFill.</summary>\n    Pie,\n    /// <summary>Randomised pulsing dots — for Scatter / EffectScatter.</summary>\n    Scatter,\n    /// <summary>5x5 grid with varying opacities — for Treemap/Heatmap/CalendarHeatmap.</summary>\n    Grid,\n    /// <summary>Rounded rect + centered ring — fallback for unrecognized chart types.</summary>\n    Generic,\n    /// <summary>Polygon web with radial axes — for RadarChart.</summary>\n    Radar,\n    /// <summary>Flow diagram (source → target nodes with curved links) — for SankeyChart.</summary>\n    Sankey,\n    /// <summary>Force-directed nodes + edges — for GraphChart.</summary>\n    Graph,\n    /// <summary>Branching hierarchy — for TreeChart.</summary>\n    Tree,\n    /// <summary>Parallel-coordinates lines across multiple axes — for ParallelChart.</summary>\n    Parallel,\n    /// <summary>Funnel-shaped stepped trapezoids — for FunnelChart.</summary>\n    Funnel,\n    /// <summary>Semi-circle dial with needle — for GaugeChart.</summary>\n    Gauge,\n    /// <summary>Radial bars around a polar origin — for PolarBar.</summary>\n    Polar,\n    /// <summary>OHLC-style wicks + boxes — for CandlestickChart.</summary>\n    Candlestick,\n}\n\n/// <summary>How the loading state's visual is presented.\n/// Phantom = render the real chart with synthesized data in a muted palette and morph\n/// to real data via ECharts' option-change animation (default — feels continuous).\n/// Silhouette = fall back to the legacy SVG skeleton overlay.\n/// A small \"Loading…\" pill (spinner + label) is overlaid on TOP of whichever style is\n/// chosen via <see cref=\"Chart.ShowLoadingLabel\"/> so users never mistake the cycling\n/// phantom data for a bug. The label can be disabled per-chart for bare visuals.</summary>\npublic enum ChartSkeletonStyle\n{\n    Phantom,\n    Silhouette,\n}\n"
    },
    {
      "path": "UI/Chart/EChartOption.cs",
      "content": "using System.Text.Json;\nusing System.Text.Json.Serialization;\n\nnamespace Lumeo;\n\n/// <summary>\n/// Root ECharts option object. Build this and pass to Chart/convenience components.\n/// All properties use JSON serialization naming to map directly to ECharts options.\n/// </summary>\npublic class EChartOption\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartTitle? Title { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartTooltip? Tooltip { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLegend? Legend { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartGrid? Grid { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartAxis>? XAxis { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartAxis>? YAxis { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartRadar? Radar { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<object>? AngleAxis { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<object>? RadiusAxis { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public object? Polar { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartSeries>? Series { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<string>? Color { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartVisualMap? VisualMap { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartAnimation? Animation { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartSingleAxis? SingleAxis { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartParallel? Parallel { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartParallelAxis>? ParallelAxis { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartDataZoom>? DataZoom { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartToolbox? Toolbox { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartBrush? Brush { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartGeo? Geo { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public object? Calendar { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartDataset>? Dataset { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? AnimationEnabled { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? AnimationDuration { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? AnimationEasing { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? AnimationDelay { get; set; }\n\n    /// <summary>\n    /// For any ECharts options not covered by typed properties, merge raw dictionary entries.\n    /// </summary>\n    [JsonExtensionData]\n    public Dictionary<string, JsonElement>? ExtensionData { get; set; }\n\n    private static readonly JsonSerializerOptions _jsonOptions = new()\n    {\n        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,\n        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull\n    };\n\n    public string ToJson()\n    {\n        return JsonSerializer.Serialize(this, _jsonOptions);\n    }\n}\n\npublic class EChartTitle\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Text { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Subtext { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Left { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Top { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartTextStyle? TextStyle { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartTextStyle? SubtextStyle { get; set; }\n}\n\npublic class EChartTextStyle\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Color { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? FontSize { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? FontWeight { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? FontFamily { get; set; }\n\n    /// <summary>Background fill behind legend/text — used for phantom skeleton-box rendering.</summary>\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? BackgroundColor { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int[]? Padding { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? BorderRadius { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Width { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Height { get; set; }\n}\n\npublic class EChartTooltip\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Trigger { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartAxisPointer? AxisPointer { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? BackgroundColor { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? BorderColor { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? BorderWidth { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartTextStyle? TextStyle { get; set; }\n}\n\npublic class EChartAxisPointer\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Type { get; set; }\n}\n\npublic class EChartLegend\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<string>? Data { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Orient { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Left { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Top { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Bottom { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartTextStyle? TextStyle { get; set; }\n}\n\npublic class EChartGrid\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Left { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Right { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Top { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Bottom { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? ContainLabel { get; set; }\n}\n\npublic class EChartAxis\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Type { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<string>? Data { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? BoundaryGap { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Min { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Max { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartAxisLine? AxisLine { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartSplitLine? SplitLine { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartAxisLabel? AxisLabel { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n}\n\npublic class EChartAxisLine\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLineStyle? LineStyle { get; set; }\n}\n\npublic class EChartSplitLine\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLineStyle? LineStyle { get; set; }\n}\n\npublic class EChartAxisLabel\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Color { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? FontSize { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Rotate { get; set; }\n\n    /// <summary>Interval between labels. 0 = show every label, 1 = every other, N = every (N+1)th.\n    /// Leave null for ECharts' default auto-thinning (hides overlapping labels).</summary>\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Interval { get; set; }\n\n    /// <summary>Background fill behind the label text — used by phantom-mode skeleton boxes\n    /// where we want label rectangles to show while the real text is transparent.</summary>\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? BackgroundColor { get; set; }\n\n    /// <summary>Padding inside the label background — [top, right, bottom, left].</summary>\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int[]? Padding { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? BorderRadius { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Width { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Height { get; set; }\n\n    /// <summary>When true (and a category label formatter returns the raw value), forces the\n    /// label to render as a formatted string — mainly a knob for phantom-box rendering.</summary>\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Formatter { get; set; }\n}\n\npublic class EChartLineStyle\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Color { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Width { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Type { get; set; }\n}\n\npublic class EChartRadar\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartRadarIndicator>? Indicator { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Shape { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartSplitLine? SplitLine { get; set; }\n}\n\npublic class EChartRadarIndicator\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Max { get; set; }\n}\n\npublic class EChartSeries\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Type { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public object? Data { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Smooth { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? ShowSymbol { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? SymbolSize { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Symbol { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Stack { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartAreaStyle? AreaStyle { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartItemStyle? ItemStyle { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLineStyle? LineStyle { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLabel? Label { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartEmphasis? Emphasis { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Radius { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<string>? RadiusList { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Center { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<string>? CenterList { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? RoseType { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? BarWidth { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? BarGap { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? BarMaxWidth { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? StartAngle { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? EndAngle { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Min { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Max { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? CoordinateSystem { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartSeriesProgress? Progress { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartPointer? Pointer { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartSeriesDetail? Detail { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartTitle? Title { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public object? Nodes { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public object? Links { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Sort { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Layout { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public object? Force { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Roam { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartGraphCategory>? Categories { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartRippleEffect? RippleEffect { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Orient { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? EdgeLength { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Repulsion { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Left { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Top { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Right { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Bottom { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartMarkLine? MarkLine { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartMarkPoint? MarkPoint { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartMarkArea? MarkArea { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? AnimationDuration { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? AnimationEasing { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Large { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? LargeThreshold { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? CalendarIndex { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? GeoIndex { get; set; }\n\n    [JsonExtensionData]\n    public Dictionary<string, JsonElement>? ExtensionData { get; set; }\n}\n\npublic class EChartAreaStyle\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Opacity { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Color { get; set; }\n}\n\npublic class EChartItemStyle\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Color { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? BorderColor { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? BorderWidth { get; set; }\n\n    [JsonIgnore]\n    public int? BorderRadius { get; set; }\n\n    /// <summary>\n    /// Per-corner border radius as [top-left, top-right, bottom-right, bottom-left].\n    /// Takes precedence over <see cref=\"BorderRadius\"/> when set. Useful for\n    /// stacked bar charts where only the outermost segment should be rounded.\n    /// </summary>\n    [JsonIgnore]\n    public int[]? BorderRadiusCorners { get; set; }\n\n    // ECharts accepts either a number or a 4-element array for `borderRadius`.\n    // We expose two strongly-typed setters and pick the right one at serialization time.\n    [JsonPropertyName(\"borderRadius\")]\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public object? BorderRadiusSerialized =>\n        BorderRadiusCorners is { Length: > 0 } corners ? corners :\n        BorderRadius is int r ? r :\n        null;\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Opacity { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? ShadowBlur { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? ShadowColor { get; set; }\n}\n\npublic class EChartLabel\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Position { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Formatter { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Color { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? FontSize { get; set; }\n}\n\npublic class EChartEmphasis\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartItemStyle? ItemStyle { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLabel? Label { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Focus { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? BlurScope { get; set; }\n}\n\npublic class EChartSeriesProgress\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Width { get; set; }\n}\n\npublic class EChartPointer\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n}\n\npublic class EChartSeriesDetail\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? FontSize { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Formatter { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Color { get; set; }\n}\n\npublic class EChartAnimation\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Enabled { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Duration { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Easing { get; set; }\n}\n\npublic class EChartPieData\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Value { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartItemStyle? ItemStyle { get; set; }\n}\n\npublic class EChartRadarData\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<double>? Value { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartAreaStyle? AreaStyle { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLineStyle? LineStyle { get; set; }\n}\n\npublic class EChartVisualMap\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Min { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Max { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Calculable { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Orient { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Left { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Bottom { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartVisualMapInRange? InRange { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Type { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartVisualMapPiece>? Pieces { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Top { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Right { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Dimension { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? SeriesIndex { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<string>? Categories { get; set; }\n}\n\npublic class EChartVisualMapInRange\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<string>? Color { get; set; }\n}\n\npublic class EChartTreemapData\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Value { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartTreemapData>? Children { get; set; }\n}\n\npublic class EChartSankeyNode\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n}\n\npublic class EChartSankeyLink\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Source { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Target { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Value { get; set; }\n}\n\npublic class EChartGraphNode\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? X { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Y { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Value { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Category { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? SymbolSize { get; set; }\n}\n\npublic class EChartGraphLink\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Source { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Target { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Value { get; set; }\n}\n\npublic class EChartGraphCategory\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n}\n\npublic class EChartTreeData\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Value { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartTreeData>? Children { get; set; }\n}\n\npublic class EChartParallelAxis\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Dim { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Min { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Max { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Type { get; set; }\n}\n\npublic class EChartRippleEffect\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? BrushType { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Scale { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Period { get; set; }\n}\n\npublic class EChartSingleAxis\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Type { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Left { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Right { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Top { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Bottom { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Height { get; set; }\n}\n\npublic class EChartParallel\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Left { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Right { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Top { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Bottom { get; set; }\n}\n\npublic class EChartDataZoom\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Type { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? Start { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? End { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? StartValue { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? EndValue { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Orient { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? XAxisIndex { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? YAxisIndex { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Realtime { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? FilterMode { get; set; }\n}\n\npublic class EChartToolbox\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Orient { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Left { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Top { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Right { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartToolboxFeature? Feature { get; set; }\n}\n\npublic class EChartToolboxFeature\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartToolboxSaveAsImage? SaveAsImage { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartToolboxRestore? Restore { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartToolboxDataZoom? DataZoom { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartToolboxDataView? DataView { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartToolboxMagicType? MagicType { get; set; }\n}\n\npublic class EChartToolboxSaveAsImage\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Type { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Title { get; set; }\n}\n\npublic class EChartToolboxRestore\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Title { get; set; }\n}\n\npublic class EChartToolboxDataZoom\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Title { get; set; }\n}\n\npublic class EChartToolboxDataView\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? ReadOnly { get; set; }\n}\n\npublic class EChartToolboxMagicType\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Show { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<string>? Type { get; set; }\n}\n\npublic class EChartBrush\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<string>? ToolBox { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? XAxisIndex { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? YAxisIndex { get; set; }\n}\n\npublic class EChartGeo\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Map { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Roam { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartItemStyle? ItemStyle { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLabel? Label { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartEmphasis? Emphasis { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Zoom { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<double>? Center { get; set; }\n}\n\npublic class EChartCalendar\n{\n    /// <summary>\n    /// Calendar range — set a string like \"2024\" for a single year,\n    /// or a List&lt;string&gt; like [\"2024-01-01\",\"2024-06-30\"] for a date range.\n    /// </summary>\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public object? Range { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Left { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Right { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Top { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Bottom { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Orient { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? CellSize { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartSplitLine? SplitLine { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartItemStyle? ItemStyle { get; set; }\n}\n\npublic class EChartDataset\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<List<object>>? Source { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartDatasetDimension>? Dimensions { get; set; }\n}\n\npublic class EChartDatasetDimension\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Type { get; set; }\n}\n\npublic class EChartMarkLine\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Silent { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<object>? Data { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLabel? Label { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLineStyle? LineStyle { get; set; }\n}\n\npublic class EChartMarkPoint\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<EChartMarkPointData>? Data { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public int? SymbolSize { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLabel? Label { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartItemStyle? ItemStyle { get; set; }\n}\n\npublic class EChartMarkPointData\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Name { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Type { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Coord { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Value { get; set; }\n}\n\npublic class EChartMarkArea\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public bool? Silent { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public List<List<object>>? Data { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartItemStyle? ItemStyle { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public EChartLabel? Label { get; set; }\n}\n\npublic class EChartVisualMapPiece\n{\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Min { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Max { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public double? Value { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Label { get; set; }\n\n    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]\n    public string? Color { get; set; }\n}\n\n/// <summary>\n/// Source-generated JSON serialization context for EChart types.\n/// </summary>\n[JsonSourceGenerationOptions(\n    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,\n    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]\n[JsonSerializable(typeof(EChartOption))]\n[JsonSerializable(typeof(EChartSeries))]\n[JsonSerializable(typeof(EChartPieData))]\n[JsonSerializable(typeof(EChartRadarData))]\n[JsonSerializable(typeof(List<EChartPieData>))]\n[JsonSerializable(typeof(List<EChartRadarData>))]\n[JsonSerializable(typeof(EChartVisualMap))]\n[JsonSerializable(typeof(EChartVisualMapInRange))]\n[JsonSerializable(typeof(EChartTreemapData))]\n[JsonSerializable(typeof(List<EChartTreemapData>))]\n[JsonSerializable(typeof(EChartSankeyNode))]\n[JsonSerializable(typeof(List<EChartSankeyNode>))]\n[JsonSerializable(typeof(EChartSankeyLink))]\n[JsonSerializable(typeof(List<EChartSankeyLink>))]\n[JsonSerializable(typeof(List<double[]>))]\n[JsonSerializable(typeof(List<double>))]\n[JsonSerializable(typeof(List<string>))]\n[JsonSerializable(typeof(List<object>))]\n[JsonSerializable(typeof(Dictionary<string, object>))]\n[JsonSerializable(typeof(List<Dictionary<string, object>>))]\n[JsonSerializable(typeof(Dictionary<string, object?>))]\n[JsonSerializable(typeof(List<Dictionary<string, object?>>))]\n[JsonSerializable(typeof(EChartGraphNode))]\n[JsonSerializable(typeof(List<EChartGraphNode>))]\n[JsonSerializable(typeof(EChartGraphLink))]\n[JsonSerializable(typeof(List<EChartGraphLink>))]\n[JsonSerializable(typeof(EChartGraphCategory))]\n[JsonSerializable(typeof(List<EChartGraphCategory>))]\n[JsonSerializable(typeof(EChartTreeData))]\n[JsonSerializable(typeof(List<EChartTreeData>))]\n[JsonSerializable(typeof(EChartParallelAxis))]\n[JsonSerializable(typeof(List<EChartParallelAxis>))]\n[JsonSerializable(typeof(EChartRippleEffect))]\n[JsonSerializable(typeof(EChartSingleAxis))]\n[JsonSerializable(typeof(EChartParallel))]\n[JsonSerializable(typeof(List<string[]>))]\n[JsonSerializable(typeof(EChartDataZoom))]\n[JsonSerializable(typeof(List<EChartDataZoom>))]\n[JsonSerializable(typeof(EChartToolbox))]\n[JsonSerializable(typeof(EChartToolboxFeature))]\n[JsonSerializable(typeof(EChartToolboxSaveAsImage))]\n[JsonSerializable(typeof(EChartToolboxRestore))]\n[JsonSerializable(typeof(EChartToolboxDataZoom))]\n[JsonSerializable(typeof(EChartToolboxDataView))]\n[JsonSerializable(typeof(EChartToolboxMagicType))]\n[JsonSerializable(typeof(EChartBrush))]\n[JsonSerializable(typeof(EChartGeo))]\n[JsonSerializable(typeof(EChartCalendar))]\n[JsonSerializable(typeof(List<EChartCalendar>))]\n[JsonSerializable(typeof(EChartDataset))]\n[JsonSerializable(typeof(List<EChartDataset>))]\n[JsonSerializable(typeof(EChartDatasetDimension))]\n[JsonSerializable(typeof(List<EChartDatasetDimension>))]\n[JsonSerializable(typeof(EChartMarkLine))]\n[JsonSerializable(typeof(EChartMarkPoint))]\n[JsonSerializable(typeof(EChartMarkPointData))]\n[JsonSerializable(typeof(List<EChartMarkPointData>))]\n[JsonSerializable(typeof(EChartMarkArea))]\n[JsonSerializable(typeof(EChartVisualMapPiece))]\n[JsonSerializable(typeof(List<EChartVisualMapPiece>))]\n[JsonSerializable(typeof(List<List<object>>))]\n[JsonSerializable(typeof(object[]))]\n[JsonSerializable(typeof(List<object[]>))]\n[JsonSerializable(typeof(int[]))]\n[JsonSerializable(typeof(List<int>))]\n[JsonSerializable(typeof(List<int[]>))]\ninternal partial class EChartJsonContext : JsonSerializerContext;\n"
    }
  ],
  "slots": [],
  "serviceDependencies": [
    {
      "service": "ComponentInteropService",
      "namespace": ""
    },
    {
      "service": "ILumeoLocalizer",
      "namespace": "Lumeo.Services.Localization"
    }
  ],
  "cascadingDependencies": [],
  "relatedComponents": [
    {
      "name": "AreaChart",
      "reason": "sub-component"
    },
    {
      "name": "BarChart",
      "reason": "sub-component"
    },
    {
      "name": "BoxPlotChart",
      "reason": "sub-component"
    },
    {
      "name": "CalendarHeatmapChart",
      "reason": "sub-component"
    },
    {
      "name": "CandlestickChart",
      "reason": "sub-component"
    },
    {
      "name": "DonutChart",
      "reason": "sub-component"
    },
    {
      "name": "EffectScatterChart",
      "reason": "sub-component"
    },
    {
      "name": "FunnelChart",
      "reason": "sub-component"
    },
    {
      "name": "GaugeChart",
      "reason": "sub-component"
    },
    {
      "name": "GeoMapChart",
      "reason": "sub-component"
    },
    {
      "name": "GraphChart",
      "reason": "sub-component"
    },
    {
      "name": "HeatmapChart",
      "reason": "sub-component"
    },
    {
      "name": "LineChart",
      "reason": "sub-component"
    },
    {
      "name": "LiquidFillChart",
      "reason": "sub-component"
    },
    {
      "name": "MixedChart",
      "reason": "sub-component"
    },
    {
      "name": "NightingaleChart",
      "reason": "sub-component"
    },
    {
      "name": "ParallelChart",
      "reason": "sub-component"
    },
    {
      "name": "PictorialBarChart",
      "reason": "sub-component"
    },
    {
      "name": "PieChart",
      "reason": "sub-component"
    },
    {
      "name": "PolarBarChart",
      "reason": "sub-component"
    },
    {
      "name": "RadarChart",
      "reason": "sub-component"
    },
    {
      "name": "RadialChart",
      "reason": "sub-component"
    },
    {
      "name": "SankeyChart",
      "reason": "sub-component"
    },
    {
      "name": "ScatterChart",
      "reason": "sub-component"
    },
    {
      "name": "SunburstChart",
      "reason": "sub-component"
    },
    {
      "name": "ThemeRiverChart",
      "reason": "sub-component"
    },
    {
      "name": "TreeChart",
      "reason": "sub-component"
    },
    {
      "name": "TreemapChart",
      "reason": "sub-component"
    },
    {
      "name": "WaterfallChart",
      "reason": "sub-component"
    },
    {
      "name": "WordCloudChart",
      "reason": "sub-component"
    },
    {
      "name": "ChartSkeleton",
      "reason": "sub-component"
    }
  ],
  "keyboardInteractions": [],
  "tests": [
    "tests/Lumeo.Tests/Components/Chart/ChartLabelHelperTests.cs",
    "tests/Lumeo.Tests/Components/Chart/ChartLoadingStateTests.cs",
    "tests/Lumeo.Tests/Components/Chart/ChartSkeletonTests.cs",
    "tests/Lumeo.Tests/Components/Chart/ChartTests.cs"
  ],
  "mdSummary": "# Chart\n\nDeclarative chart wrapper over ECharts — 30+ types supported.\n\n**Package:** `Lumeo.Charts` · **Category:** Data Display/Charts\n**Docs:** https://lumeo.nativ.sh/components/chart\n\n## Parameters\n| Owner | Name | Type | Default | Description |\n|---|---|---|---|---|\n| Chart | Option | `EChartOption?` | `` |  |\n| Chart | OptionJson | `string?` | `` |  |\n| Chart | Theme | `string?` | `` |  |\n| Chart | EChartsSource | `string?` | `` |  |\n| Chart | Width | `string` | `\"100%\"` |  |\n| Chart | Height | `string` | `\"350px\"` |  |\n| Chart | OnClick | `EventCallback<ChartEventArgs>` | `` |  |\n| Chart | OnMouseOver | `EventCallback<ChartEventArgs>` | `` |  |\n| Chart | ShowLoading | `bool` | `` |  |\n| Chart | Group | `string?` | `` |  |\n| Chart | AspectRatio | `string?` | `` |  |\n| Chart | OnDoubleClick | `EventCallback<ChartEventArgs>` | `` |  |\n| Chart | OnBrushSelected | `EventCallback<ChartEventArgs>` | `` |  |\n| Chart | OnDataZoom | `EventCallback<ChartEventArgs>` | `` |  |\n| Chart | OnInitError | `EventCallback<string>` | `` |  |\n| Chart | Class | `string?` | `` |  |\n| Chart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| Chart | IsLoading | `bool` | `` | When true, forces the loading state on top of the chart (consumer is refetching data). Independent of the JS-driven ECharts loading indicator controlled by ShowLoading. |\n| Chart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders the loading state while the chart has not yet rendered for the first time, and whenever IsLoading is true. Set to false to opt out entirely. |\n| Chart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Generic` | Shape family for the skeleton. Each chart wrapper presets a sensible default (bars for Bar, pie ring for Pie, etc.) — consumers only override when they want a different silhouette. |\n| Chart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with synthesized placeholder data in a muted palette; ECharts animates the option swap to real data. Silhouette falls back to the legacy SVG overlay skeleton. |\n| Chart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds — each tick regenerates the placeholder dataset with a new random seed so bars/lines/slices visibly redistribute while loading. Defaults to 1400ms; clamped to ≥200ms. |\n| Chart | PhantomShapeOverride | `PhantomShape?` | `` | Explicit override for the phantom layout (category count, series count, stacking, area fill). When null (default), the shape is inferred from the real Option so the phantom mirrors the actual chart structure. |\n| Chart | ShowLoadingLabel | `bool` | `true` | When true (default), overlays a small top-right pill containing a spinner and LoadingText on top of the active skeleton — a clear, always-on visual cue that the chart is loading. The pill renders independently of SkeletonStyle so phantom and silhouette both get the same hint. Set to false for bare visuals where the skeleton speaks for itself. |\n| Chart | LoadingText | `string?` | `` | Label rendered next to the spinner inside the loading pill. Set to null or empty to hide the text and show only the spinner. When not set, uses the active locale's \"Chart.Loading\" string (default: \"Loading…\"). |\n| AreaChart | Categories | `List<string>` | `new()` |  |\n| AreaChart | Series | `List<ChartSeriesData>` | `new()` |  |\n| AreaChart | Smooth | `bool` | `` |  |\n| AreaChart | ShowDots | `bool` | `true` |  |\n| AreaChart | Stacked | `bool` | `` |  |\n| AreaChart | Gradient | `bool` | `true` |  |\n| AreaChart | ShowLegend | `bool` | `true` |  |\n| AreaChart | ShowTooltip | `bool` | `true` |  |\n| AreaChart | Colors | `List<string>?` | `` |  |\n| AreaChart | Width | `string` | `\"100%\"` |  |\n| AreaChart | Height | `string` | `\"350px\"` |  |\n| AreaChart | Theme | `string?` | `` |  |\n| AreaChart | Class | `string?` | `` |  |\n| AreaChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| AreaChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| AreaChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| AreaChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Area` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Area for this chart type; override if you want a different silhouette. |\n| AreaChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| AreaChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| AreaChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| AreaChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| AreaChart | ShowDataLabels | `bool` | `` |  |\n| AreaChart | LabelPosition | `string` | `\"top\"` |  |\n| AreaChart | LabelFormat | `string?` | `` |  |\n| AreaChart | LabelStrategy | `ChartLabelStrategy` | `ChartLabelStrategy.Smart` | Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels. |\n| AreaChart | LabelRotate | `int?` | `` | Manual rotation override (degrees) for category-axis labels. |\n| AreaChart | DataZoom | `bool` | `` |  |\n| AreaChart | Toolbox | `bool` | `` |  |\n| AreaChart | ColorPalette | `List<string>?` | `` |  |\n| AreaChart | AnimationDuration | `int?` | `` |  |\n| AreaChart | AnimationEasing | `string?` | `` |  |\n| AreaChart | MarkLine | `EChartMarkLine?` | `` |  |\n| AreaChart | MarkPoint | `EChartMarkPoint?` | `` |  |\n| AreaChart | MarkArea | `EChartMarkArea?` | `` |  |\n| AreaChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| BarChart | Categories | `List<string>` | `new()` |  |\n| BarChart | Series | `List<ChartSeriesData>` | `new()` |  |\n| BarChart | Horizontal | `bool` | `` |  |\n| BarChart | Stacked | `bool` | `` |  |\n| BarChart | ShowLegend | `bool` | `true` |  |\n| BarChart | ShowTooltip | `bool` | `true` |  |\n| BarChart | Colors | `List<string>?` | `` |  |\n| BarChart | Width | `string` | `\"100%\"` |  |\n| BarChart | Height | `string` | `\"350px\"` |  |\n| BarChart | Theme | `string?` | `` |  |\n| BarChart | Class | `string?` | `` |  |\n| BarChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| BarChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| BarChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| BarChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Bars` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Bars for this chart type; override if you want a different silhouette. |\n| BarChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| BarChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| BarChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| BarChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| BarChart | ShowDataLabels | `bool` | `` |  |\n| BarChart | LabelPosition | `string` | `\"top\"` |  |\n| BarChart | LabelFormat | `string?` | `` |  |\n| BarChart | LabelStrategy | `ChartLabelStrategy` | `ChartLabelStrategy.Smart` | Controls how category-axis labels are rendered. Smart (default): show every label; auto-rotate -30° at 11–16 categories, -60° at 17–24, -75° above 24. ShowAll: force every label with no rotation (may overlap on dense datasets). Auto: let ECharts auto-thin labels that would overlap (original ECharts default — hides every second label when crowded). |\n| BarChart | LabelRotate | `int?` | `` | Manual override for category-label rotation in degrees (e.g. -30, -45). When set, takes precedence over the rotation implied by LabelStrategy. |\n| BarChart | DataZoom | `bool` | `` |  |\n| BarChart | Toolbox | `bool` | `` |  |\n| BarChart | ColorPalette | `List<string>?` | `` |  |\n| BarChart | AnimationDuration | `int?` | `` |  |\n| BarChart | AnimationEasing | `string?` | `` |  |\n| BarChart | MarkLine | `EChartMarkLine?` | `` |  |\n| BarChart | MarkPoint | `EChartMarkPoint?` | `` |  |\n| BarChart | MarkArea | `EChartMarkArea?` | `` |  |\n| BarChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| BoxPlotChart | Categories | `List<string>` | `new()` |  |\n| BoxPlotChart | Data | `List<double[]>` | `new()` |  |\n| BoxPlotChart | ShowLegend | `bool` | `true` |  |\n| BoxPlotChart | ShowTooltip | `bool` | `true` |  |\n| BoxPlotChart | Colors | `List<string>?` | `` |  |\n| BoxPlotChart | Width | `string` | `\"100%\"` |  |\n| BoxPlotChart | Height | `string` | `\"350px\"` |  |\n| BoxPlotChart | Theme | `string?` | `` |  |\n| BoxPlotChart | Class | `string?` | `` |  |\n| BoxPlotChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| BoxPlotChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| BoxPlotChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| BoxPlotChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Bars` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Bars for this chart type; override if you want a different silhouette. |\n| BoxPlotChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| BoxPlotChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| BoxPlotChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| BoxPlotChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| BoxPlotChart | ShowDataLabels | `bool` | `` |  |\n| BoxPlotChart | LabelPosition | `string` | `\"top\"` |  |\n| BoxPlotChart | LabelFormat | `string?` | `` |  |\n| BoxPlotChart | LabelStrategy | `ChartLabelStrategy` | `ChartLabelStrategy.Smart` | Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels. |\n| BoxPlotChart | LabelRotate | `int?` | `` | Manual rotation override (degrees) for category-axis labels. |\n| BoxPlotChart | DataZoom | `bool` | `` |  |\n| BoxPlotChart | Toolbox | `bool` | `` |  |\n| BoxPlotChart | ColorPalette | `List<string>?` | `` |  |\n| BoxPlotChart | AnimationDuration | `int?` | `` |  |\n| BoxPlotChart | AnimationEasing | `string?` | `` |  |\n| BoxPlotChart | MarkLine | `EChartMarkLine?` | `` |  |\n| BoxPlotChart | MarkPoint | `EChartMarkPoint?` | `` |  |\n| BoxPlotChart | MarkArea | `EChartMarkArea?` | `` |  |\n| BoxPlotChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| CalendarHeatmapChart | Data | `object?` | `` |  |\n| CalendarHeatmapChart | Year | `int` | `DateTime.Now.Year` |  |\n| CalendarHeatmapChart | VisualMapMin | `double` | `0` |  |\n| CalendarHeatmapChart | VisualMapMax | `double` | `10` |  |\n| CalendarHeatmapChart | Colors | `List<string>?` | `` |  |\n| CalendarHeatmapChart | ShowTooltip | `bool` | `true` |  |\n| CalendarHeatmapChart | Width | `string` | `\"100%\"` |  |\n| CalendarHeatmapChart | Height | `string` | `\"350px\"` |  |\n| CalendarHeatmapChart | Theme | `string?` | `` |  |\n| CalendarHeatmapChart | Class | `string?` | `` |  |\n| CalendarHeatmapChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| CalendarHeatmapChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| CalendarHeatmapChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| CalendarHeatmapChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Grid` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Grid for this chart type; override if you want a different silhouette. |\n| CalendarHeatmapChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| CalendarHeatmapChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| CalendarHeatmapChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| CalendarHeatmapChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| CalendarHeatmapChart | ShowDataLabels | `bool` | `` |  |\n| CalendarHeatmapChart | LabelPosition | `string` | `\"inside\"` |  |\n| CalendarHeatmapChart | LabelFormat | `string?` | `` |  |\n| CalendarHeatmapChart | DataZoom | `bool` | `` |  |\n| CalendarHeatmapChart | Toolbox | `bool` | `` |  |\n| CalendarHeatmapChart | ColorPalette | `List<string>?` | `` |  |\n| CalendarHeatmapChart | AnimationDuration | `int?` | `` |  |\n| CalendarHeatmapChart | AnimationEasing | `string?` | `` |  |\n| CalendarHeatmapChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| CandlestickChart | Categories | `List<string>` | `new()` |  |\n| CandlestickChart | Data | `List<double[]>` | `new()` |  |\n| CandlestickChart | ShowLegend | `bool` | `true` |  |\n| CandlestickChart | ShowTooltip | `bool` | `true` |  |\n| CandlestickChart | Colors | `List<string>?` | `` |  |\n| CandlestickChart | Width | `string` | `\"100%\"` |  |\n| CandlestickChart | Height | `string` | `\"350px\"` |  |\n| CandlestickChart | Theme | `string?` | `` |  |\n| CandlestickChart | Class | `string?` | `` |  |\n| CandlestickChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| CandlestickChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| CandlestickChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| CandlestickChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Candlestick` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Candlestick for this chart type; override if you want a different silhouette. |\n| CandlestickChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| CandlestickChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| CandlestickChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| CandlestickChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| CandlestickChart | ShowDataLabels | `bool` | `` |  |\n| CandlestickChart | LabelPosition | `string` | `\"top\"` |  |\n| CandlestickChart | LabelFormat | `string?` | `` |  |\n| CandlestickChart | LabelStrategy | `ChartLabelStrategy` | `ChartLabelStrategy.Smart` | Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels. |\n| CandlestickChart | LabelRotate | `int?` | `` | Manual rotation override (degrees) for category-axis labels. |\n| CandlestickChart | DataZoom | `bool` | `` |  |\n| CandlestickChart | Toolbox | `bool` | `` |  |\n| CandlestickChart | ColorPalette | `List<string>?` | `` |  |\n| CandlestickChart | AnimationDuration | `int?` | `` |  |\n| CandlestickChart | AnimationEasing | `string?` | `` |  |\n| CandlestickChart | MarkLine | `EChartMarkLine?` | `` |  |\n| CandlestickChart | MarkPoint | `EChartMarkPoint?` | `` |  |\n| CandlestickChart | MarkArea | `EChartMarkArea?` | `` |  |\n| CandlestickChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| ChartSkeleton | Kind | `ChartSkeletonKind` | `ChartSkeletonKind.Generic` |  |\n| ChartSkeleton | Width | `string` | `\"100%\"` |  |\n| ChartSkeleton | Height | `string` | `\"350px\"` |  |\n| ChartSkeleton | Class | `string?` | `` |  |\n| ChartSkeleton | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| DonutChart | Data | `List<DonutChartData>` | `new()` |  |\n| DonutChart | ShowLegend | `bool` | `true` |  |\n| DonutChart | ShowTooltip | `bool` | `true` |  |\n| DonutChart | ShowLabels | `bool` | `true` |  |\n| DonutChart | InnerRadius | `string` | `\"50%\"` |  |\n| DonutChart | OuterRadius | `string` | `\"70%\"` |  |\n| DonutChart | CenterLabel | `string?` | `` |  |\n| DonutChart | Colors | `List<string>?` | `` |  |\n| DonutChart | Width | `string` | `\"100%\"` |  |\n| DonutChart | Height | `string` | `\"350px\"` |  |\n| DonutChart | Theme | `string?` | `` |  |\n| DonutChart | Class | `string?` | `` |  |\n| DonutChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| DonutChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| DonutChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| DonutChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Pie` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette. |\n| DonutChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| DonutChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| DonutChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| DonutChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| DonutChart | ShowDataLabels | `bool` | `` |  |\n| DonutChart | LabelPosition | `string` | `\"outside\"` |  |\n| DonutChart | LabelFormat | `string?` | `` |  |\n| DonutChart | DataZoom | `bool` | `` |  |\n| DonutChart | Toolbox | `bool` | `` |  |\n| DonutChart | ColorPalette | `List<string>?` | `` |  |\n| DonutChart | AnimationDuration | `int?` | `` |  |\n| DonutChart | AnimationEasing | `string?` | `` |  |\n| DonutChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| EffectScatterChart | Series | `List<EffectScatterSeriesData>` | `new()` |  |\n| EffectScatterChart | ShowLegend | `bool` | `true` |  |\n| EffectScatterChart | ShowTooltip | `bool` | `true` |  |\n| EffectScatterChart | Colors | `List<string>?` | `` |  |\n| EffectScatterChart | Width | `string` | `\"100%\"` |  |\n| EffectScatterChart | Height | `string` | `\"350px\"` |  |\n| EffectScatterChart | Theme | `string?` | `` |  |\n| EffectScatterChart | Class | `string?` | `` |  |\n| EffectScatterChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| EffectScatterChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| EffectScatterChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| EffectScatterChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Scatter` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Scatter for this chart type; override if you want a different silhouette. |\n| EffectScatterChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| EffectScatterChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| EffectScatterChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| EffectScatterChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| EffectScatterChart | ShowDataLabels | `bool` | `` |  |\n| EffectScatterChart | LabelPosition | `string` | `\"top\"` |  |\n| EffectScatterChart | LabelFormat | `string?` | `` |  |\n| EffectScatterChart | DataZoom | `bool` | `` |  |\n| EffectScatterChart | Toolbox | `bool` | `` |  |\n| EffectScatterChart | ColorPalette | `List<string>?` | `` |  |\n| EffectScatterChart | AnimationDuration | `int?` | `` |  |\n| EffectScatterChart | AnimationEasing | `string?` | `` |  |\n| EffectScatterChart | MarkLine | `EChartMarkLine?` | `` |  |\n| EffectScatterChart | MarkPoint | `EChartMarkPoint?` | `` |  |\n| EffectScatterChart | MarkArea | `EChartMarkArea?` | `` |  |\n| EffectScatterChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| FunnelChart | Data | `List<FunnelData>` | `new()` |  |\n| FunnelChart | Sort | `string` | `\"descending\"` |  |\n| FunnelChart | ShowLegend | `bool` | `true` |  |\n| FunnelChart | ShowTooltip | `bool` | `true` |  |\n| FunnelChart | Colors | `List<string>?` | `` |  |\n| FunnelChart | Width | `string` | `\"100%\"` |  |\n| FunnelChart | Height | `string` | `\"350px\"` |  |\n| FunnelChart | Theme | `string?` | `` |  |\n| FunnelChart | Class | `string?` | `` |  |\n| FunnelChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| FunnelChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| FunnelChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| FunnelChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Funnel` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Funnel for this chart type; override if you want a different silhouette. |\n| FunnelChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| FunnelChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| FunnelChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| FunnelChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| FunnelChart | ShowDataLabels | `bool` | `` |  |\n| FunnelChart | LabelPosition | `string` | `\"inside\"` |  |\n| FunnelChart | LabelFormat | `string?` | `` |  |\n| FunnelChart | DataZoom | `bool` | `` |  |\n| FunnelChart | Toolbox | `bool` | `` |  |\n| FunnelChart | ColorPalette | `List<string>?` | `` |  |\n| FunnelChart | AnimationDuration | `int?` | `` |  |\n| FunnelChart | AnimationEasing | `string?` | `` |  |\n| FunnelChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| GaugeChart | Value | `double` | `` |  |\n| GaugeChart | Min | `double` | `0` |  |\n| GaugeChart | Max | `double` | `100` |  |\n| GaugeChart | Title | `string?` | `` |  |\n| GaugeChart | Unit | `string?` | `` |  |\n| GaugeChart | ShowPointer | `bool` | `true` |  |\n| GaugeChart | Ranges | `List<GaugeRange>?` | `` |  |\n| GaugeChart | Width | `string` | `\"100%\"` |  |\n| GaugeChart | Height | `string` | `\"350px\"` |  |\n| GaugeChart | Theme | `string?` | `` |  |\n| GaugeChart | Class | `string?` | `` |  |\n| GaugeChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| GaugeChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| GaugeChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| GaugeChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Gauge` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Gauge for this chart type; override if you want a different silhouette. |\n| GaugeChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| GaugeChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| GaugeChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| GaugeChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| GaugeChart | ShowDataLabels | `bool` | `` |  |\n| GaugeChart | LabelPosition | `string` | `\"top\"` |  |\n| GaugeChart | LabelFormat | `string?` | `` |  |\n| GaugeChart | DataZoom | `bool` | `` |  |\n| GaugeChart | Toolbox | `bool` | `` |  |\n| GaugeChart | ColorPalette | `List<string>?` | `` |  |\n| GaugeChart | AnimationDuration | `int?` | `` |  |\n| GaugeChart | AnimationEasing | `string?` | `` |  |\n| GaugeChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| GeoMapChart | MapName | `string` | `\"world\"` |  |\n| GeoMapChart | MapJson | `string?` | `` |  |\n| GeoMapChart | Data | `object?` | `` |  |\n| GeoMapChart | VisualMapMin | `double` | `0` |  |\n| GeoMapChart | VisualMapMax | `double` | `100` |  |\n| GeoMapChart | Roam | `bool` | `true` |  |\n| GeoMapChart | ShowTooltip | `bool` | `true` |  |\n| GeoMapChart | Colors | `List<string>?` | `` |  |\n| GeoMapChart | Width | `string` | `\"100%\"` |  |\n| GeoMapChart | Height | `string` | `\"350px\"` |  |\n| GeoMapChart | Theme | `string?` | `` |  |\n| GeoMapChart | Class | `string?` | `` |  |\n| GeoMapChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| GeoMapChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| GeoMapChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| GeoMapChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Generic` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Generic for this chart type; override if you want a different silhouette. |\n| GeoMapChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| GeoMapChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| GeoMapChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| GeoMapChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| GeoMapChart | ShowDataLabels | `bool` | `` |  |\n| GeoMapChart | LabelPosition | `string` | `\"inside\"` |  |\n| GeoMapChart | LabelFormat | `string?` | `` |  |\n| GeoMapChart | DataZoom | `bool` | `` |  |\n| GeoMapChart | Toolbox | `bool` | `` |  |\n| GeoMapChart | ColorPalette | `List<string>?` | `` |  |\n| GeoMapChart | AnimationDuration | `int?` | `` |  |\n| GeoMapChart | AnimationEasing | `string?` | `` |  |\n| GeoMapChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| GraphChart | Nodes | `List<GraphNode>` | `new()` |  |\n| GraphChart | Links | `List<GraphLink>` | `new()` |  |\n| GraphChart | Categories | `List<GraphCategory>` | `new()` |  |\n| GraphChart | Layout | `string` | `\"force\"` |  |\n| GraphChart | ShowLegend | `bool` | `true` |  |\n| GraphChart | ShowTooltip | `bool` | `true` |  |\n| GraphChart | Roam | `bool` | `true` |  |\n| GraphChart | Colors | `List<string>?` | `` |  |\n| GraphChart | Width | `string` | `\"100%\"` |  |\n| GraphChart | Height | `string` | `\"350px\"` |  |\n| GraphChart | Theme | `string?` | `` |  |\n| GraphChart | Class | `string?` | `` |  |\n| GraphChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| GraphChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| GraphChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| GraphChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Graph` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Graph for this chart type; override if you want a different silhouette. |\n| GraphChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| GraphChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| GraphChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| GraphChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| GraphChart | ShowDataLabels | `bool` | `` |  |\n| GraphChart | LabelPosition | `string` | `\"right\"` |  |\n| GraphChart | LabelFormat | `string?` | `` |  |\n| GraphChart | DataZoom | `bool` | `` |  |\n| GraphChart | Toolbox | `bool` | `` |  |\n| GraphChart | ColorPalette | `List<string>?` | `` |  |\n| GraphChart | AnimationDuration | `int?` | `` |  |\n| GraphChart | AnimationEasing | `string?` | `` |  |\n| GraphChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| HeatmapChart | XCategories | `List<string>` | `new()` |  |\n| HeatmapChart | YCategories | `List<string>` | `new()` |  |\n| HeatmapChart | Data | `List<double[]>` | `new()` |  |\n| HeatmapChart | ShowLegend | `bool` | `true` |  |\n| HeatmapChart | ShowTooltip | `bool` | `true` |  |\n| HeatmapChart | Colors | `List<string>?` | `` |  |\n| HeatmapChart | Width | `string` | `\"100%\"` |  |\n| HeatmapChart | Height | `string` | `\"350px\"` |  |\n| HeatmapChart | Theme | `string?` | `` |  |\n| HeatmapChart | Class | `string?` | `` |  |\n| HeatmapChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| HeatmapChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| HeatmapChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| HeatmapChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Grid` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Grid for this chart type; override if you want a different silhouette. |\n| HeatmapChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| HeatmapChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| HeatmapChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| HeatmapChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| HeatmapChart | ShowDataLabels | `bool` | `` |  |\n| HeatmapChart | LabelPosition | `string` | `\"inside\"` |  |\n| HeatmapChart | LabelFormat | `string?` | `` |  |\n| HeatmapChart | DataZoom | `bool` | `` |  |\n| HeatmapChart | Toolbox | `bool` | `` |  |\n| HeatmapChart | ColorPalette | `List<string>?` | `` |  |\n| HeatmapChart | AnimationDuration | `int?` | `` |  |\n| HeatmapChart | AnimationEasing | `string?` | `` |  |\n| HeatmapChart | MarkLine | `EChartMarkLine?` | `` |  |\n| HeatmapChart | MarkPoint | `EChartMarkPoint?` | `` |  |\n| HeatmapChart | MarkArea | `EChartMarkArea?` | `` |  |\n| HeatmapChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| LineChart | Categories | `List<string>` | `new()` |  |\n| LineChart | Series | `List<ChartSeriesData>` | `new()` |  |\n| LineChart | Smooth | `bool` | `` |  |\n| LineChart | ShowDots | `bool` | `true` |  |\n| LineChart | ShowLegend | `bool` | `true` |  |\n| LineChart | ShowTooltip | `bool` | `true` |  |\n| LineChart | Colors | `List<string>?` | `` |  |\n| LineChart | Width | `string` | `\"100%\"` |  |\n| LineChart | Height | `string` | `\"350px\"` |  |\n| LineChart | Theme | `string?` | `` |  |\n| LineChart | Class | `string?` | `` |  |\n| LineChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| LineChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| LineChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| LineChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Line` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Line for this chart type; override if you want a different silhouette. |\n| LineChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| LineChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| LineChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| LineChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| LineChart | ShowDataLabels | `bool` | `` |  |\n| LineChart | LabelPosition | `string` | `\"top\"` |  |\n| LineChart | LabelFormat | `string?` | `` |  |\n| LineChart | LabelStrategy | `ChartLabelStrategy` | `ChartLabelStrategy.Smart` | Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels. |\n| LineChart | LabelRotate | `int?` | `` | Manual rotation override (degrees) for category-axis labels. |\n| LineChart | DataZoom | `bool` | `` |  |\n| LineChart | Toolbox | `bool` | `` |  |\n| LineChart | ColorPalette | `List<string>?` | `` |  |\n| LineChart | AnimationDuration | `int?` | `` |  |\n| LineChart | AnimationEasing | `string?` | `` |  |\n| LineChart | MarkLine | `EChartMarkLine?` | `` |  |\n| LineChart | MarkPoint | `EChartMarkPoint?` | `` |  |\n| LineChart | MarkArea | `EChartMarkArea?` | `` |  |\n| LineChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| LiquidFillChart | Value | `double` | `0.5` |  |\n| LiquidFillChart | Title | `string?` | `` |  |\n| LiquidFillChart | Shape | `string` | `\"circle\"` |  |\n| LiquidFillChart | Color | `string?` | `` |  |\n| LiquidFillChart | BackgroundColor | `string?` | `` |  |\n| LiquidFillChart | WaveAnimation | `bool` | `true` |  |\n| LiquidFillChart | EChartsSource | `string?` | `` |  |\n| LiquidFillChart | Width | `string` | `\"100%\"` |  |\n| LiquidFillChart | Height | `string` | `\"350px\"` |  |\n| LiquidFillChart | Theme | `string?` | `` |  |\n| LiquidFillChart | Class | `string?` | `` |  |\n| LiquidFillChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| LiquidFillChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| LiquidFillChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| LiquidFillChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Pie` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette. |\n| LiquidFillChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| LiquidFillChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| LiquidFillChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| LiquidFillChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| LiquidFillChart | ShowDataLabels | `bool` | `` |  |\n| LiquidFillChart | LabelPosition | `string` | `\"center\"` |  |\n| LiquidFillChart | LabelFormat | `string?` | `` |  |\n| LiquidFillChart | Toolbox | `bool` | `` |  |\n| LiquidFillChart | ColorPalette | `List<string>?` | `` |  |\n| LiquidFillChart | AnimationDuration | `int?` | `` |  |\n| LiquidFillChart | AnimationEasing | `string?` | `` |  |\n| LiquidFillChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| MixedChart | Categories | `List<string>` | `new()` |  |\n| MixedChart | Series | `List<MixedSeriesData>` | `new()` |  |\n| MixedChart | ShowLegend | `bool` | `true` |  |\n| MixedChart | ShowTooltip | `bool` | `true` |  |\n| MixedChart | Colors | `List<string>?` | `` |  |\n| MixedChart | Width | `string` | `\"100%\"` |  |\n| MixedChart | Height | `string` | `\"350px\"` |  |\n| MixedChart | Theme | `string?` | `` |  |\n| MixedChart | Class | `string?` | `` |  |\n| MixedChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| MixedChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| MixedChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| MixedChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Line` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Line for this chart type; override if you want a different silhouette. |\n| MixedChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| MixedChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| MixedChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| MixedChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| MixedChart | ShowDataLabels | `bool` | `` |  |\n| MixedChart | LabelPosition | `string` | `\"top\"` |  |\n| MixedChart | LabelFormat | `string?` | `` |  |\n| MixedChart | LabelStrategy | `ChartLabelStrategy` | `ChartLabelStrategy.Smart` | Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels. |\n| MixedChart | LabelRotate | `int?` | `` | Manual rotation override (degrees) for category-axis labels. |\n| MixedChart | DataZoom | `bool` | `` |  |\n| MixedChart | Toolbox | `bool` | `` |  |\n| MixedChart | ColorPalette | `List<string>?` | `` |  |\n| MixedChart | AnimationDuration | `int?` | `` |  |\n| MixedChart | AnimationEasing | `string?` | `` |  |\n| MixedChart | MarkLine | `EChartMarkLine?` | `` |  |\n| MixedChart | MarkPoint | `EChartMarkPoint?` | `` |  |\n| MixedChart | MarkArea | `EChartMarkArea?` | `` |  |\n| MixedChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| NightingaleChart | Data | `List<NightingaleData>` | `new()` |  |\n| NightingaleChart | ShowLegend | `bool` | `true` |  |\n| NightingaleChart | ShowTooltip | `bool` | `true` |  |\n| NightingaleChart | ShowLabels | `bool` | `true` |  |\n| NightingaleChart | Colors | `List<string>?` | `` |  |\n| NightingaleChart | Width | `string` | `\"100%\"` |  |\n| NightingaleChart | Height | `string` | `\"350px\"` |  |\n| NightingaleChart | Theme | `string?` | `` |  |\n| NightingaleChart | Class | `string?` | `` |  |\n| NightingaleChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| NightingaleChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| NightingaleChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| NightingaleChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Pie` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette. |\n| NightingaleChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| NightingaleChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| NightingaleChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| NightingaleChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| NightingaleChart | ShowDataLabels | `bool` | `` |  |\n| NightingaleChart | LabelPosition | `string` | `\"outside\"` |  |\n| NightingaleChart | LabelFormat | `string?` | `` |  |\n| NightingaleChart | DataZoom | `bool` | `` |  |\n| NightingaleChart | Toolbox | `bool` | `` |  |\n| NightingaleChart | ColorPalette | `List<string>?` | `` |  |\n| NightingaleChart | AnimationDuration | `int?` | `` |  |\n| NightingaleChart | AnimationEasing | `string?` | `` |  |\n| NightingaleChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| ParallelChart | Dimensions | `List<ParallelDimension>` | `new()` |  |\n| ParallelChart | Series | `List<ParallelSeriesData>` | `new()` |  |\n| ParallelChart | ShowLegend | `bool` | `true` |  |\n| ParallelChart | ShowTooltip | `bool` | `true` |  |\n| ParallelChart | Colors | `List<string>?` | `` |  |\n| ParallelChart | Width | `string` | `\"100%\"` |  |\n| ParallelChart | Height | `string` | `\"350px\"` |  |\n| ParallelChart | Theme | `string?` | `` |  |\n| ParallelChart | Class | `string?` | `` |  |\n| ParallelChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| ParallelChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| ParallelChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| ParallelChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Parallel` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Parallel for this chart type; override if you want a different silhouette. |\n| ParallelChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| ParallelChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| ParallelChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| ParallelChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| ParallelChart | ShowDataLabels | `bool` | `` |  |\n| ParallelChart | LabelPosition | `string` | `\"top\"` |  |\n| ParallelChart | LabelFormat | `string?` | `` |  |\n| ParallelChart | DataZoom | `bool` | `` |  |\n| ParallelChart | Toolbox | `bool` | `` |  |\n| ParallelChart | ColorPalette | `List<string>?` | `` |  |\n| ParallelChart | AnimationDuration | `int?` | `` |  |\n| ParallelChart | AnimationEasing | `string?` | `` |  |\n| ParallelChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| PictorialBarChart | Categories | `List<string>` | `new()` |  |\n| PictorialBarChart | Series | `List<PictorialSeriesData>` | `new()` |  |\n| PictorialBarChart | ShowLegend | `bool` | `true` |  |\n| PictorialBarChart | ShowTooltip | `bool` | `true` |  |\n| PictorialBarChart | Colors | `List<string>?` | `` |  |\n| PictorialBarChart | Width | `string` | `\"100%\"` |  |\n| PictorialBarChart | Height | `string` | `\"350px\"` |  |\n| PictorialBarChart | Theme | `string?` | `` |  |\n| PictorialBarChart | Class | `string?` | `` |  |\n| PictorialBarChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| PictorialBarChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| PictorialBarChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| PictorialBarChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Bars` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Bars for this chart type; override if you want a different silhouette. |\n| PictorialBarChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| PictorialBarChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| PictorialBarChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| PictorialBarChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| PictorialBarChart | ShowDataLabels | `bool` | `` |  |\n| PictorialBarChart | LabelPosition | `string` | `\"top\"` |  |\n| PictorialBarChart | LabelFormat | `string?` | `` |  |\n| PictorialBarChart | LabelStrategy | `ChartLabelStrategy` | `ChartLabelStrategy.Smart` | Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels. |\n| PictorialBarChart | LabelRotate | `int?` | `` | Manual rotation override (degrees) for category-axis labels. |\n| PictorialBarChart | DataZoom | `bool` | `` |  |\n| PictorialBarChart | Toolbox | `bool` | `` |  |\n| PictorialBarChart | ColorPalette | `List<string>?` | `` |  |\n| PictorialBarChart | AnimationDuration | `int?` | `` |  |\n| PictorialBarChart | AnimationEasing | `string?` | `` |  |\n| PictorialBarChart | MarkLine | `EChartMarkLine?` | `` |  |\n| PictorialBarChart | MarkPoint | `EChartMarkPoint?` | `` |  |\n| PictorialBarChart | MarkArea | `EChartMarkArea?` | `` |  |\n| PictorialBarChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| PieChart | Data | `List<PieChartData>` | `new()` |  |\n| PieChart | ShowLegend | `bool` | `true` |  |\n| PieChart | ShowTooltip | `bool` | `true` |  |\n| PieChart | ShowLabels | `bool` | `true` |  |\n| PieChart | Colors | `List<string>?` | `` |  |\n| PieChart | Width | `string` | `\"100%\"` |  |\n| PieChart | Height | `string` | `\"350px\"` |  |\n| PieChart | Theme | `string?` | `` |  |\n| PieChart | Class | `string?` | `` |  |\n| PieChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| PieChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| PieChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| PieChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Pie` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette. |\n| PieChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| PieChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| PieChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| PieChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| PieChart | ShowDataLabels | `bool` | `` |  |\n| PieChart | LabelPosition | `string` | `\"outside\"` |  |\n| PieChart | LabelFormat | `string?` | `` |  |\n| PieChart | DataZoom | `bool` | `` |  |\n| PieChart | Toolbox | `bool` | `` |  |\n| PieChart | ColorPalette | `List<string>?` | `` |  |\n| PieChart | AnimationDuration | `int?` | `` |  |\n| PieChart | AnimationEasing | `string?` | `` |  |\n| PieChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| PolarBarChart | Categories | `List<string>` | `new()` |  |\n| PolarBarChart | Values | `List<double>` | `new()` |  |\n| PolarBarChart | ShowLegend | `bool` | `false` |  |\n| PolarBarChart | ShowTooltip | `bool` | `true` |  |\n| PolarBarChart | Colors | `List<string>?` | `` |  |\n| PolarBarChart | Width | `string` | `\"100%\"` |  |\n| PolarBarChart | Height | `string` | `\"350px\"` |  |\n| PolarBarChart | Theme | `string?` | `` |  |\n| PolarBarChart | Class | `string?` | `` |  |\n| PolarBarChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| PolarBarChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| PolarBarChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| PolarBarChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Polar` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Polar for this chart type; override if you want a different silhouette. |\n| PolarBarChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| PolarBarChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| PolarBarChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| PolarBarChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| PolarBarChart | ShowDataLabels | `bool` | `` |  |\n| PolarBarChart | LabelPosition | `string` | `\"inside\"` |  |\n| PolarBarChart | LabelFormat | `string?` | `` |  |\n| PolarBarChart | DataZoom | `bool` | `` |  |\n| PolarBarChart | Toolbox | `bool` | `` |  |\n| PolarBarChart | ColorPalette | `List<string>?` | `` |  |\n| PolarBarChart | AnimationDuration | `int?` | `` |  |\n| PolarBarChart | AnimationEasing | `string?` | `` |  |\n| PolarBarChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| RadarChart | Indicators | `List<RadarIndicator>` | `new()` |  |\n| RadarChart | Series | `List<RadarSeriesData>` | `new()` |  |\n| RadarChart | ShowLegend | `bool` | `true` |  |\n| RadarChart | ShowTooltip | `bool` | `true` |  |\n| RadarChart | Shape | `string` | `\"polygon\"` |  |\n| RadarChart | FillArea | `bool` | `true` |  |\n| RadarChart | Colors | `List<string>?` | `` |  |\n| RadarChart | Width | `string` | `\"100%\"` |  |\n| RadarChart | Height | `string` | `\"350px\"` |  |\n| RadarChart | Theme | `string?` | `` |  |\n| RadarChart | Class | `string?` | `` |  |\n| RadarChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| RadarChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| RadarChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| RadarChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Radar` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Radar for this chart type; override if you want a different silhouette. |\n| RadarChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| RadarChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| RadarChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| RadarChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| RadarChart | ShowDataLabels | `bool` | `` |  |\n| RadarChart | LabelPosition | `string` | `\"top\"` |  |\n| RadarChart | LabelFormat | `string?` | `` |  |\n| RadarChart | DataZoom | `bool` | `` |  |\n| RadarChart | Toolbox | `bool` | `` |  |\n| RadarChart | ColorPalette | `List<string>?` | `` |  |\n| RadarChart | AnimationDuration | `int?` | `` |  |\n| RadarChart | AnimationEasing | `string?` | `` |  |\n| RadarChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| RadialChart | Value | `double` | `` |  |\n| RadialChart | Min | `double` | `0` |  |\n| RadialChart | Max | `double` | `100` |  |\n| RadialChart | Label | `string?` | `` |  |\n| RadialChart | Color | `string?` | `` |  |\n| RadialChart | ShowProgress | `bool` | `true` |  |\n| RadialChart | Width | `string` | `\"100%\"` |  |\n| RadialChart | Height | `string` | `\"350px\"` |  |\n| RadialChart | Theme | `string?` | `` |  |\n| RadialChart | Class | `string?` | `` |  |\n| RadialChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| RadialChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| RadialChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| RadialChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Pie` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette. |\n| RadialChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| RadialChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| RadialChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| RadialChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| RadialChart | ShowDataLabels | `bool` | `` |  |\n| RadialChart | LabelPosition | `string` | `\"top\"` |  |\n| RadialChart | LabelFormat | `string?` | `` |  |\n| RadialChart | DataZoom | `bool` | `` |  |\n| RadialChart | Toolbox | `bool` | `` |  |\n| RadialChart | ColorPalette | `List<string>?` | `` |  |\n| RadialChart | AnimationDuration | `int?` | `` |  |\n| RadialChart | AnimationEasing | `string?` | `` |  |\n| RadialChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| SankeyChart | Nodes | `List<SankeyNode>` | `new()` |  |\n| SankeyChart | Links | `List<SankeyLink>` | `new()` |  |\n| SankeyChart | ShowLegend | `bool` | `true` |  |\n| SankeyChart | ShowTooltip | `bool` | `true` |  |\n| SankeyChart | Colors | `List<string>?` | `` |  |\n| SankeyChart | Width | `string` | `\"100%\"` |  |\n| SankeyChart | Height | `string` | `\"350px\"` |  |\n| SankeyChart | Theme | `string?` | `` |  |\n| SankeyChart | Class | `string?` | `` |  |\n| SankeyChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| SankeyChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| SankeyChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| SankeyChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Sankey` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Sankey for this chart type; override if you want a different silhouette. |\n| SankeyChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| SankeyChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| SankeyChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| SankeyChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| SankeyChart | ShowDataLabels | `bool` | `` |  |\n| SankeyChart | LabelPosition | `string` | `\"right\"` |  |\n| SankeyChart | LabelFormat | `string?` | `` |  |\n| SankeyChart | DataZoom | `bool` | `` |  |\n| SankeyChart | Toolbox | `bool` | `` |  |\n| SankeyChart | ColorPalette | `List<string>?` | `` |  |\n| SankeyChart | AnimationDuration | `int?` | `` |  |\n| SankeyChart | AnimationEasing | `string?` | `` |  |\n| SankeyChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| ScatterChart | Series | `List<ScatterSeriesData>` | `new()` |  |\n| ScatterChart | ShowLegend | `bool` | `true` |  |\n| ScatterChart | ShowTooltip | `bool` | `true` |  |\n| ScatterChart | BubbleSize | `int?` | `` |  |\n| ScatterChart | Colors | `List<string>?` | `` |  |\n| ScatterChart | Width | `string` | `\"100%\"` |  |\n| ScatterChart | Height | `string` | `\"350px\"` |  |\n| ScatterChart | Theme | `string?` | `` |  |\n| ScatterChart | Class | `string?` | `` |  |\n| ScatterChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| ScatterChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| ScatterChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| ScatterChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Scatter` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Scatter for this chart type; override if you want a different silhouette. |\n| ScatterChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| ScatterChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| ScatterChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| ScatterChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| ScatterChart | ShowDataLabels | `bool` | `` |  |\n| ScatterChart | LabelPosition | `string` | `\"top\"` |  |\n| ScatterChart | LabelFormat | `string?` | `` |  |\n| ScatterChart | DataZoom | `bool` | `` |  |\n| ScatterChart | Toolbox | `bool` | `` |  |\n| ScatterChart | ColorPalette | `List<string>?` | `` |  |\n| ScatterChart | AnimationDuration | `int?` | `` |  |\n| ScatterChart | AnimationEasing | `string?` | `` |  |\n| ScatterChart | MarkLine | `EChartMarkLine?` | `` |  |\n| ScatterChart | MarkPoint | `EChartMarkPoint?` | `` |  |\n| ScatterChart | MarkArea | `EChartMarkArea?` | `` |  |\n| ScatterChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| SunburstChart | Data | `List<SunburstData>` | `new()` |  |\n| SunburstChart | ShowLegend | `bool` | `true` |  |\n| SunburstChart | ShowTooltip | `bool` | `true` |  |\n| SunburstChart | Colors | `List<string>?` | `` |  |\n| SunburstChart | Width | `string` | `\"100%\"` |  |\n| SunburstChart | Height | `string` | `\"350px\"` |  |\n| SunburstChart | Theme | `string?` | `` |  |\n| SunburstChart | Class | `string?` | `` |  |\n| SunburstChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| SunburstChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| SunburstChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| SunburstChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Pie` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Pie for this chart type; override if you want a different silhouette. |\n| SunburstChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| SunburstChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| SunburstChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| SunburstChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| SunburstChart | ShowDataLabels | `bool` | `` |  |\n| SunburstChart | LabelPosition | `string` | `\"inside\"` |  |\n| SunburstChart | LabelFormat | `string?` | `` |  |\n| SunburstChart | DataZoom | `bool` | `` |  |\n| SunburstChart | Toolbox | `bool` | `` |  |\n| SunburstChart | ColorPalette | `List<string>?` | `` |  |\n| SunburstChart | AnimationDuration | `int?` | `` |  |\n| SunburstChart | AnimationEasing | `string?` | `` |  |\n| SunburstChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| ThemeRiverChart | Data | `List<string[]>` | `new()` |  |\n| ThemeRiverChart | ShowLegend | `bool` | `true` |  |\n| ThemeRiverChart | ShowTooltip | `bool` | `true` |  |\n| ThemeRiverChart | Colors | `List<string>?` | `` |  |\n| ThemeRiverChart | Width | `string` | `\"100%\"` |  |\n| ThemeRiverChart | Height | `string` | `\"350px\"` |  |\n| ThemeRiverChart | Theme | `string?` | `` |  |\n| ThemeRiverChart | Class | `string?` | `` |  |\n| ThemeRiverChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| ThemeRiverChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| ThemeRiverChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| ThemeRiverChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Generic` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Generic for this chart type; override if you want a different silhouette. |\n| ThemeRiverChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| ThemeRiverChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| ThemeRiverChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| ThemeRiverChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| ThemeRiverChart | ShowDataLabels | `bool` | `` |  |\n| ThemeRiverChart | LabelPosition | `string` | `\"top\"` |  |\n| ThemeRiverChart | LabelFormat | `string?` | `` |  |\n| ThemeRiverChart | DataZoom | `bool` | `` |  |\n| ThemeRiverChart | Toolbox | `bool` | `` |  |\n| ThemeRiverChart | ColorPalette | `List<string>?` | `` |  |\n| ThemeRiverChart | AnimationDuration | `int?` | `` |  |\n| ThemeRiverChart | AnimationEasing | `string?` | `` |  |\n| ThemeRiverChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| TreeChart | Data | `TreeData?` | `` |  |\n| TreeChart | Orient | `string` | `\"LR\"` |  |\n| TreeChart | ShowLabels | `bool` | `true` |  |\n| TreeChart | ShowTooltip | `bool` | `true` |  |\n| TreeChart | Colors | `List<string>?` | `` |  |\n| TreeChart | Width | `string` | `\"100%\"` |  |\n| TreeChart | Height | `string` | `\"350px\"` |  |\n| TreeChart | Theme | `string?` | `` |  |\n| TreeChart | Class | `string?` | `` |  |\n| TreeChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| TreeChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| TreeChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| TreeChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Tree` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Tree for this chart type; override if you want a different silhouette. |\n| TreeChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| TreeChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| TreeChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| TreeChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| TreeChart | ShowDataLabels | `bool` | `` |  |\n| TreeChart | LabelPosition | `string` | `\"right\"` |  |\n| TreeChart | LabelFormat | `string?` | `` |  |\n| TreeChart | DataZoom | `bool` | `` |  |\n| TreeChart | Toolbox | `bool` | `` |  |\n| TreeChart | ColorPalette | `List<string>?` | `` |  |\n| TreeChart | AnimationDuration | `int?` | `` |  |\n| TreeChart | AnimationEasing | `string?` | `` |  |\n| TreeChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| TreemapChart | Data | `List<TreemapData>` | `new()` |  |\n| TreemapChart | ShowLegend | `bool` | `true` |  |\n| TreemapChart | ShowTooltip | `bool` | `true` |  |\n| TreemapChart | Colors | `List<string>?` | `` |  |\n| TreemapChart | Width | `string` | `\"100%\"` |  |\n| TreemapChart | Height | `string` | `\"350px\"` |  |\n| TreemapChart | Theme | `string?` | `` |  |\n| TreemapChart | Class | `string?` | `` |  |\n| TreemapChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| TreemapChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| TreemapChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| TreemapChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Grid` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Grid for this chart type; override if you want a different silhouette. |\n| TreemapChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| TreemapChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| TreemapChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| TreemapChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| TreemapChart | ShowDataLabels | `bool` | `` |  |\n| TreemapChart | LabelPosition | `string` | `\"inside\"` |  |\n| TreemapChart | LabelFormat | `string?` | `` |  |\n| TreemapChart | DataZoom | `bool` | `` |  |\n| TreemapChart | Toolbox | `bool` | `` |  |\n| TreemapChart | ColorPalette | `List<string>?` | `` |  |\n| TreemapChart | AnimationDuration | `int?` | `` |  |\n| TreemapChart | AnimationEasing | `string?` | `` |  |\n| TreemapChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| WaterfallChart | Categories | `List<string>` | `new()` |  |\n| WaterfallChart | Values | `List<double>` | `new()` |  |\n| WaterfallChart | IncreaseColor | `string` | `\"var(--color-primary)\"` |  |\n| WaterfallChart | DecreaseColor | `string` | `\"var(--color-destructive)\"` |  |\n| WaterfallChart | ShowLegend | `bool` | `false` |  |\n| WaterfallChart | ShowTooltip | `bool` | `true` |  |\n| WaterfallChart | Colors | `List<string>?` | `` |  |\n| WaterfallChart | Width | `string` | `\"100%\"` |  |\n| WaterfallChart | Height | `string` | `\"350px\"` |  |\n| WaterfallChart | Theme | `string?` | `` |  |\n| WaterfallChart | Class | `string?` | `` |  |\n| WaterfallChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| WaterfallChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| WaterfallChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| WaterfallChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Bars` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Bars for this chart type; override if you want a different silhouette. |\n| WaterfallChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| WaterfallChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| WaterfallChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| WaterfallChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| WaterfallChart | ShowDataLabels | `bool` | `` |  |\n| WaterfallChart | LabelPosition | `string` | `\"top\"` |  |\n| WaterfallChart | LabelFormat | `string?` | `` |  |\n| WaterfallChart | LabelStrategy | `ChartLabelStrategy` | `ChartLabelStrategy.Smart` | Controls category-axis label rendering. Smart auto-rotates at higher densities; ShowAll forces all horizontal; Auto thins overlapping labels. |\n| WaterfallChart | LabelRotate | `int?` | `` | Manual rotation override (degrees) for category-axis labels. |\n| WaterfallChart | DataZoom | `bool` | `` |  |\n| WaterfallChart | Toolbox | `bool` | `` |  |\n| WaterfallChart | ColorPalette | `List<string>?` | `` |  |\n| WaterfallChart | AnimationDuration | `int?` | `` |  |\n| WaterfallChart | AnimationEasing | `string?` | `` |  |\n| WaterfallChart | MarkLine | `EChartMarkLine?` | `` |  |\n| WaterfallChart | MarkPoint | `EChartMarkPoint?` | `` |  |\n| WaterfallChart | MarkArea | `EChartMarkArea?` | `` |  |\n| WaterfallChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n| WordCloudChart | Words | `List<WordCloudItem>?` | `` |  |\n| WordCloudChart | Shape | `string` | `\"circle\"` |  |\n| WordCloudChart | SizeRange | `int[]` | `new[] { 12, 60 }` |  |\n| WordCloudChart | RotationRange | `int[]` | `new[] { -90, 90 }` |  |\n| WordCloudChart | EChartsSource | `string?` | `` |  |\n| WordCloudChart | ShowTooltip | `bool` | `true` |  |\n| WordCloudChart | Width | `string` | `\"100%\"` |  |\n| WordCloudChart | Height | `string` | `\"350px\"` |  |\n| WordCloudChart | Theme | `string?` | `` |  |\n| WordCloudChart | Class | `string?` | `` |  |\n| WordCloudChart | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n| WordCloudChart | IsLoading | `bool` | `` | When true, overlays a shape-aware loading skeleton on top of the chart (consumer is fetching data). Flips back to the real chart once set to false. |\n| WordCloudChart | ShowLoadingSkeleton | `bool` | `true` | When true (default), renders a shape-aware skeleton while the chart has not yet mounted and whenever IsLoading is true. Set to false to opt out. |\n| WordCloudChart | SkeletonKind | `ChartSkeletonKind` | `ChartSkeletonKind.Generic` | Shape family for the loading skeleton. Defaults to ChartSkeletonKind.Generic for this chart type; override if you want a different silhouette. |\n| WordCloudChart | SkeletonStyle | `ChartSkeletonStyle` | `ChartSkeletonStyle.Phantom` | Phantom (default) renders the real chart with placeholder data and morphs to real data via ECharts' option-change animation. Silhouette falls back to the legacy SVG skeleton overlay. |\n| WordCloudChart | PhantomCycleMs | `int` | `1400` | Phantom-mode refresh interval in milliseconds. Lower = faster data cycling while loading. Defaults to 1400. |\n| WordCloudChart | ShowLoadingLabel | `bool` | `true` | When true (default), shows a small \"Loading…\" pill in the top-right corner whenever IsLoading is active. Works on top of any SkeletonStyle. |\n| WordCloudChart | LoadingText | `string?` | `\"Loading…\"` | Label rendered next to the spinner inside the loading pill. Set to empty to hide. Defaults to \"Loading…\". |\n| WordCloudChart | ShowDataLabels | `bool` | `` |  |\n| WordCloudChart | LabelPosition | `string` | `\"center\"` |  |\n| WordCloudChart | LabelFormat | `string?` | `` |  |\n| WordCloudChart | Toolbox | `bool` | `` |  |\n| WordCloudChart | ColorPalette | `List<string>?` | `` |  |\n| WordCloudChart | AnimationDuration | `int?` | `` |  |\n| WordCloudChart | AnimationEasing | `string?` | `` |  |\n| WordCloudChart | OptionOverride | `Dictionary<string, object>?` | `` |  |\n\n## Slots\n- (none)\n\n## Events\n- `OnClick` (`EventCallback<ChartEventArgs>`)\n- `OnMouseOver` (`EventCallback<ChartEventArgs>`)\n- `OnDoubleClick` (`EventCallback<ChartEventArgs>`)\n- `OnBrushSelected` (`EventCallback<ChartEventArgs>`)\n- `OnDataZoom` (`EventCallback<ChartEventArgs>`)\n- `OnInitError` (`EventCallback<string>`)\n\n## Sub-components\n- `AreaChart`, `BarChart`, `BoxPlotChart`, `CalendarHeatmapChart`, `CandlestickChart`, `DonutChart`, `EffectScatterChart`, `FunnelChart`, `GaugeChart`, `GeoMapChart`, `GraphChart`, `HeatmapChart`, `LineChart`, `LiquidFillChart`, `MixedChart`, `NightingaleChart`, `ParallelChart`, `PictorialBarChart`, `PieChart`, `PolarBarChart`, `RadarChart`, `RadialChart`, `SankeyChart`, `ScatterChart`, `SunburstChart`, `ThemeRiverChart`, `TreeChart`, `TreemapChart`, `WaterfallChart`, `WordCloudChart`, `ChartSkeleton`\n\n## Cascading values it expects\n- (none)\n\n## Services it injects\n- `ComponentInteropService` ()\n- `ILumeoLocalizer` (Lumeo.Services.Localization)\n\n## Keyboard\n—\n\n## Example\n```razor\n<Chart />\n```\n\n## CSS variables\n`--color-background`, `--color-border`, `--color-destructive`, `--color-muted`, `--color-muted-foreground`, `--radius`\n\n## Files\n- `UI/Chart/Chart.razor`\n- `UI/Chart/ChartHelper.cs`\n- `UI/Chart/ChartLabelHelper.cs`\n- `UI/Chart/ChartPlaceholderFactory.cs`\n- `UI/Chart/Charts/AreaChart.razor`\n- `UI/Chart/Charts/BarChart.razor`\n- `UI/Chart/Charts/BoxPlotChart.razor`\n- `UI/Chart/Charts/CalendarHeatmapChart.razor`\n- `UI/Chart/Charts/CandlestickChart.razor`\n- `UI/Chart/Charts/DonutChart.razor`\n- `UI/Chart/Charts/EffectScatterChart.razor`\n- `UI/Chart/Charts/FunnelChart.razor`\n- `UI/Chart/Charts/GaugeChart.razor`\n- `UI/Chart/Charts/GeoMapChart.razor`\n- `UI/Chart/Charts/GraphChart.razor`\n- `UI/Chart/Charts/HeatmapChart.razor`\n- `UI/Chart/Charts/LineChart.razor`\n- `UI/Chart/Charts/LiquidFillChart.razor`\n- `UI/Chart/Charts/MixedChart.razor`\n- `UI/Chart/Charts/NightingaleChart.razor`\n- `UI/Chart/Charts/ParallelChart.razor`\n- `UI/Chart/Charts/PictorialBarChart.razor`\n- `UI/Chart/Charts/PieChart.razor`\n- `UI/Chart/Charts/PolarBarChart.razor`\n- `UI/Chart/Charts/RadarChart.razor`\n- `UI/Chart/Charts/RadialChart.razor`\n- `UI/Chart/Charts/SankeyChart.razor`\n- `UI/Chart/Charts/ScatterChart.razor`\n- `UI/Chart/Charts/SunburstChart.razor`\n- `UI/Chart/Charts/ThemeRiverChart.razor`\n- `UI/Chart/Charts/TreeChart.razor`\n- `UI/Chart/Charts/TreemapChart.razor`\n- `UI/Chart/Charts/WaterfallChart.razor`\n- `UI/Chart/Charts/WordCloudChart.razor`\n- `UI/Chart/ChartSkeleton.razor`\n- `UI/Chart/ChartSkeletonKind.cs`\n- `UI/Chart/EChartOption.cs`\n"
}