Files

27 lines
116 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
window.EASYCHART_MANUAL_EN = window.EASYCHART_MANUAL_EN || {};
window.EASYCHART_MANUAL_EN.generatedAt = "2026-03-18 17:29:31";
window.EASYCHART_MANUAL_EN.chapters = [
{ id: "00_00-Index", relPath: "00_00-Index.md", title: "Quick Overview", content: "# Quick Overview\n\nThis manual is intended for a workflow where you configure charts (the `ChartProfile` asset) in the Unity Editor via **`EasyChartLibraryWindow`**.\n\n- Menu entry: `EasyChart/Library Editor`\n- Manual viewer: `EasyChart/Manual`\n\n---\n\n## Table of Contents\n\n### A. Getting Started & Workflows\n\n- [Quick overview](./00_00-Index.md)\n- [Quick start: create your first chart in 2 minutes](./00_01-QuickStart.md)\n- [UIToolKit workflow (recommended)](./00_02-WorkflowAndLibrary.md)\n- [UGUI workflow](./00_03-UGUIWorkflow.md)\n- [Runtime data injection (UIToolKit)](./00_04-RuntimeDataInjectionUIToolKit.md)\n- [Runtime data injection (UGUI)](./00_05-RuntimeDataInjectionUGUI.md)\n\n### B. Editor & Panels\n\n- [Editor workflow and panels](./01_01-EditorWorkflow.md)\n- [Library panel (asset tree)](./01_02-LibraryPanel.md)\n- [JSON Injection panel](./01_03-JsonInjectionPanel.md)\n- [Preview panel](./02_04-PreviewPanel.md)\n- [Inspector panel](./02_05-InspectorPanel.md)\n- [Series panel](./02_06-SeriesPanel.md)\n\n### C. Series Configuration (Goal-Oriented)\n\n- [Line chart](./03_01-LineChart.md)\n- [Bar chart](./03_02-BarChart.md)\n- [Scatter chart](./03_03-ScatterChart.md)\n- [Heatmap chart](./03_04-HeatmapChart.md)\n- [Radar chart](./03_05-RadarChart.md)\n- [Pie chart](./03_06-PieChart.md)\n- [Ring chart](./03_07-RingChart.md)\n\n### D. Reference (Lookup by Field)\n\n- [Common recipes](./04_08-CommonRecipes.md)\n- [FAQ (fastest troubleshooting path)](./04_09-FAQ.md)\n\n### E. Updates & Roadmap\n\n- [Roadmap / update plan](./05_01-UpdatePlan.md)\n\n---\n\n## Conventions & Terminology\n\n- **ChartProfile**: A chart configuration asset (reusable; previewable in the editor).\n- **Series / Serie**: A data series (e.g. one line in a line chart, or one group of bars in a bar chart).\n- **SeriesData**: The set of data points in a series.\n- **Axis**: Axis configuration (`AxisType=Category/Value`).\n- **Category**: Category axis (uses the `labels` list).\n- **Value**: Value axis (continuous numeric range).\n\n---\n\n## Recommended Project Structure\n\nRecommended to create a dedicated folder in your project for chart assets:\n\n- `Assets/EasyChart/Library/Custom/`: your own `ChartProfile` assets\n- `Assets/EasyChart/Docs/Manual/`: this manual (Markdown chapters)\n\n---\n\n## Manual Version\n\n- This manual will be kept in sync with EasyChart field and editor feature updates.\n" },
{ id: "00_01-QuickStart", relPath: "00_01-QuickStart.md", title: "Quick Start: Create Your First Chart in 2 Minutes", content: "# Quick Start: Create Your First Chart in 2 Minutes\n\nGoal of this chapter: follow the fastest recommended EasyChart path to complete the loop of **Edit → Export → Use in UI**.\n\n---\n\n## Open the Editor Window\n\nIn the Unity menu bar, choose:\n\n- `EasyChart/Library Editor`\n\nYou will see a window with sections like a library tree, configuration panels, and a preview area (later chapters explain each area).\n\n---\n\n## Clone a Library (Recommended)\n\nIf you want to get started quickly and keep a consistent style, it's recommended to:\n\n- Select an existing Library from the top toolbar (e.g. a built-in Demo library)\n- Click **Clone** on the toolbar to create your personal library (e.g. `MyLibrary`)\n\nThis way, all subsequent changes happen in your own library, avoiding modifications to the original examples.\n\n---\n\n## Clone a ChartProfile (Recommended)\n\nIn the library tree, find a chart (`ChartProfile`) close to what you want, then right-click:\n\n- `Clone`\n\nAfter cloning, you'll get a new Profile (a \"variant\" of the original). Select it, and the Inspector on the right will show all editable settings.\n\n---\n\n## Modify Settings and Save\n\nMinimal recommended changes:\n\n- `coordinateSystem`: make sure it matches your intended Series (e.g. Line/Bar/Scatter use `Cartesian2D`)\n- `series`: confirm `type` is correct, and fill in `seriesData`\n- `axes`: at minimum, make sure X/Y axis types match the meaning of your data\n\nAfter editing, click the save button on the top toolbar (if your version has it), or let Unity auto-save the asset.\n\n---\n\n## Export to UXML (Reusable in UI Builder)\n\nThe recommended workflow is exporting the Profile to a reusable `.uxml`:\n\n- Right-click your Profile in the tree\n- Choose `Export to UXML`\n\nThe exported UXML will be placed under:\n\n- `Assets/EasyChart/LibraryUxml/` (Mirror/Backup operations also manage exported assets under this root)\n\n---\n\n## Use It in the QuickStart Scene via UIDocument + UI Builder\n\nOpen the demo scene:\n\n- `Assets/EasyChart/Demo/Scenes/EasyChart_QuickStart.unity`\n\nThen in the Project window, locate:\n\n- `Assets/EasyChart/Demo/UIToolKit/NewUXMLTemplate.uxml`\n\nDouble-click to open it (or open with UI Builder). Next:\n\n- Drag the chart `.uxml` you just exported into the hierarchy of `NewUXMLTemplate.uxml`\n- Save the UXML\n- Make sure the `UIDocument` in the scene references your updated `NewUXMLTemplate.uxml`\n\nRun the scene, and you should see the chart rendered in the UI Toolkit UI.\n\n---\n\n## Alternative: Export as a UGUI Prefab\n\nIf you prefer a UGUI (Canvas/RectTransform) workflow, you can also export the selected Profile as a UGUI prefab in the Library Editor and place it directly into your scene UI (exact menu entry and details depend on your current version).\n\n---\n\n## What to Read Next\n\n- To understand the recommended UI Toolkit workflow: `00_02-WorkflowAndLibrary.md`\n- To use charts with UGUI (Canvas/RectTransform): `00_03-UGUIWorkflow.md`\n\n" },
{ id: "00_02-WorkflowAndLibrary", relPath: "00_02-WorkflowAndLibrary.md", title: "UI Toolkit Workflow (Recommended)", content: "# UI Toolkit Workflow (Recommended)\n\nGoal of this chapter: explain the overall recommended EasyChart approach (primarily for UI Toolkit):\n\n1. Edit `ChartProfile` in the editor with **`EasyChartLibraryWindow`**\n2. **Export** `ChartProfile` **to `.uxml`** as your chart library assets\n3. Compose pages in UI Toolkit using **UI Builder** / UXML, enabling fast UI assembly\n\nThis chapter focuses on **UI Toolkit (recommended)**. If you need the UGUI (Canvas/RectTransform) workflow, see:\n\n- `00_03-UGUIWorkflow.md`\n\n## 0. Why the UI Toolkit workflow is recommended\n\nThe key idea is to separate the \"chart configuration source\" (`ChartProfile`) from the \"UI artifact\" (exported UXML).\n\n- `ChartProfile`: defines what the chart looks like, which axes it uses, what Series it has, and how data should be interpreted. This is best maintained centrally in the editor.\n- Exported `.uxml`: places the chart into a page as a UI Toolkit component. This is best suited for reuse, composition, and version control.\n\nWhat you get:\n\n- Reusable configuration (multiple pages can share one chart style)\n- Composable pages (drag & drop in UI Builder; no need to rebuild UI from scratch each time)\n- Clearer collaboration (Profile as the \"source\", UXML as the \"product/component library\")\n\n---\n\n## 1. Why export to UXML\n\nIn a project, `ChartProfile` describes \"what the chart looks like\", \"which axes it uses\", \"which Series it has\", and \"how data points are interpreted\".\n\nAfter exporting to `.uxml`, you get a reusable UI asset for UI Toolkit:\n\n- Can be dragged directly in UI Builder\n- Can be reused by multiple pages (same chart style)\n- Can be managed by version control and asset pipelines (your \"chart library\")\n\n---\n\n## 2. Recommended workflow (from configuration to page)\n\n### Step 1: Clone your working library and charts (recommended)\n\n- Open from the Unity menu: `EasyChart/Library Editor`\n\nRecommended process:\n\n- **Clone Library**: clone your own Library first (avoid modifying the built-in demo library directly)\n- **Clone ChartProfile**: in your library tree, right-click a Profile close to your target look, then choose `Clone` to create a variant\n- Modify in the Inspector on the right:\n - `coordinateSystem`\n - `series`\n - `axes`\n\n> Recommendation: keep your own Profiles under `Assets/EasyChart/Library/Custom/` (or a team-agreed folder).\n\n### Step 2: Export to UXML (generate library assets)\n\nYou can export from the Library Editor:\n\n- For a Profile: `Export to UXML`\n- For a folder:\n - `Export Folder to UXML (Mirror)`\n - `Export Folder to UXML (Backup)`\n- For all:\n - `Export All UXML (Mirror)`\n - `Export All UXML (Backup)`\n\nExport root folder:\n\n- `Assets/EasyChart/LibraryUxml/`\n\nWith multiple Libraries, the usual structure is:\n\n- `Assets/EasyChart/LibraryUxml/<LibraryName>/...`\n\nThe `_Backups` subfolder is used for backup exports (and some JSON backups generated during exporting):\n\n- `Assets/EasyChart/LibraryUxml/<LibraryName>/_Backups/...`\n\nThe exported UXML usually looks like:\n\n- A `<ec:ChartElement profile-name=\"...\" />`\n- `profile-name` corresponds to the ChartProfile key (usually the asset file name)\n- Width/height styles for the chart are written into the UXML as well\n\n> Key point: treat exported `.uxml` as **reusable chart components**, not something you hand-write for every UI.\n\n### Mirror vs Backup (which one should you use?)\n\n- **Mirror**:\n - used to \"mirror the current Profile state into UXML\"\n - typically overwrites exports with the same name, and may remove stale files that no longer exist (keep the mirror consistent)\n- **Backup**:\n - used to \"export a snapshot\" by time/tag\n - not recommended as the primary path that your pages reference (better for history/rollback)\n\n### Step 3: Compose pages in UI Builder\n\nIn UI Builder:\n\n- Open your page UXML\n- Drag the exported chart `.uxml` from the Project window\n- Combine it with other UI (Label, Button, ListView, etc.) into a full page\n\nFor the quickest export-pipeline verification, you can use the demo scene and template:\n\n- Scene: `Assets/EasyChart/Demo/Scenes/EasyChart_QuickStart.unity`\n- Template: `Assets/EasyChart/Demo/UIToolKit/NewUXMLTemplate.uxml`\n\nDrag the exported chart `.uxml` into `NewUXMLTemplate.uxml`, then make sure the `UIDocument` in the scene references that template.\n\n#### Exact steps in UI Builder (recommended order)\n\n1. Open UI Builder (or double-click your page `.uxml`).\n2. In the Project window, locate the exported chart `.uxml` (usually under `Assets/EasyChart/LibraryUxml/<LibraryName>/...`).\n3. Drag the `.uxml` into the page hierarchy (recommended to put it inside a container `VisualElement`).\n4. Save the page UXML.\n5. Back in the scene, make sure `UIDocument` references the page `.uxml` you just saved.\n\n#### What's inside an exported chart UXML\n\nExported `.uxml` typically contains an `EasyChart.ChartElement` with attributes like:\n\n- `profile-name`: usually the ChartProfile asset file name (key)\n- `profile-guid`: a more stable way to locate the asset\n\nTherefore:\n\n- If you only modify the Profile, the page will not change automatically: you need to re-export (Mirror) so the UXML gets updated.\n- If you rename the Profile asset, the exported `profile-name` will also change (so keep naming stable when possible).\n\n### Step 4: Load/replace data at runtime (depends on your product)\n\n`ChartProfile`/UXML defines the \"style and structure\", while your data usually comes from business logic.\n\n- Static display: fill data directly in the Profile `seriesData`\n- Dynamic display: write/replace `seriesData` at runtime (and keep `SeriesData.id` stable)\n\n---\n\n## 3. Folder layout recommendations for your chart library\n\nRecommended to separate the \"source configuration\" and the \"exported artifacts\":\n\n- `Assets/EasyChart/Library/Custom/`: the `ChartProfile` assets you maintain\n- `Assets/EasyChart/LibraryUxml/`: exported UXML (both Mirror and Backup exports live under this root)\n\nWhen using multiple Libraries, exported assets are typically organized by library name:\n\n- `Assets/EasyChart/LibraryUxml/<LibraryName>/...`\n\nRecommended conventions:\n\n- **Maintain Profiles only under `Assets/EasyChart/Library/...`** (as the source of truth)\n- **Pages reference only Mirror exports** (as your component library)\n- Treat Backup exports purely as **historical snapshots**\n\n> Benefits:\n> - Your configuration source stays readable and editable\n> - Exported artifacts are reusable, composable, and can be used directly in UI Builder\n\n---\n\n## 4. Common issues & troubleshooting\n\n- **Can't find the exported chart UXML in UI Builder**\n - First check whether files were generated under `Assets/EasyChart/LibraryUxml/`\n - If you use multiple Libraries, check under `Assets/EasyChart/LibraryUxml/<LibraryName>/`\n - Mirror/Backup exports may appear under `_Backups`; pages should not reference files under `_Backups`\n\n- **The page references the UXML, but nothing shows at runtime**\n - Make sure the scene `UIDocument` references the page `.uxml` you edited\n - In the Library Editor, use Preview to verify the Profile renders correctly (rule out Profile configuration issues first)\n\n- **You modified the Profile but the page didn't change**\n - The Profile is the \"source\"; the page references exported UXML\n - After modifying a Profile, re-export (Mirror), then return to the page and refresh/save\n\n- **The component is visible in UI Builder, but still doesn't show at runtime**\n - First verify the scene `UIDocument` truly references the intended page (not an older page)\n - Then verify the Profile renders correctly in the Library Editor Preview\n\n---\n\n## 5. What to read next\n\n- To quickly complete a single chart end-to-end: `00_01-QuickStart.md`\n- To use charts with UGUI (Canvas/RectTransform): `00_03-UGUIWorkflow.md`\n\n---\n\n## 6. Editor workflow & panels quick reference (Library Editor)\n\nThis section consolidates the editor workflow and panel explanations that were previously spread across multiple chapters, serving as a quick reference when working in `EasyChart/Library Editor`.\n\n### 6.1 What are you editing? (ChartProfile)\n\nThe chart selected in the Library Editor is essentially a `ChartProfile` asset.\n\n- It's a reusable configuration: the same Profile can be referenced by multiple scenes/prefabs.\n- It's previewable: changes in the editor can be previewed immediately.\n\n### 6.2 Main areas of the Library Editor\n\nYou can think of the window as four areas:\n\n- Left: Library (asset tree)\n- Center: Preview\n- Right: Inspector (configuration)\n- Right: Series (series and data)\n\nAdditionally, there is usually a JSON Injection panel on the left.\n\n### 6.3 Library panel (asset tree)\n\nOverview:\n\n- Displays folders and `ChartProfile` (`.asset`) files in a tree.\n- Selecting a `ChartProfile` drives binding and refresh for Inspector/Series/Preview.\n- Supports create/rename/delete, drag-move, and sorting.\n\nSelection logic:\n\n- Selecting a folder: clears the Inspector/Series panels (no Profile to edit).\n- Selecting a ChartProfile: binds the right panels to that Profile.\n\nCommon actions (title bar and right-click menu; may vary by version):\n\n- Folder: New Folder / New Chart / Export Folder to UXML (Mirror/Backup) / Rename / Delete\n- ChartProfile: Export to UXML / Clone / Rename / Delete\n\n### 6.4 Preview panel\n\nPreview renders the currently selected `ChartProfile` directly, so you can validate changes while editing.\n\nCommon issues:\n\n- Preview is empty: make sure there is at least 1 `Serie` and its `seriesData` is not empty.\n- Data exists but looks wrong: verify CoordinateSystem matches the SeriesType, and axis ranges are not excluding your data.\n\n### 6.5 Inspector panel\n\nInspector edits the serialized fields of the Profile (coordinate system, axes, grid, interaction, legend, etc.), and drives Preview updates.\n\nTip:\n\n- If some field changes appear to have no effect, confirm the page references the exported UXML, not the Profile directly.\n\n### 6.6 Series panel\n\nThe Series panel edits `ChartProfile.series` from a \"chart-structure\" perspective:\n\n- Add/remove/reorder series\n- Choose `type` for each serie and edit `settings`\n- Edit `seriesData` (data points)\n\n### 6.7 JSON Injection panel\n\nPurpose: represent the current Profile as copyable JSON, and support parsing JSON to write back into the current Profile.\n\nRecommended workflow:\n\n1. Generate example JSON from the current Profile\n2. Copy it into an external editor for batch edits\n3. Paste it back and ApplyToChart\n\n---\n\n## 7. Axes & ranges (Axis & Range)\n\n### 7.1 AxisType: Category vs Value\n\n- Category: use `labels` to define discrete categories (A/B/C, or Mon/Tue/Wed).\n- Value: continuous numeric range (0~100, -3~3, 0~1e6).\n\n#### 7.1.1 When to use Category\n\n- The X axis is a sequence of text labels\n- You want points to land on `labels[i]`\n- Typical: bar charts (one bar group per category), line charts (aligned by categories)\n\nKey points for Category:\n\n- `labels[0]` corresponds to category index `0`\n- `labels[1]` corresponds to category index `1`\n\n#### 7.1.2 When to use Value\n\n- X or Y is a continuous numeric value (timestamp, money, temperature, etc.)\n- You want to scale/pan the axis by numeric values\n\nKey points for Value:\n\n- Axis range is usually computed by auto range (if enabled)\n- You can lock only one side (e.g. fix min=0 and keep max auto)\n\n### 7.2 Category axis: labels and LabelPlacement\n\n`labels` determines the number of categories and the label text.\n\n`LabelPlacement` affects alignment:\n\n- `Tick`: labels align to tick marks; better for Line/Scatter.\n- `CellCenter`: labels align to the center of a cell; better for Bar/Heatmap.\n\nCommon symptom:\n\n- Bars appear between two labels: set `LabelPlacement` to `CellCenter`.\n\n### 7.3 Value axis: autoRangeMin / autoRangeMax\n\nIf the range is \"locked\" and data is not visible, revert to full auto range first:\n\n- Enable `autoRangeMin/autoRangeMax`\n\nAfter it's visible, add business constraints gradually (e.g. make bar chart Y start at 0).\n\n#### 7.3.1 Common template: Y axis starts at 0\n\n- `axisType = Value`\n- Fix `minValue = 0`\n- `autoRangeMax = true`\n\n#### 7.3.2 Common template: lock only Max (e.g. percentages)\n\n- Fix `maxValue = 100`\n- `autoRangeMin = true`\n\n### 7.4 rounding / unit / labelFormat\n\n- rounding: snap the range to \"nicer\" numbers.\n- unit: display unit scaling (K/M, ten-thousand/million, etc.).\n- labelFormat: control number formatting (N0/N2/F1/percent, etc.).\n\n#### 7.4.1 Unit display (showUnit / unitText)\n\nWhen values are large (e.g. 10,000+), a common approach is showing a unit at the end of the axis (e.g. \"k\", \"M\").\n\n#### 7.4.2 Quick troubleshooting\n\n- Labels misaligned / bars centered between labels: check Category axis `LabelPlacement`\n- Range looks weird (too large/too small): check if min/max is locked; check rounding/unit\n- Too many decimals in ticks: set `labelFormat`\n\n---\n\n## 8. Series and data (Serie / SeriesData)\n\n### 8.1 Serie (one series)\n\nEach element in `ChartProfile.series` is a `Serie`:\n\n- `name`\n- `type`\n- `visible`\n- `settings`\n- `labelSettings`\n- `seriesData`\n\nNote: `settings` is usually a polymorphic object (`SerializeReference`). When you change `type`, the editor will try to preserve the last used settings for each type (better editing experience).\n\n### 8.2 SeriesData (one data point)\n\nCommon `SeriesData` fields:\n\n- `id`: stable identifier (tooltip/hover/hidden state).\n- `x`: X coordinate or Category index.\n- `value`: main value.\n- `y`: second dimension (scatter/heatmap, etc.).\n- `z`: third dimension (e.g. sizeMapping).\n- `name`: point name (often used by Radar/Pie/Ring).\n- `useColor` + `color`: point-level color override.\n\nIf interactions are enabled, keep `SeriesData.id` stable to avoid generating a new set of ids on every data refresh.\n\n### 8.3 Matching SerieType and coordinate system\n\n- Cartesian2D: Line/Bar/Scatter/Heatmap\n- Polar2D: Radar\n\nIt's not recommended to mix Polar and Cartesian series in a single (non-Pie) ChartProfile. If you do mix them, be careful about whether axes/grid semantics remain consistent.\n\n### 8.4 Common data patterns (by type)\n\n#### 8.4.1 Line\n\n- Common: Category X + Value Y\n - Data point: `x=category index`, `value=value`\n- Continuous: Value X + Value Y\n - Data point: `x=x value`, `value=y value`\n\n#### 8.4.2 Bar\n\n- Category X + Value Y\n - One point per bar: `x=category index`, `value=bar height`\n- Grouped: multiple Bar series share the same Category X\n- Stacked: series with `stacked=true` and the same `stackGroup` will stack\n\n#### 8.4.3 Scatter\n\n- Common: X=Value, Y=Value\n- Recommended to explicitly write `x/y` for data points\n\n#### 8.4.4 Heatmap\n\n- Triplet: `x=column index`, `y=row index`, `value=intensity`\n\n#### 8.4.5 Radar\n\n- Typical: `x=dimension index`, `value=value of that dimension`, `name=dimension label`\n\n### 8.5 Common data pitfalls (symptom-driven)\n\n- Category chart uses Category axis on X, but point `x` is not 0/1/2...\n - Symptom: points/bars don't align with labels\n - Fix: ensure `x=category index`, or change X axis to Value\n\n- NaN/Infinity appears\n - Symptom: chart doesn't render, range explodes\n - Fix: filter invalid values at the data source\n\n- Chart is not visible (but `seriesData` is not empty)\n - Check: coordinate system matches (Cartesian vs Polar)\n - Check: AxisType matches your data meaning\n\n- Interactions/tooltip mapping feels wrong\n - Check: `SeriesData.id` is stable (don't randomly regenerate ids on each refresh)\n" },
{ id: "00_03-UGUIWorkflow", relPath: "00_03-UGUIWorkflow.md", title: "UGUI Workflow", content: "# UGUI Workflow\n\nGoal of this chapter: use EasyChart with a UGUI workflow (Canvas/RectTransform), and understand the choice between two rendering modes:\n\n- `ScreenSpaceOverlay`: best visual quality (no RenderTexture), but typically visible only in the Game view\n- `WorldSpace`: visible in both Scene/Game views (uses RenderTexture), suitable for 3D world-space UI\n\n---\n\n## 1. Recommended approach: UGUIChartBridge\n\nRecommended component: `UGUIChartBridge`.\n\nIts role is:\n\n- Still uses **UI Toolkit `ChartElement`** as the core chart renderer\n- Bridges the chart so it \"fits\" onto a target UGUI `RectTransform`\n\nSo you get the best of both:\n\n- Chart capability + UI Toolkit rendering\n- UGUI scene/prefab layout workflow and habits\n\n---\n\n## 2. Common prerequisites\n\nNo matter which mode you choose, prepare the following first:\n\n- A `ChartProfile` to display (recommended: clone one from `EasyChart/Library Editor` and modify it)\n- A `PanelSettings` asset, and assign it to `UGUIChartBridge` via `Panel Settings Asset`\n\n> Note: providing `Panel Settings Asset` is usually better for font rendering and overall stability.\n\n---\n\n## 3. Screen Space Overlay (recommended for HUD/panels)\n\n### Use cases\n\n- HUD, UI panels, dialogs\n- You care most about clarity and visual quality\n\n### Characteristics\n\n- No RenderTexture\n- Typically visible only in the Game view\n\n### Setup steps (overview)\n\n1. Create a `Canvas`\n2. Under the Canvas, create a node with `RectTransform` (`Image` or an empty GameObject both work)\n3. Add `UGUIChartBridge`\n4. Configure:\n - `Profile`\n - `Panel Settings Asset`\n - `Render Mode = ScreenSpaceOverlay`\n - `Sort Order` (controls overlay order; effective only in Screen Space Overlay mode)\n\nKey points:\n\n- This mode renders the chart inside a runtime-created/reused `UIDocument`.\n- If the chart is covered by other UI, increase `Sort Order` first.\n\n---\n\n## 4. World Space (recommended for 3D world-space panels)\n\n### Use cases\n\n- Billboards/screens/panels inside a 3D world\n- You want to see the result in the Scene view as well\n\n### Characteristics\n\n- Uses RenderTexture\n- Usually visible in both Scene and Game views\n- Visual quality can be affected by RenderTexture resolution\n\n### Setup steps (overview)\n\n1. Create a `Canvas`\n2. Set `Render Mode = World Space`\n3. Under the Canvas, create a node with `RectTransform` (recommended: `RawImage`)\n4. Add `UGUIChartBridge`\n5. Configure:\n - `Profile`\n - `Panel Settings Asset`\n - `Render Mode = WorldSpace`\n\nKey points:\n\n- World Space mode creates and maintains a `RenderTexture` and displays it via `RawImage`.\n- Clarity is strongly tied to the `RenderTexture` resolution, which usually comes from the target `RectTransform` width/height.\n - If the chart looks blurry, make the target `RectTransform` larger first (e.g. 600x400+).\n\n---\n\n## 5. Mode selection (quick conclusion)\n\n- Prefer **ScreenSpaceOverlay** when:\n - you're building traditional UI (HUD/panels)\n - clarity is your top priority\n\n- Prefer **WorldSpace** when:\n - your chart needs to appear in a 3D world\n - you want it visible in the Scene view\n\n---\n\n## 6. Common issues & troubleshooting\n\n- **Not visible at runtime**\n - Make sure the target `RectTransform` size is not 0\n - Make sure `Profile` is assigned, and the Profile renders correctly in Library Editor Preview\n - If fonts look wrong, check whether `Panel Settings Asset` is missing\n\n- **Blurry chart in World Space mode**\n - Increase the target `RectTransform` size (this increases RenderTexture resolution)\n - Avoid frequent aggressive scaling at runtime (may trigger RenderTexture resizing)\n\n---\n\n## 7. Alternative: export as a UGUI Prefab\n\nIf your version provides `Export UGUI Prefab`:\n\n- You can export the Profile to a UGUI prefab and use it directly under a Canvas\n- Coverage for interaction/compatibility depends on the exporter version\n" },
{ id: "00_04-RuntimeDataInjectionUIToolKit", relPath: "00_04-RuntimeDataInjectionUIToolKit.md", title: "Runtime Data Injection (UI Toolkit)", content: "# Runtime Data Injection (UI Toolkit)\n\nThis chapter explains how to inject data into `ChartElement` at runtime in a UI Toolkit workflow.\n\nRelated component: `EasyChartDataSource`\n\n---\n\n## 1. When should you use this approach?\n\n- Your chart is built with UI Toolkit (`UIDocument` + UXML + `ChartElement`)\n- You want a set of injection APIs that are easier to call from gameplay/business logic (labels / values / x-y / pie / ring)\n- Or you want to inject a JSON payload directly (`ChartFeed`)\n\n---\n\n## 2. Quick start (recommended flow)\n\n1. Prepare a `UIDocument` in the scene, and make sure there is a `ChartElement` in your UXML.\n2. Add `EasyChartDataSource` to the same GameObject that has the `UIDocument`.\n3. Fill in the Inspector fields:\n - `uiDocument`\n - `chartElementName` (default: `main-chart`, matches the `name` of the `ChartElement` in UXML)\n - `profile` (optional, but strongly recommended: lets style/Series type come from an editor-authored `ChartProfile`)\n4. At runtime, call from code:\n - `SetCategoryLabels(...)`\n - `SetSeriesValues(...)` / `SetSeriesXY(...)`\n - or `ApplyJson(...)`\n\nInternally, the component will:\n\n- Find the target `ChartElement` from `UIDocument.rootVisualElement`\n- Initialize chart data from `profile` when needed\n- Modify `ChartElement.Data` and call `RefreshData()`\n\n---\n\n## 3. Inspector fields\n\nKey fields of `EasyChartDataSource`:\n\n- `uiDocument`\n - Points to the current UI `UIDocument`.\n - If not set, the script will try `GetComponent<UIDocument>()`.\n\n- `chartElementName`\n - The `name` of the target `ChartElement` (the UXML/USS name). Default is `main-chart`.\n - If you want JSON `chartId/chartName` to locate the chart automatically, keep the `ChartElement.name` consistent with those values (see section 5).\n\n- `profile`\n - Optional.\n - If set, the component assigns `ChartElement.Profile = profile` to initialize/preserve styles, Series structure, etc.\n\n- `playAnimationOnRefresh`\n - After each injection, call `RefreshData(..., playAnimation: playAnimationOnRefresh)`.\n\n- `allowCreateSeriesFromFeed`\n - When injecting via JSON (`ApplyJson`), if a series in the feed does not match any existing Serie:\n - `false` (default): do not create new Serie; only update matched ones.\n - `true`: allow creating new Serie from the feed (may rebuild renderers).\n\n---\n\n## 4. Common injection APIs (without JSON)\n\n### 4.1 Set category axis labels\n\n`SetCategoryLabels(labels, axisId = AxisId.XBottom)`\n\n- Sets the axis to Category and overwrites `labels`.\n\n### 4.2 Single-series Y values (auto x=0..n-1)\n\n`SetSeriesValues(\"Sales\", values)`\n\n- Finds/creates a Serie by default (default type is Line; it does not force the type to change).\n- Writes to `SeriesData.value` and sets `SeriesData.x` to the index.\n\n### 4.3 XY points\n\n`SetSeriesXY(\"Scatter\", x, y)`\n\n- Writes `x[]` into `SeriesData.x` and `y[]` into `SeriesData.value`.\n\n### 4.4 Pie / Ring injection\n\n- `SetPie(serieName, names, values)`\n - Forces the Serie type to `Pie`.\n - Uses `SeriesData.name` as slice name and `SeriesData.value` as slice value.\n\n- `SetRing(serieName, names, percents)`\n - Forces the Serie type to `RingChart`.\n - Uses `SeriesData.name` as ring name and `SeriesData.value` as progress value.\n\n---\n\n## 5. JSON injection (ChartFeed)\n\nYou can call: `ApplyJson(json)`\n\nThis method parses JSON into `ChartFeed` and applies it to `ChartElement.Data`.\n\n### 5.1 `ChartFeed` schema\n\n```json\n{\n \"chartId\": \"optional\",\n \"chartName\": \"optional\",\n \"axes\": [\n {\n \"axisId\": \"XBottom\",\n \"labels\": [\"Mon\", \"Tue\", \"Wed\"]\n }\n ],\n \"series\": [\n {\n \"serieId\": \"optional\",\n \"name\": \"optional\",\n \"type\": \"Line\",\n \"datas\": [\n { \"x\": 0, \"value\": 12 },\n { \"x\": 1, \"value\": 18 }\n ]\n }\n ]\n}\n```\n\nSee the runtime code `Scripts/Runtime/Feed/ChartFeed.cs` for the exact fields.\n\n### 5.2 ChartElement lookup rules (`chartId` / `chartName`)\n\nInternally, `ApplyJson` tries:\n\n- If `chartId` is provided: `rootVisualElement.Q<ChartElement>(chartId)` first\n- Else if `chartName` is provided: try `Q<ChartElement>(chartName)`\n- If still not found: fall back to `chartElementName` (default `main-chart`)\n\nTherefore:\n\n- If you only have one chart, keeping the default is fine.\n- If you have multiple `ChartElement` in one UI, it's recommended to align each chart's `name` with the feed `chartId` or `chartName`.\n\n### 5.3 Series matching and type override\n\n`ApplyJson` checks whether the JSON contains `\"type\":`. If present, it assumes you want to allow type override (`allowTypeOverride=true`).\n\nSerie matching rules:\n\n- If `serieId` is not empty: match by `Serie.id`\n- Else if `name` is not empty: match by `Serie.name`\n- Else (index mode): match by feed index (i-th to i-th)\n\nWhen no Serie can be matched:\n\n- `allowCreateSeriesFromFeed=false` (default): the feed series is skipped (no creation).\n- `allowCreateSeriesFromFeed=true`: create a new Serie using the feed `type/name/serieId`.\n\nFor matched Serie:\n\n- Only when `allowTypeOverride=true` and it's not index mode, overriding `id/name/type` is allowed.\n\n---\n\n## 6. Common issues & troubleshooting\n\n- **Not visible / TryGetChart failed**\n - Make sure `uiDocument` is assigned correctly\n - Make sure the `ChartElement` `name` in UXML matches `chartElementName`\n\n- **JSON parse failed**\n - When `EasyChartDataSource` parses JSON:\n - it tries Newtonsoft first (if `Newtonsoft.Json` exists in your project)\n - otherwise falls back to Unity `JsonUtility`, normalizing string forms like `type/axisId` into enum integers before parsing\n - Recommendation: start from a known-good JSON (e.g. generated from the editor JSON panel) and modify it.\n\n- **Series mismatch after injection / updated the wrong line**\n - Prefer `serieId` for stable matching.\n - If you only use `name` and there are multiple series with the same name, the script uses the first one and logs a warning.\n\n- **JSON wanted to add a Serie but none was added**\n - Enable `allowCreateSeriesFromFeed`.\n" },
{ id: "00_05-RuntimeDataInjectionUGUI", relPath: "00_05-RuntimeDataInjectionUGUI.md", title: "Runtime Data Injection (UGUI)", content: "# Runtime Data Injection (UGUI)\n\nRelated scripts: `UGUIRuntimeJsonInjection`, `UGUIRuntimeJsonInjectionEditor`\n\nThis chapter explains how to inject data into charts at runtime via JSON in a UGUI workflow (`UGUIChartBridge`).\n\n---\n\n## 1. When should you use this approach?\n\n- You have JSON coming from a server/business layer (or you want to quickly edit JSON at runtime)\n- You want an editor-like workflow: \"Generate example → Modify → Apply\" (similar to the `JSON Injection` panel)\n- You already configured the chart structure (style/axes/Series types) via `ChartProfile`\n\nThis injector is primarily designed for **updating data**. Structural changes (e.g. adding Series, force-overriding Series types) are not its main goal.\n\n---\n\n## 2. Quick start (recommended flow)\n\n1. Set up `UGUIChartBridge` in the scene (and make sure `Profile` is assigned).\n2. Add `UGUIRuntimeJsonInjection` to the same GameObject.\n3. Click **Generate Example JSON** to generate sample JSON that matches your current Profile.\n4. Modify the data in the `JSON Content` text box.\n5. Click **Apply JSON to Chart**.\n\nInternally, the component will:\n\n- Parse JSON → convert to `ChartFeed`\n- Apply `ChartFeed` to `UGUIChartBridge.Profile`\n- Call `_bridge.Refresh()` to redraw\n\n---\n\n## 3. Component and Inspector fields\n\n`UGUIRuntimeJsonInjection` must be on the same GameObject as `UGUIChartBridge` (the script has `[RequireComponent(typeof(UGUIChartBridge))]`).\n\n### 3.1 JSON Generation Settings\n\n- **Example Mode (`ChartJsonExampleMode`)**\n - Controls the format when generating example JSON.\n - Generally recommended to start with `Standard` or `Standard_Axis` (more intuitive).\n\n- **Data Mode (`ChartJsonDatasMode`)**\n - Controls how `datas` is represented.\n - `Standard`: `datas` is an array of objects (e.g. `{ \"x\": 0, \"value\": 12 }`).\n - `Values`: `datas` is an array of raw numbers (shorter).\n - Note: this format requires the \"flexible parser\" in `ChartJsonUtils` (reflection-based parsing via Newtonsoft). If your project does not include Newtonsoft (`Newtonsoft.Json` / `Unity.Newtonsoft.Json`), parsing may fail.\n - Therefore **`Standard` is recommended by default**, unless you're sure Newtonsoft is available.\n\n- **API Envelope (`UseApiEnvelope`)**\n - When generating example JSON, whether to wrap it with an API envelope:\n - `{ \"code\": 200, \"message\": \"success\", \"data\": { ...the real ChartFeed... } }`\n - When applying, it will also try to extract `data` automatically.\n\n- **Auto Generate (`AutoGenerateJson`)**\n - Automatically regenerates example JSON when you change `Example Mode / Data Mode / API Envelope`.\n\n### 3.2 JSON Content\n\n- **JSON Content (`JsonContent`)**\n - The JSON string to inject.\n - If empty, clicking Apply will log a warning and return.\n\n---\n\n## 4. JSON format (ChartFeed)\n\nThe underlying data model is `ChartFeed`:\n\n```json\n{\n \"chartId\": \"optional\",\n \"chartName\": \"optional\",\n \"axes\": [\n {\n \"axisId\": \"XBottom\",\n \"labels\": [\"Mon\", \"Tue\", \"Wed\"]\n }\n ],\n \"series\": [\n {\n \"serieId\": \"optional\",\n \"name\": \"optional\",\n \"type\": \"Line\",\n \"datas\": [\n { \"x\": 0, \"value\": 12 },\n { \"x\": 1, \"value\": 18 }\n ]\n }\n ]\n}\n```\n\nField-to-code mapping notes:\n\n- `chartId` / `chartName`\n - In the `UGUIRuntimeJsonInjection` injection path, it **will not overwrite** the Profile `chartId/chartName` (it calls `ChartJsonUtils.ApplyFeedToProfile(profile, feed)` with `allowMetaOverwrite=false`).\n - But these fields can help other injectors (e.g. `EasyChartDataSource`) locate a `ChartElement` by name/ID in the UI tree.\n\n- `axes[]`\n - `axisId` is the `AxisId` enum (e.g. `XBottom`, `XTop`, `YLeft`, `YRight`).\n - If `labels` exists, that axis is treated as Category and labels are overwritten.\n\n- `series[]`\n - **Matching priority**:\n - If `serieId` is provided: match by `Serie.id`\n - Else if `name` is provided: match by `Serie.name`\n - Else (both `serieId` and `name` are empty): match by index (feed 0 -> profile 0)\n - `type`\n - Mainly used when generating example JSON.\n - In the current injection path:\n - For existing matched Serie: it **will not force the type to change** (meta overwrite is not allowed).\n - For newly created Serie in \"index mode + feed exceeds Profile series count\": it will use the feed `type` as the new Serie type.\n - `datas[]` for each point:\n - numeric `x/y/z/value`\n - optional `id/name`\n - optional `useColor/color`\n\n---\n\n## 5. What happens when you apply? (injection flow)\n\nWhen you click **Apply JSON to Chart**:\n\n1. If the JSON is wrapped in an API envelope (contains `data`), it first tries to extract the object under `data`.\n2. Calls `ChartJsonUtils.TryDeserializeFeed(json, out feed)` to deserialize into `ChartFeed`.\n - Tries Newtonsoft first (if available); otherwise falls back to Unity `JsonUtility`.\n - String values like `type: \"Line\"` / `axisId: \"XBottom\"` are normalized to enum values in the fallback path before parsing.\n3. Calls `ChartJsonUtils.ApplyFeedToProfile(_bridge.Profile, feed)` to write the feed back into the Profile.\n4. Calls `_bridge.Refresh()` to redraw.\n\n---\n\n## 6. Common issues & troubleshooting\n\n- **Click Apply does nothing / console warns: No UGUIChartBridge or ChartProfile found**\n - Make sure the object has `UGUIChartBridge`\n - Make sure `UGUIChartBridge.Profile` is assigned\n\n- **Error: Failed to parse JSON**\n - Generate a known-good JSON first, then modify it.\n - If your API response has an outer wrapper, enable `API Envelope`, or ensure the JSON `data` field contains the `ChartFeed`.\n\n- **JSON applied but data didn't change / only partially changed**\n - Check how `series` is matched (`serieId` / `name` / index mode).\n - If you use `serieId/name` matching: make sure the corresponding Serie exists in the Profile (this injection path won't auto-create new Serie in this mode).\n - If you use \"index mode\" (both `serieId` and `name` are empty):\n - When feed `series[]` count **exceeds** the Profile series count, it will auto-create additional Serie.\n - If you don't want auto-creation, provide an explicit `name` or `serieId` for each serie.\n\n- **After injecting in Play Mode, the Profile asset became dirty**\n - Injection essentially \"applies the feed to the `ChartProfile`\". If you drag the asset directly into the bridge, runtime changes may mark the asset dirty.\n - If you don't want to modify the asset, instantiate a runtime copy of the Profile and inject into that copy.\n" },
{ id: "01_01-EditorWorkflow", relPath: "01_01-EditorWorkflow.md", title: "Editor Workflow and Panel Guide", content: "# Editor Workflow and Panel Guide\n\nThis chapter helps you understand **what to edit where** in `EasyChart/Library Editor`, and the common editing workflows (create, clone, preview, export).\n\n---\n\n## 1. What are you editing? (ChartProfile)\n\nThe chart selected in the Library Editor is essentially a `ChartProfile` asset.\n\n- It is a **reusable configuration**: the same Profile can be referenced by multiple scenes/prefabs.\n- It is a **previewable configuration**: after modifying it in the editor, you can see the preview update immediately.\n\nRecommendation: put your own Profiles under `Assets/EasyChart/Library/Custom/` (or a folder agreed by your team).\n\n---\n\n## 2. The three core areas of the Library Editor\n\nAlthough UI details may change slightly across versions, you can understand the overall layout like this:\n\n### 2.1 Left: Library Tree\n\nThis shows the folder structure where chart assets (`ChartProfile`) are located.\n\nCommon actions:\n\n- Right-click a folder:\n - `New Folder...`: create a subfolder\n - `New Chart...`: create a new `ChartProfile`\n- Right-click a chart:\n - `Clone`: duplicate a new Profile (to create variants)\n - `Export to UXML`: export (if your workflow needs to sync/export configuration into UXML)\n - `Ping`: locate the asset in the Project window\n - `Rename...` / `Delete`\n\n> Tip: use `Clone` to create variants like \"same chart with different colors / different data scale\", instead of configuring from scratch.\n\n### 2.2 Right: Inspector\n\nThis is where you do most of your editing.\n\nIt typically includes:\n\n- **Basic settings**:\n - `coordinateSystem`\n - `padding` (if present)\n - `animationDuration` (if present)\n- **Series list**: each Serie represents a line, a group of bars, a scatter series, etc.\n- **Axes**:\n - choose which `XAxisId/YAxisId` to use\n - configure display/labels/range/ticks of the corresponding `AxisConfig`\n- **Legend / Tooltip / Grid**: if your version exposes these settings\n\n> Practical tip: configure `coordinateSystem`, `Series`, and `Axes` first. The rest is \"nice to have\".\n\n### 2.3 Preview\n\nUse it to check:\n\n- whether data exists\n- whether axis ranges are correct\n- whether labels are crowded/misaligned\n- Tooltip / Legend interactions (if enabled)\n\nWhen preview looks wrong, troubleshoot in this order:\n\n1. Is `coordinateSystem` correct?\n2. Does `series` contain at least 1 serie and data points?\n3. Does the axis `axisType` match the meaning of your data `x/y`?\n4. Is the Value axis range locked manually (`autoRangeMin/autoRangeMax`)?\n\n### 2.4 JSON Injection\n\nBelow the left panel there is a **JSON Injection** area, used to:\n\n- quickly generate an \"injection JSON example\" for the selected `ChartProfile`\n- apply your pasted/edited JSON back to the selected Profile (`ApplyToChart`)\n\nCommon controls:\n\n- **API Envelope**: whether to wrap with `{ code, message, data }`.\n - When enabled: generated JSON will be wrapped; parsing can also recognize it and automatically extract `data`.\n- **Feed Mode**: the structure/field completeness of the example JSON (to support different injection protocols).\n- **Datas Format**: the point format inside the `datas` field (e.g. compact arrays, or more readable objects).\n- **ApplyToChart**: parse JSON from the text box and write it back into the selected `ChartProfile`.\n\n---\n\n## 3. Recommended editing flow (from zero to reusable)\n\n### Step 1: Create or select a ChartProfile\n\n- New: right-click the target folder and choose `New Chart...`\n- Existing: click to select in the left tree\n\nIf you prefer the \"clone first, then modify\" approach (recommended):\n\n- First, use **Clone** in the top toolbar to create your own Library\n- Then in your own library, right-click a Profile -> `Clone` to create variants\n\n### Step 2: Decide the coordinate system\n\n- `Cartesian2D`: Line/Bar/Scatter/Heatmap\n- `Polar2D`: Radar\n\n> Tip: decide the coordinate system first, then choose SeriesType, to avoid style/axis confusion after switching later.\n\n### Step 3: Configure Series\n\n- Add Series\n- Set `type`\n- Fill `seriesData`\n\nTip: start with a small number of points (38) to validate the look, then scale up.\n\n### Step 4: Configure Axes\n\nMost common combination:\n\n- X: Category\n - put text into `labels`\n- Y: Value\n - enable auto range (default)\n\nWhen you want more professional axis formatting:\n\n- Use `labelFormat` (e.g. `F1`, `N0`)\n- Use `autoRangeMin/autoRangeMax` to lock only one side of the range\n- If the Value axis needs a unit:\n - `showUnit=true`\n - `unitText=\"items\"/\"10k\"`\n - use `unitLabelStyle` to adjust font/color/position\n\n### Step 5: Clone variants (recommended)\n\nWhen you need multiple versions of the same chart (colors, font size, slightly different axis display):\n\n- Right-click the chart -> `Clone`\n- Modify only the differences\n\nThis keeps style consistent and is easier for version management.\n\nWhen you want to use the chart in UI:\n\n- UI Toolkit: export to UXML, then compose the page in UI Builder (see demo scene `Assets/EasyChart/Demo/Scenes/EasyChart_QuickStart.unity` and template `Assets/EasyChart/Demo/UIToolKit/NewUXMLTemplate.uxml`)\n- UGUI: export to a UGUI prefab and use it in a Canvas/RectTransform workflow\n\n---\n\n## 4. Common pitfalls (quick diagnosis)\n\n- **Nothing shows up**\n - Is `seriesData` empty?\n - Does `AxisType` match the meaning of your data (does the Category axis have labels)?\n\n- **Value axis looks weird (range too large/too small)**\n - Check `autoRangeMin/autoRangeMax`\n - Check whether rounding/unit snapped the range to an unsuitable unit\n\n- **Bars and labels are misaligned**\n - Check `LabelPlacement` (Tick vs CellCenter)\n\n---\n\n## Next\n\n- `00_02-WorkflowAndLibrary.md`: axis types, label placement, auto range, rounding, and unit display are merged into section 7\n" },
{ id: "01_02-LibraryPanel", relPath: "01_02-LibraryPanel.md", title: "Library Panel (Asset Tree)", content: "# Library Panel (Asset Tree)\n\nThis chapter explains the **Library** panel on the left side of `Unity Easy Chart/Library Editor`. It manages your chart assets (`ChartProfile`) and folder structure, and determines which Profile the panels on the right are currently editing.\n\n---\n\n## Feature Overview\n\n- **Structure Display**: shows folders and `ChartProfile` (`.asset`) under the library root in a tree.\n- **Selection Drives Editing**: selecting a `ChartProfile` triggers:\n - Inspector binds to that Profile\n - Series binds to that Profile `series`\n - Preview refresh\n - JSON Injection generates example JSON (and can Apply back to that Profile)\n- **Asset Management**: create/rename/delete, drag-move, expand/collapse, etc.\n\n---\n\n## UI Structure\n\nThe header bar at the top of the Library panel typically contains (left to right):\n\n- **Library**: title.\n- **(Current Library Name)**: shows the selected library/root name (useful if you have multiple libraries).\n- **Menu**: entry for common actions (similar to right-click, but centralized).\n- **Help**: opens this chapter.\n\nIn the global window toolbar at the top, on the right side of the Library dropdown you may see:\n\n- **+**: add a new Library.\n- **-**: delete the current Library.\n- **Clone**: duplicate the current Library (see below).\n\nBelow the header is:\n\n- **TreeView**:\n - folders\n - `ChartProfile` assets (chart configuration assets)\n\n---\n\n## Selection Logic (Important)\n\n- **When a Folder is Selected**:\n - Inspector/Series on the right are cleared (no Profile to edit).\n - JSON Injection switches to a \"no selected Profile\" example or keeps the current example (implementation-dependent).\n- **When a ChartProfile is Selected**:\n - Inspector/Series immediately bind to the Profile's serialized data.\n - Any field change triggers a delayed Preview refresh (`delayCall`).\n\nTip: if the right panels are empty, first confirm a `ChartProfile` (not a folder) is selected on the left.\n\n---\n\n## Common Actions (Header Menu)\n\nClicking **Menu** (the small menu icon) opens an action menu. Common items include:\n\n- **New Chart**: create a new `ChartProfile` under the target folder.\n- **New Folder**: create a new folder under the target folder.\n- **Refresh**: rescan and rebuild the tree (useful after manual moves/copies in the Project view).\n- **Expand All**: expand all folders.\n- **Collapse All**: collapse all folders.\n- **Rename / Delete**: rename/delete the currently selected item.\n - If the library root is selected, these are typically disabled.\n- **Export UGUI Prefab** (available when a Profile is selected): export the selected Profile as a UGUI prefab (for runtime display).\n\n### How the Target Folder is Determined\n\n**New Chart / New Folder** create assets under the \"target folder\":\n\n- If a **folder** is selected: the target is that folder.\n- If a **ChartProfile** is selected: the target is usually the Profile's parent folder.\n- If nothing is selected or unclear: the target usually falls back to the library root.\n\n---\n\n## Clone Library\n\nWhen you need to duplicate a full chart library (including Profiles and UXML) into a new library (for branching/versions/theme variants), use **Clone** in the top toolbar.\n\n### Entry and Usage\n\n- Click the **Clone** icon to the right of the Library dropdown.\n- Enter the new library name and confirm.\n\n### What Gets Cloned\n\n- `Assets/EasyChart/Library/<CurrentLibrary>` is copied to `Assets/EasyChart/Library/<NewLibrary>`.\n- `Assets/EasyChart/LibraryUxml/<CurrentLibrary>` is copied to `Assets/EasyChart/LibraryUxml/<NewLibrary>` (if the source UXML folder exists).\n\n### Limitations and Naming Rules\n\n- The `<Root>` library cannot be cloned.\n- The new name is sanitized (invalid filename characters removed); blank names are ignored.\n- If the target library already exists (folder already exists), it will prompt and cancel.\n\n### After Cloning\n\n- Automatically switches the current Library selection to the new library.\n- Refreshes the Library dropdown and tree view, and triggers refresh for right panels/preview.\n\n---\n\n## Common Actions (Context Menu)\n\nYou can also **right-click** items in the tree:\n\n### Right-click a Folder\n\n- **New Folder...**: create a subfolder.\n- **New Chart...**: create a new `ChartProfile` in that folder.\n- **Export Folder to UXML (Mirror/Backup)**: export the folder contents to UXML (backup/distribution/versioning).\n- **Rename...**: rename the folder.\n- **Delete**: delete the folder (destructive; be careful).\n\n### Right-click a ChartProfile\n\n- **Export to UXML**: export UXML for the current Profile.\n- **Clone**: clone a new Profile (quickly derive a similar chart).\n- **Rename...**: rename the asset (also tries to sync-update `profile.name` / `profile.chartName`).\n- **Ping**: locate the asset in the Project view.\n- **Delete**: delete the asset.\n\n---\n\n## Drag-move and Sorting\n\nThe Library tree supports dragging folders or `ChartProfile`:\n\n- **Drag a ChartProfile onto a folder**: triggers `AssetDatabase.MoveAsset` to move the `.asset` into the target folder.\n- **Drag a folder onto a folder**: moves the whole folder under the target folder.\n\nNotes:\n\n- If the target is invalid (dragging into itself/child folder), it will be rejected (cursor shows Rejected).\n- After moving, the tree refreshes automatically.\n\n---\n\n## Rename (Double-click and Inline Editing)\n\n**Double-clicking** a tree item enters inline rename (equivalent to running Rename).\n\nInternally, names are sanitized (invalid filename characters removed). If the new name is blank or unchanged, rename is canceled.\n\n---\n\n## Common Issues & Troubleshooting\n\n- **Right panels are empty**:\n - First confirm a `ChartProfile` (not a folder) is selected.\n- **Renamed but chartName didn't update**:\n - ChartProfile may have additional sync logic; verify the `Chart Name` field in Inspector.\n- **Drag failed**:\n - Common causes: dragging onto itself/child folder, or a name conflict at target path.\n\n---\n\n## Help\n\n- Click the rightmost **Help** icon in the header to open this chapter.\n" },
{ id: "01_03-JsonInjectionPanel", relPath: "01_03-JsonInjectionPanel.md", title: "JSON Injection Panel", content: "# JSON Injection Panel\n\nThis chapter explains the **JSON Injection** panel at the bottom-left of `Unity Easy Chart/Library Editor`.\n\nIts purpose is to represent the current `ChartProfile` configuration (or externally imported configuration) as readable/copyable JSON, and supports **ApplyToChart** to parse the JSON and write it back into the selected Profile.\n\n---\n\n## Location and purpose\n\n- **Location**: below the Library panel (tree view).\n- **Main uses**:\n - **Export**: convert the selected `ChartProfile` into example JSON (Feed)\n - **Edit**: manually edit the JSON in the text box\n - **Import/Apply**: click **ApplyToChart** to parse and apply JSON into the selected `ChartProfile`\n\nUse cases:\n\n- **Debugging**: quickly validate whether a specific field takes effect.\n- **Batch edits**: copy JSON to an external editor (multi-cursor/find-replace), then paste back and Apply.\n- **Integrations**: e.g. your toolchain/scripts generate a Feed and you Apply it in the editor.\n\n---\n\n## Controls (header bar)\n\nThe header bar typically contains (left to right):\n\n- **Min/Max** (label changes)\n - Toggles panel height.\n - `Min`: collapse to a smaller height (more like an auxiliary tool).\n - `Max`: expand to a larger height (better for long JSON).\n\n- **ApplyToChart** (icon button)\n - Attempts to parse the JSON in the text box as a Feed and apply it to the selected `ChartProfile`.\n - On success it will:\n - mark the asset dirty and call `SaveAssets()`\n - refresh the Series list\n - refresh Preview\n\n- **Help** (icon button)\n - Opens this chapter.\n\n---\n\n## Controls (button row)\n\nBelow the header there is a row of buttons (may wrap):\n\n- **API Envelope** (icon toggle)\n - Controls whether the example JSON is wrapped in an \"API response\" envelope.\n - Useful when you want to send the Feed directly to an HTTP API/service.\n - Toggling regenerates the example and overwrites the text box (see \"overwrite rules\").\n\n- **Feed Mode** (dropdown)\n - Controls which levels/fields are included in the example JSON.\n - Options come from an internal enum (common ones include):\n - `Lite`\n - `Standard / ID`\n - `Standard / Default`\n - `Standard / With Axes`\n - `Full`\n - General recommendations:\n - **Quickly inspect structure**: use `Lite`\n - **Need stable references**: use `Standard / ID`\n - **Need to include axes config**: use `Standard / With Axes`\n - **Need full copy/migration**: use `Full`\n\n- **Datas Format** (dropdown)\n - Controls the output format of `seriesData` (data points).\n - Common options:\n - `Values`: more compact, mostly \"values only\".\n - `Standard`: default format, good for editing and Apply.\n - `Full`: more complete (may include more fields/structure), good for migration/restoration.\n\n- **Copy** (icon button)\n - Copies the current text box content to the clipboard.\n\n---\n\n## Text box and \"overwrite rules\" (important)\n\nThe JSON text box is editable. To prevent your manual edits from being overwritten automatically, the panel has a \"dirty\" flag logic:\n\n- **As soon as you manually change the text box**, it is considered \"user modified\" (dirty).\n- When dirty:\n - the editor will not automatically overwrite your content with example JSON.\n- However, switching the following options will **force overwrite** (and clear dirty):\n - `API Envelope`\n - `Feed Mode`\n - `Datas Format`\n - or when switching the selected Profile (resets to that Profile's example)\n\nRecommendation:\n\n- If you plan to do major edits:\n - Copy to an external editor first\n - Paste back and Apply when done\n\n---\n\n## ApplyToChart behavior and notes\n\n- **ApplyToChart modifies the selected `ChartProfile` asset**.\n- If JSON parsing fails, an error is logged to the Console:\n - `ApplyToChart failed: invalid JSON or unsupported format.`\n- In `Full` mode, more meta/structural information may be overwritten (IDs/config, etc.), which is more powerful but also more dangerous.\n\nRecommendations:\n\n- Before applying, make sure:\n - the correct `ChartProfile` is selected on the left\n - JSON format is valid (brackets/commas)\n - you understand what the current Feed Mode will overwrite\n\n---\n\n## Recommended workflows\n\n### 1) Export from current Profile and tweak\n\n- Select a `ChartProfile`\n- Choose appropriate `Feed Mode` / `Datas Format`\n- Copy to an external editor for tweaks\n- Paste back\n- ApplyToChart\n\n### 2) Import configuration from external sources\n\n- Paste external JSON into the text box\n- ApplyToChart\n- Fine-tune further in Inspector / Series\n\n---\n\n## Help\n\n- Click the rightmost **Help** icon in the header to open this chapter.\n" },
{ id: "02_04-PreviewPanel", relPath: "02_04-PreviewPanel.md", title: "Preview Panel", content: "# Preview Panel\n\nThis chapter explains the **Preview** panel at the top of the middle area in `Unity Easy Chart/Library Editor`.\n\nThe purpose of Preview is to render the currently selected `ChartProfile` directly, so you can see results immediately while editing.\n\n---\n\n## When does Preview refresh\n\nPreview refresh is **delayed** (to avoid excessive redraw while you are continuously dragging/typing):\n\n- When you modify any bound field in **Inspector** or **Series**, it triggers `ScheduleUpdatePreview()`.\n- When you click **ApplyToChart** in **JSON Injection**, it triggers a refresh.\n- When you switch to another `ChartProfile` in the left **Library** tree, Preview refreshes to the new Profile.\n\nImplementation-wise, the refresh is scheduled via `EditorApplication.delayCall`, so you may feel it updates \"a moment after\" your change. This is expected.\n\n---\n\n## What does Preview display\n\n- Preview draws using a runtime chart component (e.g. `ChartElement`).\n- Preview reads data from the currently selected `ChartProfile` and renders it.\n\nYou can think of Preview as:\n\n- **what you edit is what it renders**\n- **what you see is (mostly) the runtime effect**\n\n---\n\n## Common issues and troubleshooting\n\n### 1) Preview is empty\n\nCheck first:\n\n- Is a `ChartProfile` selected?\n- Is there at least one `Serie`?\n- Is `seriesData` empty (no data points)?\n\n### 2) Data exists but looks wrong / not visible\n\nCommon causes:\n\n- **Coordinate system and SeriesType mismatch**: e.g. the Profile is `Polar2D` but the Series type is not Radar.\n- **Axis range and data range mismatch**: e.g. all values are outside the axis range.\n- **Category axis visible count (VisibleCount) is too small**: only a small segment is shown.\n\n### 3) Console shows \"Preview refresh failed\"\n\nIf an exception occurs during refresh, the Console logs:\n\n- `[EasyChartLibraryWindow] Preview refresh failed: ...`\n\nThis usually means:\n\n- some configuration combination is invalid\n- or some field value is unexpected (e.g. null / NaN)\n\nRecommended handling:\n\n- revert the most recent change first\n- then re-apply changes step by step to locate which field triggers the exception\n\n---\n\n## Tips\n\n- Preview only focuses on rendering results. Structural issues usually need to be fixed in **Inspector/Series/JSON Injection**.\n- If you modify many fields in a short time, Preview may refresh only after your last change (for performance).\n\n---\n\n## Help\n\n- Click the rightmost **Help** icon in the title bar to open this chapter.\n" },
{ id: "02_05-InspectorPanel", relPath: "02_05-InspectorPanel.md", title: "Inspector Panel", content: "# Inspector Panel\n\nThis chapter explains the **Inspector** panel at the bottom of the middle area in `Unity Easy Chart/Library Editor`.\n\nInspector is designed to edit the serialized fields of the currently selected `ChartProfile` from a \"configuration\" perspective (coordinate system, axes, grid, interactions, legend, etc.), and drive the Preview to update.\n\n---\n\n## Panel structure overview\n\nAfter you select a `ChartProfile` in the left Library tree, Inspector builds a set of foldouts (Foldout), typically including:\n\n- **Chart Settings**: basic chart settings (e.g. background, name)\n- **Coordinate System**: coordinate system selection and related options\n- **Axis Settings**: axis configuration (X/Y or Angle/Radius)\n- **Grid Settings**: grid-related configuration\n- **Hover Settings**: hover/tooltip related configuration\n- **Legend Settings**: legend configuration\n\nTip:\n\n- If you select a folder instead of a `ChartProfile`, Inspector will be empty (this is expected).\n\n---\n\n## Chart Settings (common)\n\n### Chart Name\n\nInspector contains a `Chart Name` text field (from `ChartProfile.chartName`). It is not only a display name, it also participates in asset renaming:\n\n- When you type a new name and then **lose focus** or press **Enter**:\n - the editor attempts to rename the `.asset` file to that name\n - and tries to keep `profile.name` and `profile.chartName` in sync\n\nNotes:\n\n- The name will be sanitized for filenames (invalid characters are removed/replaced).\n- If renaming fails (e.g. name conflict), an Error dialog is shown and the field is reverted.\n\n### Background\n\n`Background` is usually a sub-foldout that contains background color/alpha fields (depending on version).\n\n---\n\n## Coordinate System\n\nThis area shows the `coordinateSystem` selector.\n\nThe coordinate system affects:\n\n- available Series types/semantics (e.g. Polar2D is commonly Radar; Pie is a special layout)\n- whether Axis Settings shows Cartesian (X/Y) or Polar (Angle/Radius) configuration\n\nRecommendation:\n\n- decide the coordinate system before you start configuring, to avoid large adjustments later.\n\n---\n\n## Axis Settings\n\n### Axis selection (X Axis / Y Axis)\n\nIn Cartesian mode, the top provides X/Y axis dropdowns:\n\n- **X Axis**: e.g. `XBottom` / `XTop`\n- **Y Axis**: e.g. `YLeft` / `YRight`\n\nWhen the selection changes, it will:\n\n- ensure the axes list contains an element for that AxisId (auto-create if needed)\n- refresh the Axis field UI below\n- trigger a Preview refresh\n\n### Common fields of a single Axis\n\nEach Axis configuration typically contains:\n\n- `axisType`: Category / Value, etc.\n- `visible`: whether to show\n- `color` / `width`: axis line style\n\n#### LabelTexts (Category axis labels)\n\nInspector provides `LabelTexts` (internal field name `labels`) to configure category labels.\n\n#### Range\n\nCommon fields include:\n\n- `minValue` / `maxValue`\n- `autoRangeMin` / `autoRangeMax`\n- `autoRangeRounding`\n- `autoRangeUnit`\n\n#### Ticks / VisibleCount\n\nIf the axis supports auto ticks:\n\n- When `autoTicks` is enabled, it shows `splitCount`.\n- For **Category Axis**, this field is displayed as **VisibleCount** (number of visible categories).\n\n#### Category Auto Scroll\n\nIf the axis supports category scrolling, common fields include:\n\n- `categoryAutoScroll`: whether to auto scroll (marquee effect).\n- `categorySmoothScroll`: whether to scroll smoothly.\n- `categoryScrollInterval`: scroll interval.\n- `categoryScrollStep`: scroll step per tick.\n\n#### Unit (unit display)\n\nCommon fields:\n\n- `showUnit`: whether to show unit.\n- `unitText`: unit text (e.g. `ms`/`%`/`MB`).\n- `unitLabelStyle`: unit label style.\n\n---\n\n## Polar Axis\n\nWhen `coordinateSystem = Polar2D`, Axis Settings shows `polarAxes`:\n\n- **Angle Axis** (angleAxis)\n- **Radius Axis** (radiusAxis)\n\nCommon field meanings are similar to Cartesian:\n\n- `labels`: angle/dimension labels (Radar dimension names typically come from here).\n- `visible/color/width`: axis line style.\n- `showLabels/fontSize/labelColor/labelPosition/labelOffset`: label display controls.\n- `autoRangeMin/autoRangeMax/minValue/maxValue`: radius axis range.\n- `autoTicks/splitCount`: tick count.\n\n---\n\n## Grid Settings (fields, Cartesian2D only)\n\nGrid Settings is visible in Cartesian2D. Key fields come from `cartesianGrid`:\n\n- **xGridColor / xGridLineWidth**: X-direction grid line color and width.\n- **yGridColor / yGridLineWidth**: Y-direction grid line color and width.\n\nIf you need dashed lines:\n\n- `xGridDashed` / `yGridDashed`: enable dashed.\n- `xGridDashLength` / `yGridDashLength`: dash segment length.\n- `xGridDashGap` / `yGridDashGap`: dash gap.\n- `xGridDashOffset` / `yGridDashOffset`: dash offset.\n\n---\n\n## Hover Settings (fields, Cartesian2D only)\n\nHover Settings is visible in Cartesian2D. Key fields come from `hover`:\n\n- **cursorLineColor**: hover cursor line color.\n- **cursorLineWidth**: line width.\n- **cursorLineDashed**: dashed or not.\n- **cursorLineDashLength / cursorLineDashGap / cursorLineDashOffset**: dash parameters.\n\n---\n\n## Legend Settings (fields)\n\nLegend Settings comes from `legendSettings` (it may be auto-hidden in some cases; see below).\n\n- **enabled**: whether to show legend.\n- **position**: legend position (Top/Bottom/Left/Right).\n- **fontSize / color**: text size and color.\n- **backgroundColor**: legend background color.\n- **itemSpacing**: spacing between legend items.\n- **offset**: offset relative to the edge.\n - When offset is default, a common offset is applied based on position (e.g. Bottom defaults to `y=-30`).\n\n---\n\n## Legend Settings (may be auto-hidden)\n\nWhen the chart is a \"pure Pie series\" (only Pie/Ring/Pie3D, with no non-Pie series), Legend Settings may be hidden automatically.\n\nThis avoids showing meaningless or conflicting legend configuration in some layouts.\n\n---\n\n## Editing tips and troubleshooting\n\n- **When making many changes**: use `Save` in the top toolbar to save the asset.\n- **When changing key structure** (e.g. coordinate system, axis type, Series Type):\n - after the change, check whether Preview refreshes correctly\n - if inconsistent, try switching selection to trigger a rebuild\n\n---\n\n## Help\n\n- Click the rightmost **Help** icon in the title bar to open this chapter.\n" },
{ id: "02_06-SeriesPanel", relPath: "02_06-SeriesPanel.md", title: "Series Panel (Data and Series)", content: "# Series Panel (Data and Series)\n\nThis chapter explains the **Series** panel on the right side of `Unity Easy Chart/Library Editor`.\n\nThe Series panel edits `ChartProfile.series` from a \"chart structure\" perspective: you can add/remove/reorder series, set each Serie's type and parameters, and directly edit `seriesData` (data points).\n\n---\n\n## Panel structure overview\n\nAfter you select a `ChartProfile`, the Series panel shows:\n\n- a list of Serie cards (each card corresponds to `series[i]`)\n- a **+ Add Series** button at the bottom\n\nEach Serie card typically consists of three parts:\n\n- **Header**: fold toggle + title\n- **Body**: Name / Id / Type / Settings / Data\n- **Footer** (bottom-right controls): ↑ ↓ X\n\n---\n\n## Header: Collapse/Expand\n\n- On the left side of the Header there is a small toggle:\n - `▼` means expanded\n - `▶` means collapsed\n- The fold state is remembered (stored by Profile + index) to reduce visual clutter for long configurations.\n\n---\n\n## Name and Serie Id\n\n### Name\n\n- The `Name` field displays and edits the name of the Serie.\n- When you change Name, the card title updates and triggers a Preview refresh.\n\n### Serie Id (read-only)\n\nIf the Serie supports an `id` field, the panel shows:\n\n- **Serie Id** (read-only text field)\n- **Copy** button to copy the id to the clipboard\n\nThis id is commonly used for:\n\n- interaction/highlighting/external systems referencing a serie\n- keeping references stable (especially when you reorder/add/remove series)\n\n---\n\n## Type (series type) and compatibility tips\n\n### Type dropdown\n\n- `Type` is used to select the SerieType (Line/Bar/Scatter/Pie/Radar...).\n- The dropdown provides options based on the registry. If the current type is not in the list, it will be temporarily inserted to keep it visible.\n\n### Compatibility warning\n\nWhen the SeriesType is incompatible with the Profile `coordinateSystem`, the Series panel shows a warning message:\n\n- rendering is still allowed (not forcibly blocked)\n- but it warns that axis/grid semantics may be inconsistent\n\nTypical example:\n\n- Profile is `Polar2D` but the SeriesType is Line/Bar (not recommended)\n\n### Pro-only type restrictions\n\nSome types are not available in the Free version (e.g. RingChart / HorizontalBar / Heatmap / Pie3D).\n\n- When you try to select these types without Pro installed:\n - a hint text will be shown\n - and the dropdown will automatically revert to the previous type (it will not modify the asset)\n\n---\n\n## Settings (series parameters)\n\nThe Series panel shows a group of Settings for each serie:\n\n- The root foldout name changes by type (e.g. `LineSettings` / `BarSettings` / `PieSettings` ...).\n- Some types have more detailed sub-foldouts (e.g. Ring layout/valueMapping, etc.).\n\nNotes:\n\n- Switching Type may trigger a \"Settings instance replacement\" (managedReference structure changes).\n- When replacement happens, the UI rebuild is delayed by one frame to avoid invalid serialized handles.\n\n---\n\n## Data: seriesData (data points)\n\nThe Series panel directly shows the `seriesData` array (Unity's default array editor).\n\n- It is expanded by default (easier to edit).\n- When you add/remove/modify points, it triggers a Preview refresh.\n\nRecommendation:\n\n- If you have many data points, use the JSON Injection panel for batch editing.\n\n---\n\n## Footer: Reorder and delete (render order)\n\nEach serie card has three buttons at the bottom-right:\n\n- **↑**: move the serie up (`MoveArrayElement(index, index-1)`)\n- **↓**: move the serie down (`MoveArrayElement(index, index+1)`)\n- **X**: delete the serie (`DeleteArrayElementAtIndex(index)`)\n\nRender order tip:\n\n- Usually, **later series are drawn on top**.\n- So you can use ↑↓ to control overlap (e.g. points/lines on top of bars).\n\n---\n\n## + Add Series\n\nClick **+ Add Series** at the bottom:\n\n- Inserts a new element at the end of the `series` array.\n- Note: if there is already at least one serie, Unity's `InsertArrayElementAtIndex(arraySize)` will **duplicate the last element** (including type/settings).\n- If this is the first serie, a default type is chosen based on coordinate system:\n - Polar2D: defaults to Radar\n - otherwise: defaults to Line\n\nAfter adding, it typically will:\n\n- auto-fill a name (e.g. `Serie N`)\n- call `EnsureRuntimeData()`\n- refresh the Series list and Preview\n\n---\n\n## Recommended workflows\n\n### 1) Create a basic chart from scratch\n\n- + Add Series\n- Choose Line or Bar in Type\n- Add a few points in seriesData\n- Adjust axis range/visible count in Inspector\n\n### 2) Adjust overlap\n\n- Use ↑↓ to adjust series order\n- Observe layering changes in Preview\n\n### 3) Large data / batch editing\n\n- Switch `Datas Format` in JSON Injection\n- Copy to an external editor to batch-generate/replace data\n- Paste back and ApplyToChart\n\n---\n\n## Help\n\n- Click the rightmost **Help** icon in the title bar to open this chapter.\n" },
{ id: "03_01-LineChart", relPath: "03_01-LineChart.md", title: "Line Chart (Line)", content: "# Line Chart (Line)\n\nThis chapter explains the key points of configuring a Line chart in EasyChart: how to set it up, how data is interpreted, and which fields affect rendering.\n\n---\n\n## 1. Use cases\n\n- Trend changes (time series / category-based)\n- Comparing multiple curves (same X dimension)\n- Line styles such as smooth / step / straight\n\n---\n\n## 2. Minimum viable setup (checklist)\n\n1. `ChartProfile.coordinateSystem = Cartesian2D`\n2. Axes (Axis Settings)\n - X: usually **Category** (fill `labels`) or **Value** (numeric X axis)\n - Y: usually **Value**\n3. Series (Series panel)\n - Add 1 `Serie`\n - `Serie.type = Line`\n - `Serie.seriesData` has at least 2 points (a line needs at least two points)\n\n---\n\n## 3. Inspector fields (what you see in panels)\n\n- **ChartProfile / Coordinate System**\n - `coordinateSystem`\n\n- **Axis Settings** (depends on coordinate system)\n - Cartesian: `cartesian.xAxisId / cartesian.yAxisId`\n - Axis list: `axes[]` (each Axis has `axisType/labels/min/max/autoRange/...`)\n\n- **Series** (each curve)\n - `series[i].type = Line`\n - `series[i].settings`: actual type is `LineSettings`\n - `stroke`: line stroke (type/color/width/texture, etc.)\n - `point`: point marker style (toggle/size/texture, etc.)\n - `hover`: hover emphasis (enables pick radius / highlight)\n - `area`: area fill (fill under the line)\n - `series[i].labelSettings`: point labels (visibility/format/decimals/offset, etc.)\n\n---\n\n## 4. SeriesData field interpretation (runtime behavior)\n\nLine chart uses:\n\n- **X coordinate**: `SeriesData.x`\n- **Y value**: `SeriesData.value`\n- `SeriesData.y` is **not used for rendering** in line charts (do not treat `y` as the Y value).\n\nTwo common patterns:\n\n### 4.1 Category X + Value Y (most common)\n\n- X axis: `AxisType.Category`\n- `AxisConfig.labels = [\"A\",\"B\",\"C\",...]`\n- Data points:\n - `x = category index` (0/1/2..., mapped into labels)\n - `value = numeric value`\n\n### 4.2 Value X + Value Y (numeric X axis)\n\n- X axis: `AxisType.Value`\n- Data points:\n - `x = X value`\n - `value = Y value`\n\n> Additional note: when your axis dimensions are **X=Value, Y=Category**, runtime treats it as a transposed Cartesian layout (`transposed`) and swaps how X/Y are interpreted during rendering (useful for horizontal layouts).\n\n---\n\n## 5. Common style settings (LineSettings)\n\n- **Line type**: `LineSettings.stroke.lineType`\n - `Straight`: straight lines\n - `Step`: step line\n - `Smooth`: smooth curve\n\n- **Stroke width/color**: `LineSettings.stroke.width` / `LineSettings.stroke.color`\n\n- **Point markers**: `LineSettings.point.show/size/textureFill`\n\n- **Area fill**: `LineSettings.area.show` + `LineSettings.area.textureFill`\n\n---\n\n## 6. Common pitfalls and troubleshooting (by symptoms)\n\n- **Line breaks / not visible**\n - Check whether `SeriesData.value` contains `NaN/Infinity`\n - A line needs at least 2 valid points\n\n- **Points do not align with labels (Category X)**\n - Check that `x` is an index within 0..(labels.Count-1)\n - Do not write `x` as a category string (EasyChart uses index, not string)\n\n- **I filled `y`, but the chart is wrong**\n - Line chart uses `value` as the Y value, not `y`\n\n---\n\n## 7. Further reading\n\n- Axes/range, Series and data: `00_02-WorkflowAndLibrary.md`\n- Common recipes: `04_08-CommonRecipes.md`\n- FAQ: `04_09-FAQ.md`\n" },
{ id: "03_02-BarChart", relPath: "03_02-BarChart.md", title: "Bar Chart (Bar)", content: "# Bar Chart (Bar)\n\nThis chapter explains bar charts in EasyChart: the data interpretation rules (`SeriesData.x/value`), how grouping/stacking really behaves, and commonly used style fields.\n\n---\n\n## 1. Use cases\n\n- Category comparisons (compare values of A/B/C)\n- Grouped comparison (multiple Bar series side-by-side under the same category)\n- Stacked totals (stack bars within the same category)\n\n---\n\n## 2. Minimum viable setup (checklist)\n\n1. `ChartProfile.coordinateSystem = Cartesian2D`\n2. Axes\n - X: usually `AxisType.Category` (fill `labels`)\n - Y: usually `AxisType.Value` (recommended to start from 0)\n3. Series\n - Add 1 `Serie`\n - `Serie.type = Bar`\n - `Serie.seriesData` has at least 1 point\n\n---\n\n## 3. Inspector fields\n\n- **Axis Settings**\n - `cartesian.xAxisId / cartesian.yAxisId`\n - `axes[]` (AxisConfig for X/Y)\n\n- **Series**\n - `series[i].type = Bar`\n - `series[i].settings`: actual type is `BarSettings`\n - `barWidth`\n - `stacked` / `stackGroup`\n - `barGap` / `categoryGap`\n - `cornerRadius` / `cornerSegments`\n - `textureFill` (color/texture)\n - `border` / `background`\n - `hover` (enables picking/highlight)\n\n---\n\n## 4. SeriesData field interpretation (runtime behavior)\n\nBar charts primarily use:\n\n- **Category / horizontal position**: `SeriesData.x`\n - Runtime rounds `x` with `RoundToInt`, so **treat it as a category index**.\n\n- **Bar height**: `SeriesData.value`\n\n- `SeriesData.y` / `SeriesData.z` are **not used for rendering** in Bar charts (do not treat `y` as height).\n\n---\n\n## 5. Most common template: Category X + Value Y\n\n### 5.1 X axis (Category)\n\n- `AxisType = Category`\n- `labels = [\"A\",\"B\",\"C\",...]`\n- Recommended: `labelPlacement = CellCenter` (easier to center-align bars)\n\n### 5.2 Data pattern\n\n- `x = category index` (0/1/2...)\n- `value = bar height`\n\n---\n\n## 6. Grouped bars (multiple series side-by-side): the actual rule\n\nKey points:\n\n- multiple `Serie`, all `type=Bar`\n- all series share the same X categories (same labels)\n- each series uses the same `x` index to land in the same category\n\nSpacing fields:\n\n- `BarSettings.barGap`: gap between bar groups within a category\n- `BarSettings.categoryGap`: extra gap between categories (affects edge padding)\n\n---\n\n## 7. Stacked bars (stacked): the actual rule\n\nStacking happens between Bar series with the same stackGroup:\n\n- `BarSettings.stacked = true`\n- `BarSettings.stackGroup = \"Group1\"`\n\nRuntime stacking notes:\n\n- for the same `x` (category index), it accumulates positive and negative values separately (positive/negative stacks are separate)\n- the top of each stacked segment = current accumulated base + `value`\n\n---\n\n## 8. Common pitfalls and troubleshooting\n\n- **Bars appear between labels / not aligned**\n - Check X axis `labelPlacement` (recommend `CellCenter`)\n - Ensure `x` is an integer index (runtime rounds)\n\n- **Bars do not start from 0**\n - Check whether Y axis (Value Axis) has `autoRangeMin` disabled and `minValue=0` locked\n\n- **Stacking result is wrong**\n - Check that all series that should stack use the same `stackGroup`\n - Remember: positive and negative values stack separately\n\n---\n\n## 9. Further reading\n\n- Axes/range, Series and data: `00_02-WorkflowAndLibrary.md`\n- Common recipes: `04_08-CommonRecipes.md`\n- FAQ: `04_09-FAQ.md`\n" },
{ id: "03_03-ScatterChart", relPath: "03_03-ScatterChart.md", title: "Scatter Chart (Scatter)", content: "# Scatter Chart (Scatter)\n\nThis chapter explains the data field conventions for Scatter charts in EasyChart, especially the compatibility behavior of `SeriesData.y/value`, and how the `z` dimension drives `sizeMapping`.\n\n---\n\n## 1. Use cases\n\n- Correlation analysis (two numeric dimensions X/Y)\n- Distribution visualization (point cloud)\n- Outlier detection\n\n---\n\n## 2. Minimum viable setup (checklist)\n\n1. `ChartProfile.coordinateSystem = Cartesian2D`\n2. Axes\n - Common: X=Value, Y=Value\n3. Series\n - Add 1 `Serie`\n - `Serie.type = Scatter`\n - `Serie.seriesData` has at least 2 points\n\n---\n\n## 3. Inspector fields\n\n- `series[i].type = Scatter`\n- `series[i].settings`: actual type is `ScatterSettings`\n - `point`: point style (visible/size/texture)\n - `hover`: hover highlight (enabled/pickRadius/scale, etc. depending on version)\n - `sizeMapping`: point size mapping\n\n---\n\n## 4. SeriesData field interpretation (runtime behavior)\n\nScatter chart uses:\n\n- **X coordinate**: `SeriesData.x`\n- **Y coordinate**: prefers `SeriesData.y`\n - Compatibility: if `y == 0` and `value != 0`, runtime uses `value` as y\n- **Size mapping dimension**: `SeriesData.z` (when `sizeMapping.enabled=true`)\n\nSo there are two common patterns:\n\n### 4.1 Recommended (explicit X/Y)\n\n- `x = X value`\n- `y = Y value`\n\n### 4.2 Compatibility (legacy data: use value as y)\n\n- `x = X value`\n- `value = Y value`\n- `y = 0`\n\n> Recommendation: for new data, write `y` directly to avoid mixing meanings with `value`.\n\n---\n\n## 5. Standard template: Value X + Value Y\n\n- X axis: `AxisType = Value`\n- Y axis: `AxisType = Value`\n- Data: use pattern 4.1 (x/y)\n\n---\n\n## 6. sizeMapping: actual behavior\n\nWhen `ScatterSettings.sizeMapping.enabled = true`:\n\n- point radius is mapped from `SeriesData.z`\n- mapping range: `minValue/maxValue` -> `minSize/maxSize`\n- if `clamp = true`, t is clamped to 0..1\n- `curve` applies a curve transform to t (non-linear mapping)\n\nIf sizeMapping \"doesn't work\", check first:\n\n- did you actually set `z` values (default 0)\n- is `minValue/maxValue` equal (degenerates mapping)\n\n---\n\n## 7. Common pitfalls and troubleshooting\n\n- **All points are on a horizontal line**\n - you may have filled only `value`, but also set `y` to a non-zero value (compatibility won't trigger)\n - recommend using `y` consistently as the Y coordinate\n\n- **Hover does not respond**\n - `ScatterSettings.hover.enabled` must be enabled\n - too small `pickRadius` makes picking difficult\n\n- **Points are too small / too large**\n - adjust `ScatterSettings.point.size`\n - or check `minSize/maxSize` in sizeMapping\n\n---\n\n## 8. Further reading\n\n- Axes/range, Series and data: `00_02-WorkflowAndLibrary.md`\n- Common recipes: `04_08-CommonRecipes.md`\n- FAQ: `04_09-FAQ.md`\n" },
{ id: "03_04-HeatmapChart", relPath: "03_04-HeatmapChart.md", title: "Heatmap Chart (Heatmap)", content: "# Heatmap Chart (Heatmap)\n\nThis chapter explains the rules for Heatmaps in EasyChart: how coordinates/cells are mapped, how `SeriesData` fields are interpreted, and how the value-to-color range is calculated. It also notes that Heatmap is a Pro feature.\n\n---\n\n## 1. Use cases\n\n- 2D matrix visualization (rows/columns)\n- Density/intensity visualization\n\n---\n\n## 2. Important note (Pro feature)\n\n- The renderer for `SerieType.Heatmap` is registered by `EasyChartProBootstrap`.\n- If EasyChartPro is not installed/enabled, this serie may be treated as a \"dynamic renderer\" and attempted to be created, but it usually won't render.\n\n---\n\n## 3. Minimum viable setup (checklist)\n\n1. `ChartProfile.coordinateSystem = Cartesian2D`\n2. Axes (Axis Settings)\n - Most common: X=Category (columns), Y=Category (rows)\n - X/Y can also use Value axes (see section 7)\n3. Series\n - Add 1 `Serie`\n - `Serie.type = Heatmap`\n - `Serie.seriesData` has at least 1 point\n\n---\n\n## 4. Inspector fields\n\n- **Series**\n - `series[i].type = Heatmap`\n - `series[i].settings`: actual type is `HeatmapSettings`\n - `renderMode`: Grid / Gradient / Contour\n - `cellGapPx`\n - `xSplitCount` / `ySplitCount` (used when X/Y are Value axes)\n - `autoRange / minValue / maxValue`\n - `lowColor / midColor / highColor`\n - `clamp`\n - `influenceMode`: None / Bleed / Smooth\n - sub settings: `bleed / smooth / gradient / contour`\n\n---\n\n## 5. SeriesData field interpretation (runtime behavior)\n\nEach Heatmap data point corresponds to one \"cell/pixel area\". Runtime uses:\n\n- **X coordinate (column)**: `SeriesData.x`\n- **Y coordinate (row)**: `SeriesData.y`\n- **Intensity**: `SeriesData.value`\n- **Color override**: if `SeriesData.useColor = true`, runtime uses `SeriesData.color` directly and skips interpolation from `low/mid/high`.\n\n> Note: Heatmap `x/y` do not accept string categories. With Category axes, you still use indices.\n\n---\n\n## 6. Standard template: 2D Category (X/Y) + value intensity (most common)\n\n### 6.1 X axis (Category: columns)\n\n- `AxisType = Category`\n- `labels = [\"Col0\",\"Col1\",...]`\n\n### 6.2 Y axis (Category: rows)\n\n- `AxisType = Category`\n- `labels = [\"Row0\",\"Row1\",...]`\n\n### 6.3 Data pattern\n\n- `x = column index` (runtime applies `RoundToInt`)\n- `y = row index` (runtime applies `RoundToInt`)\n- `value = intensity`\n\n### 6.4 Important detail: cell count vs `labelPlacement` for Category axes\n\nRuntime uses the axis `labelPlacement` to decide whether to split into `labels.Count` cells or `labels.Count-1` cells:\n\n- `CategoryLabelPlacement.CellCenter`\n - X cell count = `labels.Count`\n - Y cell count = `labels.Count`\n\n- Others (non CellCenter)\n - X cell count = `max(1, labels.Count - 1)`\n - Y cell count = `max(1, labels.Count - 1)`\n\nThis directly affects the valid range of indices you should write into `x/y`.\n\n---\n\n## 7. Heatmap with Value axes (X/Y are numeric axes)\n\nWhen X or Y uses `AxisType.Value`:\n\n- Cell count no longer comes from labels. It comes from:\n - X: `HeatmapSettings.xSplitCount`\n - Y: `HeatmapSettings.ySplitCount`\n\n- `SeriesData.x/y` are normalized using `_xMin/_xMax` and `_yMin/_yMax`, then mapped into cell indices.\n\nThis is suitable for intensity/density distribution over a continuous value range.\n\n---\n\n## 8. Common pitfalls and troubleshooting\n\n- **All cells look the same / low contrast**\n - Check whether `HeatmapSettings.autoRange` is enabled\n - Or manually set `minValue/maxValue`\n - Also check whether all points have almost the same `value`\n\n- **Colors do not follow low/mid/high**\n - Check whether some points set `useColor=true` (it overrides palette interpolation)\n\n- **Cells are misaligned (out-of-range / off-by-one)**\n - Check whether Category axis `labelPlacement` is `CellCenter`\n - Use section 6.4 to determine correct cell count and index ranges\n\n- **Cell gaps are too large/too tight**\n - Adjust `HeatmapSettings.cellGapPx`\n\n---\n\n## 9. Further reading\n\n- Axes/range, Series and data: `00_02-WorkflowAndLibrary.md`\n- Common recipes: `04_08-CommonRecipes.md`\n- FAQ: `04_09-FAQ.md`\n" },
{ id: "03_05-RadarChart", relPath: "03_05-RadarChart.md", title: "Radar Chart (Radar)", content: "# Radar Chart (Radar)\n\nThis chapter explains how Radar charts work in EasyChart: where dimension labels come from, how value ranges are calculated, and how data point order is interpreted, mapped to Inspector fields.\n\n---\n\n## 1. Use cases\n\n- Multi-dimensional metric comparison\n- Ability/attribute radar\n\n---\n\n## 2. Minimum viable setup (checklist)\n\n1. `ChartProfile.coordinateSystem = Polar2D`\n2. Series\n - Add 1 `Serie`\n - `Serie.type = Radar`\n - `Serie.seriesData` is recommended to have at least 3 points (with <= 2 dimensions, runtime will not draw / hover won't work)\n3. PolarAxes (recommended)\n - `polarAxes.angleAxis.labels`: dimension names\n - `polarAxes.radiusAxis`: value range (auto/manual)\n\n---\n\n## 3. Inspector fields\n\n- **ChartProfile / Coordinate System**\n - `coordinateSystem = Polar2D`\n\n- **PolarAxes** (recommended for configuring Radar axes)\n - `polarAxes.angleAxis.labels`: dimension labels\n - `polarAxes.radiusAxis.autoRangeMin/autoRangeMax/minValue/maxValue/autoRangeRounding/labelFormat/...`\n\n- **Series**\n - `series[i].type = Radar`\n - `series[i].settings`: actual type is `RadarSettings`\n - `radar`: layout (startAngleDeg / clockwise / innerRadius / outerRadius / plot / background)\n - `stroke`: polyline style\n - `area`: area fill\n - `point`: point style (point visibility also affects hover pick radius)\n - `series[i].labelSettings`: data point labels (can show dimension name and value)\n\n---\n\n## 4. SeriesData field interpretation (runtime behavior)\n\nKey point for Radar: **dimension order is defined by the index of items in the `seriesData` list**.\n\n- **Value**: uses `SeriesData.value`\n- **Dimension index**: uses the position `i` in `seriesData` (0..dimensionCount-1)\n- `SeriesData.x` is **not used for positioning** in Radar rendering (do not rely on x to represent dimensions)\n\nWhere does dimensionCount come from:\n\n1. Prefer `Data.PolarAxes.angleAxis.labels.Count`\n2. If angleAxis.labels is not configured, it uses labels from a Category Axis (see section 5)\n3. Otherwise, fall back to `seriesData.Count` (or the maximum count among multiple series)\n\n---\n\n## 5. Actual priority order for dimension labels\n\nRuntime resolves dimension names in this priority order:\n\n1. `polarAxes.angleAxis.labels[i]`\n2. `labels[i]` from a `AxisType.Category` axis in `axes[]`\n - it prefers the Category axis that matches `Data.XAxisId`\n3. `seriesData[i].name`\n4. If none exists, it shows `Dim i`\n\n> Recommendation: for Radar, manage dimension names via `polarAxes.angleAxis.labels`. Use `SeriesData.name` as a fallback.\n\n---\n\n## 6. How radius value range is calculated\n\nRadar radius range is calculated from `SeriesData.value`:\n\n- By default, it computes auto range from values across all Radar series\n- If you configure `polarAxes.radiusAxis`:\n - `autoRangeMin/autoRangeMax` decides whether min/max are automatic\n - `minValue/maxValue` take effect when the corresponding auto range is disabled\n - `autoRangeRounding` rounds auto min/max to tens/hundreds/custom unit\n - `labelFormat` affects tooltip/label formatting\n\n---\n\n## 7. Common pitfalls and troubleshooting\n\n- **Radar chart not visible**\n - Check `coordinateSystem` is `Polar2D`\n - Dimension count must be > 2 (labels or seriesData must be at least 3)\n\n- **Dimensions do not match / order is wrong**\n - Radar does not use `x`. It uses `seriesData` list order as dimension order\n - Put points in `seriesData` in the intended dimension order\n\n- **Hover is hard to trigger**\n - Radar pick radius is related to `RadarSettings.point.size`\n - If `point.show=false`, pick radius becomes 0 (almost impossible to hover)\n\n---\n\n## 8. Further reading\n\n- Series and data: `00_02-WorkflowAndLibrary.md`\n- Common recipes: `04_08-CommonRecipes.md`\n- FAQ: `04_09-FAQ.md`\n" },
{ id: "03_06-PieChart", relPath: "03_06-PieChart.md", title: "Pie Chart (Pie)", content: "# Pie Chart (Pie)\n\nThis chapter explains how Pie charts work in EasyChart: how data fields are interpreted, how layout/aggregation/legend/interactions take effect, and which behaviors have hidden prerequisites, mapped to Inspector fields.\n\n---\n\n## 1. Use cases\n\n- Showing proportions/composition\n- Emphasizing the share of each category in the whole\n\nNot suitable for:\n\n- Too many categories (usually > 812 becomes hard to read)\n- Precise comparison of tiny differences (a bar chart is usually better)\n\n---\n\n## 2. Minimum viable setup (checklist)\n\n1. `coordinateSystem`: Pie does not rely on Cartesian/Polar coordinate systems (keep your Profile setting)\n2. Add 1 `Serie`:\n - `type = Pie`\n - `settings = PieSettings`\n - `seriesData` has at least 1 point\n3. Ensure each point has `value > 0`\n\n> Note: Pie currently ignores points with `value <= 0`.\n\n---\n\n## 3. Important limitations (runtime behavior)\n\n- **Only the first visible Pie serie is drawn**: the renderer iterates `Data.Series`, finds the first visible serie with `type=Pie`, draws it, then `break`s.\n- Slice hidden state comes from `ChartInteractionState.HiddenPieSliceIds`, which is added/removed when clicking legend items.\n\n---\n\n## 4. SeriesData field interpretation (runtime behavior)\n\nPie mainly uses:\n\n- `value`: numeric value (weight) of the slice\n- `name`: slice name (recommended)\n- `useColor + color`: per-slice custom color (optional)\n- `id`: stable slice identifier (for hidden/interaction state; keep it stable)\n\n### 4.1 Recommended pattern: explicit name + value\n\n- `SeriesData.name = \"Apple\"`\n- `SeriesData.value = 12`\n\n### 4.2 Name fallback when name is empty\n\nWhen `SeriesData.name` is empty, Pie may try to use **labels**, but with an important prerequisite:\n\n- If `ChartData.CoordinateSystem == None` (typically pure Pie / no coordinate system), runtime **skips label fallback** and uses only `SeriesData.name`.\n\nWhen coordinate system is not None, the fallback order is:\n\n- Prefer Category axis labels that match `Data.Cartesian.xAxisId`\n- Otherwise, use labels from any Category axis\n- Final fallback: `Slice {index}`\n\n> Therefore: if you do not want to depend on axis configuration, fill `SeriesData.name` directly.\n\n### 4.3 Color source\n\n- If `useColor=true` on the point: use `SeriesData.color`\n- Otherwise: use the built-in palette in order\n\n---\n\n## 5. Common settings (PieSettings)\n\nPie `settings` is `PieSettings`, mainly including:\n\n- `layout`: layout (angle/radius/gaps/center offset, etc.)\n- `hover`: hover interaction (explode)\n- `aggregation`: aggregation (TopN + Others)\n- `legend`: Pie-specific legend settings (replaces global legend only for \"pure Pie chart\" cases)\n\n### 4.1 layout (PieLayoutSettings)\n\nCommon fields:\n\n- `startAngleDeg`: start angle (default -90 makes the first slice start at the top)\n- `clockwise`: clockwise/counter-clockwise\n- `angleRangeDeg`: angle range (default 360; use 180 for half-pie, etc.)\n- `outerRadius`: outer radius\n - `<= 0`: auto\n - `0~1`: normalized by control size\n - `> 1`: pixels\n- `innerRadius`: inner radius (Pie usually 0; >0 creates a hole, but RingChart is recommended for ring/progress style)\n- `innerRadiusColor`: inner fill color\n- `sliceGapPx`: gap between slices (pixels)\n- `sliceGapType`: gap mode (Radial/Translate/Uniform)\n- `cornerRadius`: corner radius (pixels, limited by slice thickness)\n- `plot.padding`: padding (avoid clipping slices/outside labels)\n- `plot.centerOffset`: center offset\n\n### 4.2 hover (PieHoverSettings)\n\n- `hover.enabled`: enable hover interaction\n- `hover.explodeType`:\n - `Translate`: translate the whole slice\n - `Pull`: pull out / stretch\n - `Color`: brighten\n - `Stroke`: stroke emphasis\n- `hover.explodeDistance`: translate/pull distance (pixels)\n\n### 4.3 aggregation (PieAggregationSettings)\n\nWhen there are many categories, you can merge small items into `Others`:\n\n- `aggregation.enabled = true`\n- `keepTopN`: keep top N, merge the rest\n- `sortByValue`: sort by `value` before taking TopN\n- `othersName`: name for Others\n- `useOthersColor + othersColor`: Others color\n\n> Note: aggregation only takes effect when `keepTopN > 0` and slice count exceeds N.\n\n---\n\n## 6. Legend (PieLegendSettings) and \"hide slice\" interaction\n\nWhen the chart is a \"pure Pie chart\" (only Pie/RingChart/Pie3D and no other types):\n\n- Legend prefers `PieSettings.legend` (or the legend on RingChartSettings/Pie3DSettings), instead of `ChartData.legend`.\n- Clicking a legend item toggles `HiddenPieSliceIds`:\n - normal slices: `SeriesData.id` (if empty, uses index string)\n - aggregated Others: always `__ec_pie_others__`\n\n`PieLegendSettings.source` affects where legend items come from:\n\n- `Slice`: one entry per slice (default)\n- `RingSlice`: provides label source for RingChart/RingSlice scenarios (prefers PolarAxes.angleAxis.labels)\n- `Series`: one entry per serie (not slice-level)\n\n---\n\n## 7. Labels (SerieLabelSettings)\n\nPie labels are controlled by `Serie.labelSettings`:\n\n- `show`: whether to show\n- `fontSize / color / decimalPlaces`: font and value format\n- `showName`: whether to include slice name\n- `position`: `Outside/Inside/Center`\n- `offset`: offset\n\n---\n\n## 8. Common pitfalls (by symptoms)\n\n- **Some slices are not visible**\n - Check whether the point `value` is `<= 0`\n\n- **Slice name is not what I expect**\n - Recommended: fill `SeriesData.name` directly\n - If you rely on labels: ensure you have a Category axis with `labels`, and the order matches data point indices\n\n- **Slice colors change each time / hard to control**\n - For slices that need fixed colors: set `useColor=true` + `color` on the point\n\n- **Hidden/interaction state is unstable**\n - Ensure each point `SeriesData.id` is stable (do not regenerate ids on each refresh)\n\n---\n\n## 9. Next\n\n- Ring chart (RingChart): `03_07-RingChart.md`\n" },
{ id: "03_07-RingChart", relPath: "03_07-RingChart.md", title: "Ring Chart (RingChart)", content: "# Ring Chart (RingChart)\n\nThis chapter explains what RingChart really means (it is not a donut pie), and aligns its `SeriesData` interpretation, `RingChartSettings` configuration, and Pro/base differences with runtime behavior.\n\n---\n\n## 1. What is RingChart? (very important)\n\nIn the current EasyChart implementation:\n\n- `SerieType.RingChart` renders **multiple progress rings** (one ring per data point)\n- each ring is a **full 360° background ring + one progress arc**\n- it is not a pie chart that splits the circle into multiple slices\n\nIf you want a donut pie chart that shows composition:\n\n- it is closer to `SerieType.Pie` + `layout.innerRadius > 0`\n- but you should choose based on intent:\n - composition/proportion: use Pie\n - multi-metric progress/completion: use RingChart\n\n---\n\n## 2. Important note (Pro feature)\n\n- The renderer for `SerieType.RingChart` is registered by `EasyChartProBootstrap`.\n- Without Pro installed/enabled, this serie usually won't render.\n\n---\n\n## 3. Minimum viable setup (checklist)\n\n1. Add 1 `Serie`\n - `type = RingChart`\n - `settings = RingChartSettings`\n - `seriesData` has at least 1 point\n2. Each point has `value > 0`\n\n> Note: RingChart ignores points with `value <= 0`.\n\n---\n\n## 4. SeriesData field interpretation (runtime behavior)\n\nRingChart mainly uses:\n\n- `value`: the raw progress value\n- `name`: ring name\n- `useColor + color`: ring color (per-point override)\n- `id`: stable identifier (for legend/hidden state; keep it stable)\n\n### 4.1 Percent mode (default): value supports both 0~1 and 0~100\n\nWhen `RingChartSettings.valueMapping.mode = Percent` (default):\n\n- `value <= 0`: the ring is filtered out\n- `0~1`: treated as ratio (0.72 = 72%)\n- `> 1`: treated as percent (72 = 72%, runtime divides by 100)\n\nRecommendation: standardize one convention within your team (all 0~1 or all 0~100) to avoid mistakes.\n\n### 4.2 Range mode: map value to 0..1\n\nWhen `RingChartSettings.valueMapping.mode = Range`:\n\n- It first determines the range `min/max`:\n - `autoRange=true`: compute from values across all rings\n - `autoRange=false`: use `minValue/maxValue`\n- Then it maps to `(value-min)/(max-min)` and clamps to 0..1\n\n### 4.3 Name fallback when name is empty\n\nWhen `SeriesData.name` is empty, RingChart tries to fall back to labels:\n\n- If `ChartData.CoordinateSystem == None`: it won't use labels, and falls back to `Ring {i}`\n- Otherwise it prefers: `Data.PolarAxes.angleAxis.labels[i]`\n- Otherwise: labels from Cartesian/any Category axis `labels[i]`\n- Final fallback: `Ring {i}`\n\nIf you do not want to depend on PolarAxes configuration, fill `SeriesData.name` directly.\n\n---\n\n## 5. Inspector fields (RingChartSettings)\n\n- `series[i].type = RingChart`\n- `series[i].settings`: actual type is `RingChartSettings`\n - `layout`: angles/radius/inner-outer ring/padding/center offset\n - `valueMapping`: Percent/Range mapping rules\n - `hover`: hover emphasis (Translate/Pull/Color/Stroke)\n - `legend`: RingChart legend settings (effective for pure Pie charts)\n - `showBackground/backgroundAlpha/backgroundColor`: background ring\n - `cornerRadius`: rounded cap\n - `ringGapPx`: gap between rings\n\n### 5.1 layout (RingChartLayoutSettings)\n\nCommon fields:\n\n- `startAngleDeg`: start angle\n- `clockwise`: clockwise/counter-clockwise\n- `angleRangeDeg`: default 360; use for half-ring progress, etc.\n- `outerRadius`: outer radius (<=0 auto; 0~1 normalized; >1 pixels)\n- `innerRadius`: inner radius (0~1 normalized or pixels)\n- `plot.padding`: padding (avoid clipping hover/labels)\n- `plot.centerOffset`: center offset\n\n### 5.2 hover (PieHoverSettings)\n\n- `hover.enabled`: enable\n- `hover.explodeType`:\n - `Translate`: translate the whole ring\n - `Pull`: pull/stretch\n - `Color`: brighten\n - `Stroke`: stroke emphasis\n- `hover.explodeDistance`: translate/pull distance (pixels)\n\n### 5.3 Background ring and spacing\n\n- `showBackground`: draw background ring\n- `backgroundAlpha`: background ring alpha (multiplied into final color alpha)\n- `backgroundColor`: background ring color (when alpha=0, it falls back to ring color)\n- `ringGapPx`: gap between rings\n- `cornerRadius`: rounded cap (limited by ring thickness)\n\n---\n\n## 6. Legend and hide interaction (shared HiddenPieSliceIds with Pie)\n\n- RingChart shares `ChartInteractionState.HiddenPieSliceIds` with Pie.\n- Hidden key for each ring: prefer `SeriesData.id`, otherwise use the index string.\n- Legend label source is affected by `PieLegendSettings.source`:\n - `RingSlice` prefers `polarAxes.angleAxis.labels`.\n\n---\n\n## 7. Labels (SerieLabelSettings)\n\nRingChart labels also use `Serie.labelSettings`:\n\n- `show`: whether to show\n- `showName`: whether to show name\n- `decimalPlaces`: decimals (note: this displays the raw `value`, not a percent text multiplied by 100)\n- `position`:\n - `Outside`: outside label + leader line\n - `Center`: centered on the ring\n\n---\n\n## 6. Common pitfalls (by symptoms)\n\n- **I thought it was a donut pie, but it looks wrong**\n - This is a multi-ring progress chart: each point is one progress ring\n\n- **Progress is wrong (e.g. I set 75 but it is almost full)**\n - `value>1` is treated as percent and divided by 100\n - For 75%: use `0.75` or `75`\n\n- **Some rings are not visible**\n - Check whether `value <= 0` is being filtered\n\n- **Interaction/hidden state is unstable**\n - Ensure `SeriesData.id` is stable\n\n---\n\n## 8. Further reading\n\n- Pie (composition/proportion): `03_06-PieChart.md`\n- Series data structure: `00_02-WorkflowAndLibrary.md`\n" },
{ id: "04_08-CommonRecipes", relPath: "04_08-CommonRecipes.md", title: "Common Recipes", content: "\n# Common Recipes\n\nThis chapter provides copy-ready recipes for common charts (Series + Axes + common pitfalls) to help you create and troubleshoot quickly.\n\n---\n\n## 0. Before you start: minimum checklist\n\nWhen you see \"nothing shows\" or \"it looks weird\", check in this order:\n\n1. Does `coordinateSystem` match the SeriesType (Cartesian2D vs Polar2D)\n2. Does `series` contain at least 1 serie, and does that serie have `seriesData.Count > 0`\n3. Do axis types match what your data means:\n - Category axis: `labels` is not empty, and data `x/y` are indices (0/1/2...)\n - Value axis: data `x/y` are continuous numeric values\n4. Do you have any NaN/Infinity\n5. Did you lock Value axis range (`autoRangeMin/autoRangeMax` or fixed min/max) so data is outside the range\n\n---\n\n## 1. Line chart (Line): Category X + numeric Y\n\n### Target\n\n- X: category labels (A/B/C/D)\n- Y: numeric values\n- line points aligned to categories\n\n### Recipe\n\n1. `coordinateSystem = Cartesian2D`\n2. X axis:\n - `axisType = Category`\n - `labels = [A, B, C, D]`\n - `LabelPlacement = Tick`\n3. Y axis:\n - `axisType = Value`\n - `autoRangeMin/autoRangeMax = true`\n4. Series:\n - `type = Line`\n - points: `x=category index`, `y=value`\n\nData example (conceptual):\n\n```txt\n(x=0, y=10)\n(x=1, y=20)\n(x=2, y=15)\n(x=3, y=30)\n```\n\n### Common pitfalls\n\n- **Points do not align with labels**: check `x` starts from 0 and is within range (labels.Count)\n- **Line looks broken/jumpy**: check NaN/Infinity\n\n---\n\n## 2. Bar chart (Bar): centered categories + Y starts from 0\n\n### Target\n\n- one bar per category\n- labels centered under bars\n- Y axis starts from 0 to avoid misleading scaling\n\n### Recipe\n\n1. `coordinateSystem = Cartesian2D`\n2. X axis:\n - `axisType = Category`\n - fill `labels` with categories\n - `LabelPlacement = CellCenter`\n3. Y axis:\n - `axisType = Value`\n - force start at 0 (e.g. `minValue=0` + `autoRangeMax=true`, or equivalent)\n4. Series:\n - `type = Bar`\n - adjust bar width via `BarSettings.barWidth`\n\nData example:\n\n```txt\n(x=0, y=12)\n(x=1, y=18)\n(x=2, y=9)\n```\n\n### Common pitfalls\n\n- **Bars appear between labels**: switch `LabelPlacement` to `CellCenter`\n- **Bars too dense/too sparse**: adjust `barWidth`, `barGap`, `categoryGap`\n\n---\n\n## 3. Grouped bars (Grouped Bar): multiple series share the same categories\n\n### Recipe\n\n- multiple `Serie`, all `type = Bar`\n- each Serie uses the same `x=category index` convention\n- use `Serie.name` as group name (used by legend/tooltip)\n\nExample (conceptual):\n\n```txt\nSerie A:\n (x=0, y=10) (x=1, y=12)\nSerie B:\n (x=0, y=8) (x=1, y=15)\n```\n\n---\n\n## 4. Stacked bars (Stacked Bar): stacked + stackGroup\n\n### Recipe\n\n- Bar series that should stack:\n - `BarSettings.stacked = true`\n - `BarSettings.stackGroup = \"Group1\"` (same group stacks)\n\n### Common pitfalls\n\n- **Stack height looks wrong**: ensure all stacked series use exactly the same `stackGroup`\n\n---\n\n## 5. Scatter chart (Scatter): Value X/Y + hover + sizeMapping\n\n### Target\n\n- X/Y are continuous numeric values\n- point grows on hover\n- point size can be mapped by a dimension (sizeMapping)\n\n### Recipe\n\n1. `coordinateSystem = Cartesian2D`\n2. Set both X/Y axes to `Value`\n3. `type = Scatter`\n4. Data points: at least `x/value`, optionally use `z` as third dimension\n5. `ScatterSettings.hover.enabled = true`\n\n### Common pitfalls\n\n- **Points are too small**: increase `PointSettings.size`\n- **Hover does not respond**: check `HoverHighlightSettings.enabled` and `pickRadius`\n\n---\n\n## 6. Heatmap chart (Heatmap): (x, y, value) triplets\n\n### Target\n\n- X/Y are Category axes (2D labels)\n- color is determined by value\n\n### Recipe\n\n1. `coordinateSystem = Cartesian2D`\n2. X axis: Category + labels (column labels)\n3. Y axis: Category + labels (row labels)\n4. `type = Heatmap`\n5. Data points:\n - `x = column index`\n - `y = row index`\n - `value = intensity`\n\nExample (conceptual):\n\n```txt\n(x=0, y=0, value=0.2)\n(x=1, y=0, value=0.8)\n(x=0, y=1, value=0.5)\n```\n\n### Common pitfalls\n\n- **All cells look the same**: check `HeatmapSettings.autoRange/minValue/maxValue/clamp`\n- **Cells too small/too dense**: adjust `cellSizePx` / `cellGapPx`\n\n---\n\n## 7. Radar chart (Radar): dimension index + value\n\n### Recipe\n\n1. `coordinateSystem = Polar2D`\n2. `type = Radar`\n3. Data points:\n - `x = dimension index`\n - `value = numeric value`\n - `name = dimension name` (recommended for labels/tooltip)\n\nExample:\n\n```txt\n(x=0, value=72, name=\"Attack\")\n(x=1, value=55, name=\"Defense\")\n(x=2, value=90, name=\"Speed\")\n```\n\n### Common pitfalls\n\n- **Radar labels are missing/messy**: ensure your dimension label source is consistent (do not depend on Cartesian axes)\n- **Radar not visible**: check `coordinateSystem` is Polar2D\n\n---\n\n## 8. Interaction/tooltip stability: SeriesData.id\n\nIf you enabled selection/tooltip/hover, it is generally recommended:\n\n- keep each point `SeriesData.id` stable\n\n> Otherwise, if you generate new ids every refresh, interaction state cannot be associated correctly.\n\n---\n\n## Next\n\n- Next: `04_09-FAQ.md` (common issues + the fastest troubleshooting path)\n" },
{ id: "04_09-FAQ", relPath: "04_09-FAQ.md", title: "FAQ (Common Issues and the Fastest Troubleshooting Path)", content: "# FAQ (Common Issues and the Fastest Troubleshooting Path)\n\nThis chapter helps you locate the cause with the shortest path when you encounter \"nothing shows\", \"wrong rendering\", or \"weird interactions\".\n\n---\n\n## 0. Fast troubleshooting path (30-second version)\n\nCheck in order. This usually solves 80% of issues:\n\n1. **Does coordinate system match SeriesType**\n - Cartesian2D: Line/Bar/Scatter/Heatmap\n - Polar2D: Radar\n\n2. **Do you actually have data**\n - `series.Count > 0`\n - the serie has `seriesData.Count > 0`\n\n3. **Do axis types match what your data means**\n - Category axis: `labels` is not empty; data `x/y` are indices (0/1/2...)\n - Value axis: `x/y` are continuous numeric values\n\n4. **Any NaN / Infinity**\n\n5. **Is data excluded by Value axis range**\n - Check if min/max are fixed\n - Check if `autoRangeMin/autoRangeMax` are disabled\n\n---\n\n## 1. I clicked Preview but nothing shows\n\n### Possible causes\n\n- `seriesData` is empty\n- `Serie.visible = false`\n- coordinate system does not match SerieType (e.g. Radar in Cartesian2D)\n- axis range does not include your data (common when Value axis range is locked manually)\n\n### What to do\n\n1. In the Inspector, find `series` and expand a Serie\n2. Check:\n - whether `visible` is true\n - whether `seriesData` has any elements\n3. Check the coordinate system:\n - Radar -> `coordinateSystem = Polar2D`\n - Line/Bar/Scatter/Heatmap -> `coordinateSystem = Cartesian2D`\n4. If you are using Value axes:\n - first disable any \"fixed min/max\" range settings\n - enable both `autoRangeMin/autoRangeMax`, confirm it renders, then lock step-by-step\n\n---\n\n## 2. Category axis has labels, but points/bars are not on labels\n\n### Typical symptoms\n\n- bars appear between two labels\n- line points do not align to A/B/C\n\n### Quick conclusion\n\n- With a Category axis, `x` (or `y`) should usually be the **category index** (0/1/2/...), not an arbitrary value.\n\n### What to do\n\n- Check your data points:\n - `labels[0]` corresponds to `x=0`\n - `labels[1]` corresponds to `x=1`\n- If you are making a bar chart:\n - set `LabelPlacement` to `CellCenter`\n\n---\n\n## 3. Value axis range is weird (too large/too small)\n\n### Possible causes\n\n- Value axis range is locked (min/max)\n- only one side is locked but the other side has extreme values\n- rounding/unit snapped the range to an unsuitable unit\n\n### What to do\n\n1. First, revert to full auto range and confirm it renders\n2. Then gradually add your constraints:\n - common for bar charts: `minValue=0` + `autoRangeMax=true`\n3. If you see lots of weird decimal ticks:\n - set `labelFormat` first\n\n---\n\n## 4. Line is broken / nothing renders\n\n### Most common cause\n\n- NaN / Infinity exists in your data\n\n### What to do\n\n- Filter at the data source:\n - `float.IsNaN(x/y/z)`\n - `float.IsInfinity(x/y/z)`\n\n---\n\n## 5. Bar chart and labels do not align\n\n### Quick conclusion\n\n- 99% of the time, `LabelPlacement` is not the right one\n\n### What to do\n\n- Bar: prefer `LabelPlacement = CellCenter`\n- Line/Scatter: prefer `LabelPlacement = Tick`\n\n---\n\n## 6. Heatmap is all one color / looks flat\n\n### Possible causes\n\n- `autoRange=false` but `minValue/maxValue` are unreasonable\n- `clamp=false` and extreme values stretch the color range\n- all data points have the same `value` (or all are 0)\n\n### What to do\n\n1. Enable `autoRange`\n2. Check whether data point `value` varies\n3. If you need a fixed range:\n - set `minValue/maxValue` reasonably\n\n---\n\n## 7. Radar labels are missing/messy\n\n### Common cause\n\n- dimension label sources are inconsistent\n\n### Recommended approach\n\n- For each Radar point:\n - `x = dimension index`\n - `value = numeric value`\n - `name = dimension name`\n\nAlso ensure Radar does not rely on Cartesian axis configuration to obtain labels.\n\n---\n\n## 8. tooltip/hover/selection points to the wrong item or state is unstable\n\n### Common cause\n\n- new point identifiers are generated on every refresh\n\n### What to do\n\n- keep each point `SeriesData.id` stable\n- avoid clearing and generating a completely new set of points with `Guid.NewGuid()` on every refresh\n\n---\n\n## 9. I don't know which chapter to read\n\n- If you are in the workflow stage (create/clone/preview/export): see `01_01-EditorWorkflow.md`\n- If you are dealing with axes/range/alignment/unit/format: see section 7 of `00_02-WorkflowAndLibrary.md`\n- If you need the data conventions for a specific chart (SeriesData.x/y/z): see section 8 of `00_02-WorkflowAndLibrary.md`\n- If you just want copy-ready templates: see `04_08-CommonRecipes.md`\n" },
{ id: "05_01-UpdatePlan", relPath: "05_01-UpdatePlan.md", title: "Roadmap / Update Plan", content: "# Roadmap / Update Plan\n\nThis chapter describes the overall direction and planned items for future EasyChart updates.\n\n- This file is **not split by version phases** (more detailed planning can be added inside each chart type chapter later)\n- This file is organized by \"capability categories\" (you can treat it as a roadmap table of contents)\n\n## Free / Pro split (release strategy)\n\n### Free (standalone package)\n\n- Positioning: cover the most common AssetStore use cases, forming a full loop of \"configurable + previewable + runtime data injection\".\n- Includes: existing basic 2D chart types, runtime injection (`ChartFeed` / `ApplyJson`), editor workflow such as `EasyChartLibraryWindow`.\n\n### Pro (standalone package, includes all Free)\n\n- Positioning: provide clear differentiated capabilities and a long-term expansion path on top of Free.\n- Includes: everything in Free + Pro incremental features (advanced effects / new chart types / 3D / tooling, etc.).\n\n### Compatibility strategy\n\n- When Free encounters Pro-only assets/configurations: it is allowed to fail with a direct error (no downgrade compatibility required).\n\n### Pro initial selling points priority\n\n- A: new chart types\n- B: 3D charts\n- C: advanced 2D visual effects (e.g. texture UV animation, special rendering effects)\n\n---\n\n## Appendix: version plan (tentative timeline)\n\n> Note: the following is a tentative monthly/quarterly cadence. Specific dates can be refined after team capacity and release window are confirmed.\n\n### 2026 Q1 (JanMar): stabilization + complete Free loop\n\n- 2026-01 (Free v1.0.x):\n - Fix/finalize: stability of JSON Injection panel and example generation/parsing (based on current TODO)\n - Docs & samples: align with the latest data structures and panel capabilities\n- 2026-02 (Free v1.1.0):\n - 2D UX improvements (Free scope): Bar rounded corners, hover effects (if not implemented yet, land in this version)\n - Editor UX: small workflow improvements in LibraryWindow (no Pro dependencies)\n- 2026-03 (Free v1.1.x):\n - Regression fixes + performance/GC optimization (large data refresh, tooltip/interaction stability)\n\n### 2026 Q2 (AprJun): Pro v1.0 (new chart types first)\n\nEach series type adds an animation component collection, allowing effects such as Point blinking and TextureFill UV animation.\n- 2026-04 (Pro v1.0.0):\n - Finalize Pro package structure and release workflow (Pro includes all Free)\n - New chart types (batch 1): Gauge / Funnel (one or both depending on complexity)\n- 2026-05 (Pro v1.0.x):\n - New chart types (batch 2): BoxPlot / Candlestick (implement one with higher priority)\n - Improve Pro-only error hints and readable Editor-side error messages\n- 2026-06 (Free v1.2.0 + Pro v1.1.0):\n - Free: continue filling common 2D capabilities and stability\n - Pro: expand new chart types (Treemap / Sunburst research or first version)\n\n### 2026 H2 (JulDec): 3D roadmap and effects\n\n- 2026 Q3 (JulSep) (Pro v2.0 or v1.2+):\n - 3D charts (batch 1): 3D Bar / 3D Scatter (prioritize one to complete an end-to-end workflow)\n - 3D rendering pipeline and interaction foundations (iterate by minimum viable slices)\n- 2026 Q4 (OctDec):\n - 3D Surface (research/experimental)\n - Advanced 2D visual effects (Pro): texture UV animation (and more complex effects later)\n - Tooling improvements: Theme / direct networking / automated tests (pick one as the main quality track)\n\n---\n\n## 1. Chart type expansion plan (Chart Types)\n\n### 1.1 2D charts (enhancements to existing system)\n\n- Goal: without adding too many `SerieType`, fill common expressions via settings/variants.\n- Candidate directions (examples):\n - Line: more line types/fills/annotations (richer markers/threshold lines, etc.), texture UV animation (Pro)\n - Bar: more stacking modes, percent stacking, waterfall modes, rounded bar caps (Free), hover effects (Free), texture UV animation (Pro)\n - Scatter: more mapping dimensions (size/color), density expressions (aggregation/gridding)\n - Pie: more layout/aggregation strategies, label strategies, interactions\n\n### 1.2 New chart types (may add new `SerieType`)\n\n- Goal: support more common standalone chart categories in AssetStore.\n- Candidate directions (examples):\n - Gauge\n - Funnel\n - BoxPlot\n - Candlestick (OHLC)\n - Treemap / Sunburst (hierarchical visualization)\n - Sankey / Graph (more complex structural charts; later)\n\n### 1.3 3D charts (3D Charts)\n\n- Goal: provide a 3D chart capability set (possibly a separate rendering pipeline).\n- Candidate directions (examples):\n - 3D Bar / 3D Column\n - 3D Scatter\n - 3D Surface (higher complexity; later)\n\n---\n\n## 2. Multi-axis & coordinate systems\n\n- Goal: enhance multi-axis scenarios while keeping semantics clear.\n- Directions:\n - more axis combinations (dual Y axes, top/bottom X, mixed left/right Y)\n - clearer axis binding strategy (which axis a Serie binds to, which axis tooltip/label formats with)\n - constraints and hints for switching/mixing coordinate systems (avoid confusing configs)\n\n---\n\n## 3. Font & text system\n\n- Goal: unify text rendering look and configurable options, reducing UI Toolkit cross-platform differences.\n- Directions:\n - more complete text styles (font, size, weight, color, outline/shadow, etc.)\n - text layout strategies (wrap, truncate, ellipsis, alignment, anchors)\n - enhanced number formatting (thousands separator, units, percent, scientific notation, etc.)\n\n---\n\n## 4. Time axis & log axis\n\n- Goal: improve expression for time series and wide-range values.\n- Directions:\n - time axis: ticks, formatting, interval strategies (day/week/month/year)\n - log axis: log10/log2 ticks and labels\n - integration with data injection (how to feed time data, handle missing points)\n\n---\n\n## 5. Theme / palette system\n\n- Goal: abstract \"colors/fonts/default styles\" from individual Profiles into reusable themes.\n- Directions:\n - Theme assets (Palette + fonts + default styles)\n - override strategy between Profile and Theme (theme defaults vs Profile overrides)\n - theme preview, switching, theme library\n\n---\n\n## 6. Direct networking / data binding\n\n- Goal: reduce integration cost from network API to chart.\n- Directions:\n - standard input protocol based on `ChartFeed`\n - optional API Envelope support (e.g. `{code,message,data}`)\n - samples: HTTP fetch -> parse -> Apply\n - caching, throttling, error hints, fallback strategies\n\n---\n\n## 7. Automated tests & QA\n\n- Goal: reduce iteration risk and make refactors safer.\n- Directions:\n - data migration tests (serialization compatibility)\n - rendering regression tests (screenshot diff/pixel tolerance, or key mesh assertions)\n - interaction tests (tooltip/hit test stability)\n - performance benchmarks (large data refresh, GC, frame time)\n\n---\n\n## 8. Editor workflow & tooling\n\n- Goal: make configuration, preview, injection, and reuse smoother.\n- Directions:\n - LibraryWindow: templates/copy/import-export/batch operations\n - JSON Injection: stronger protocol compatibility, better error localization, better example generation\n - clearer manual and sample project\n" }
];