codeant-ai-for-open-source[bot] commented on code in PR #37868:
URL: https://github.com/apache/superset/pull/37868#discussion_r2875556843


##########
superset-frontend/plugins/plugin-chart-echarts/src/utils/eChartOptionsSchema.ts:
##########
@@ -0,0 +1,827 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Unified ECharts Options Schema
+ *
+ * This file serves as the single source of truth for:
+ * 1. Runtime validation (Zod schema)
+ * 2. TypeScript types (inferred from Zod)
+ *
+ * Reference: https://echarts.apache.org/en/option.html
+ */
+
+import { z } from 'zod';
+
+// 
=============================================================================
+// Common Schemas
+// 
=============================================================================
+
+/** Color value - hex, rgb, rgba, or named color */
+const colorSchema = z.string();
+
+/** Numeric or percentage string (e.g., '50%') */
+const numberOrPercentSchema = z.union([z.number(), z.string()]);
+
+/** Line type */
+const lineTypeSchema = z.union([
+  z.enum(['solid', 'dashed', 'dotted']),
+  z.array(z.number()),
+]);
+
+/** Font weight */
+const fontWeightSchema = z.union([
+  z.enum(['normal', 'bold', 'bolder', 'lighter']),
+  z.number().min(100).max(900),
+]);
+
+/** Font style */
+const fontStyleSchema = z.enum(['normal', 'italic', 'oblique']);
+
+/** Symbol type */
+const symbolTypeSchema = z.string();
+
+// 
=============================================================================
+// Text Style Schema
+// 
=============================================================================
+
+export const textStyleSchema = z.object({
+  color: colorSchema.optional(),
+  fontStyle: fontStyleSchema.optional(),
+  fontWeight: fontWeightSchema.optional(),
+  fontFamily: z.string().optional(),
+  fontSize: z.number().optional(),
+  lineHeight: z.number().optional(),
+  width: numberOrPercentSchema.optional(),
+  height: numberOrPercentSchema.optional(),
+  textBorderColor: colorSchema.optional(),
+  textBorderWidth: z.number().optional(),
+  textBorderType: lineTypeSchema.optional(),
+  textBorderDashOffset: z.number().optional(),
+  textShadowColor: colorSchema.optional(),
+  textShadowBlur: z.number().optional(),
+  textShadowOffsetX: z.number().optional(),
+  textShadowOffsetY: z.number().optional(),
+  overflow: z.enum(['none', 'truncate', 'break', 'breakAll']).optional(),
+  ellipsis: z.string().optional(),
+});
+
+// 
=============================================================================
+// Style Schemas
+// 
=============================================================================
+
+export const lineStyleSchema = z.object({
+  color: colorSchema.optional(),
+  width: z.number().optional(),
+  type: lineTypeSchema.optional(),
+  dashOffset: z.number().optional(),
+  cap: z.enum(['butt', 'round', 'square']).optional(),
+  join: z.enum(['bevel', 'round', 'miter']).optional(),
+  miterLimit: z.number().optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+  opacity: z.number().min(0).max(1).optional(),
+});
+
+export const areaStyleSchema = z.object({
+  color: z.union([colorSchema, z.array(colorSchema)]).optional(),
+  origin: z.union([z.enum(['auto', 'start', 'end']), z.number()]).optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+  opacity: z.number().min(0).max(1).optional(),
+});
+
+export const itemStyleSchema = z.object({
+  color: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderWidth: z.number().optional(),
+  borderType: lineTypeSchema.optional(),
+  borderRadius: z.union([z.number(), z.array(z.number())]).optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+  opacity: z.number().min(0).max(1).optional(),
+});
+
+// 
=============================================================================
+// Label Schema
+// 
=============================================================================
+
+export const labelSchema = z.object({
+  show: z.boolean().optional(),
+  position: z
+    .enum([
+      'top',
+      'left',
+      'right',
+      'bottom',
+      'inside',
+      'insideLeft',
+      'insideRight',
+      'insideTop',
+      'insideBottom',
+      'insideTopLeft',
+      'insideBottomLeft',
+      'insideTopRight',
+      'insideBottomRight',
+      'outside',
+    ])
+    .optional(),
+  distance: z.number().optional(),
+  rotate: z.number().optional(),
+  offset: z.array(z.number()).optional(),
+  formatter: z.string().optional(), // Only string formatters allowed, not 
functions
+  color: colorSchema.optional(),
+  fontStyle: fontStyleSchema.optional(),
+  fontWeight: fontWeightSchema.optional(),
+  fontFamily: z.string().optional(),
+  fontSize: z.number().optional(),
+  lineHeight: z.number().optional(),
+});
+
+// 
=============================================================================
+// Title Schema
+// 
=============================================================================
+
+export const titleSchema = z.object({
+  id: z.string().optional(),
+  show: z.boolean().optional(),
+  text: z.string().optional(),
+  link: z.string().optional(),
+  target: z.enum(['self', 'blank']).optional(),
+  textStyle: textStyleSchema.optional(),
+  subtext: z.string().optional(),
+  sublink: z.string().optional(),
+  subtarget: z.enum(['self', 'blank']).optional(),
+  subtextStyle: textStyleSchema.optional(),
+  textAlign: z.enum(['left', 'center', 'right']).optional(),
+  textVerticalAlign: z.enum(['top', 'middle', 'bottom']).optional(),
+  triggerEvent: z.boolean().optional(),
+  padding: z.union([z.number(), z.array(z.number())]).optional(),
+  itemGap: z.number().optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+  left: numberOrPercentSchema.optional(),
+  top: numberOrPercentSchema.optional(),
+  right: numberOrPercentSchema.optional(),
+  bottom: numberOrPercentSchema.optional(),
+  backgroundColor: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderWidth: z.number().optional(),
+  borderRadius: z.union([z.number(), z.array(z.number())]).optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+});
+
+// 
=============================================================================
+// Legend Schema
+// 
=============================================================================
+
+export const legendSchema = z.object({
+  id: z.string().optional(),
+  type: z.enum(['plain', 'scroll']).optional(),
+  show: z.boolean().optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+  left: numberOrPercentSchema.optional(),
+  top: numberOrPercentSchema.optional(),
+  right: numberOrPercentSchema.optional(),
+  bottom: numberOrPercentSchema.optional(),
+  width: numberOrPercentSchema.optional(),
+  height: numberOrPercentSchema.optional(),
+  orient: z.enum(['horizontal', 'vertical']).optional(),
+  align: z.enum(['auto', 'left', 'right']).optional(),
+  padding: z.union([z.number(), z.array(z.number())]).optional(),
+  itemGap: z.number().optional(),
+  itemWidth: z.number().optional(),
+  itemHeight: z.number().optional(),
+  itemStyle: itemStyleSchema.optional(),
+  lineStyle: lineStyleSchema.optional(),
+  textStyle: textStyleSchema.optional(),
+  icon: symbolTypeSchema.optional(),
+  selectedMode: z
+    .union([z.boolean(), z.enum(['single', 'multiple', 'series'])])
+    .optional(),
+  inactiveColor: colorSchema.optional(),
+  inactiveBorderColor: colorSchema.optional(),
+  inactiveBorderWidth: z.number().optional(),
+  backgroundColor: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderWidth: z.number().optional(),
+  borderRadius: z.union([z.number(), z.array(z.number())]).optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+  pageButtonItemGap: z.number().optional(),
+  pageButtonGap: z.number().optional(),
+  pageButtonPosition: z.enum(['start', 'end']).optional(),
+  pageIconColor: colorSchema.optional(),
+  pageIconInactiveColor: colorSchema.optional(),
+  pageIconSize: z.union([z.number(), z.array(z.number())]).optional(),
+  pageTextStyle: textStyleSchema.optional(),
+});
+
+// 
=============================================================================
+// Grid Schema
+// 
=============================================================================
+
+export const gridSchema = z.object({
+  id: z.string().optional(),
+  show: z.boolean().optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+  left: numberOrPercentSchema.optional(),
+  top: numberOrPercentSchema.optional(),
+  right: numberOrPercentSchema.optional(),
+  bottom: numberOrPercentSchema.optional(),
+  width: numberOrPercentSchema.optional(),
+  height: numberOrPercentSchema.optional(),
+  containLabel: z.boolean().optional(),
+  backgroundColor: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderWidth: z.number().optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+});
+
+// 
=============================================================================
+// Axis Schemas
+// 
=============================================================================
+
+const axisLineSchema = z.object({
+  show: z.boolean().optional(),
+  onZero: z.boolean().optional(),
+  onZeroAxisIndex: z.number().optional(),
+  symbol: z.union([z.string(), z.array(z.string())]).optional(),
+  symbolSize: z.array(z.number()).optional(),
+  symbolOffset: z.union([z.number(), z.array(z.number())]).optional(),
+  lineStyle: lineStyleSchema.optional(),
+});
+
+const axisTickSchema = z.object({
+  show: z.boolean().optional(),
+  alignWithLabel: z.boolean().optional(),
+  interval: z.union([z.number(), z.literal('auto')]).optional(),
+  inside: z.boolean().optional(),
+  length: z.number().optional(),
+  lineStyle: lineStyleSchema.optional(),
+});
+
+const axisLabelSchema = z.object({
+  show: z.boolean().optional(),
+  interval: z.union([z.number(), z.literal('auto')]).optional(),
+  inside: z.boolean().optional(),
+  rotate: z.number().optional(),
+  margin: z.number().optional(),
+  formatter: z.string().optional(), // Only string formatters
+  showMinLabel: z.boolean().optional(),
+  showMaxLabel: z.boolean().optional(),
+  hideOverlap: z.boolean().optional(),
+  color: colorSchema.optional(),
+  fontStyle: fontStyleSchema.optional(),
+  fontWeight: fontWeightSchema.optional(),
+  fontFamily: z.string().optional(),
+  fontSize: z.number().optional(),
+  align: z.enum(['left', 'center', 'right']).optional(),
+  verticalAlign: z.enum(['top', 'middle', 'bottom']).optional(),
+});
+
+const splitLineSchema = z.object({
+  show: z.boolean().optional(),
+  interval: z.union([z.number(), z.literal('auto')]).optional(),
+  lineStyle: lineStyleSchema.optional(),
+});
+
+const splitAreaSchema = z.object({
+  show: z.boolean().optional(),
+  interval: z.union([z.number(), z.literal('auto')]).optional(),
+  areaStyle: areaStyleSchema.optional(),
+});
+
+export const axisSchema = z.object({
+  id: z.string().optional(),
+  show: z.boolean().optional(),
+  gridIndex: z.number().optional(),
+  alignTicks: z.boolean().optional(),
+  position: z.enum(['top', 'bottom', 'left', 'right']).optional(),
+  offset: z.number().optional(),
+  type: z.enum(['value', 'category', 'time', 'log']).optional(),
+  name: z.string().optional(),
+  nameLocation: z.enum(['start', 'middle', 'center', 'end']).optional(),
+  nameTextStyle: textStyleSchema.optional(),
+  nameGap: z.number().optional(),
+  nameRotate: z.number().optional(),
+  inverse: z.boolean().optional(),
+  boundaryGap: z
+    .union([z.boolean(), z.array(z.union([z.string(), z.number()]))])
+    .optional(),
+  min: z.union([z.number(), z.string(), z.literal('dataMin')]).optional(),
+  max: z.union([z.number(), z.string(), z.literal('dataMax')]).optional(),
+  scale: z.boolean().optional(),
+  splitNumber: z.number().optional(),
+  minInterval: z.number().optional(),
+  maxInterval: z.number().optional(),
+  interval: z.number().optional(),
+  logBase: z.number().optional(),
+  silent: z.boolean().optional(),
+  triggerEvent: z.boolean().optional(),
+  axisLine: axisLineSchema.optional(),
+  axisTick: axisTickSchema.optional(),
+  minorTick: axisTickSchema.optional(),
+  axisLabel: axisLabelSchema.optional(),
+  splitLine: splitLineSchema.optional(),
+  minorSplitLine: splitLineSchema.optional(),
+  splitArea: splitAreaSchema.optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+});
+
+// 
=============================================================================
+// Tooltip Schema
+// 
=============================================================================
+
+export const tooltipSchema = z.object({
+  show: z.boolean().optional(),
+  trigger: z.enum(['item', 'axis', 'none']).optional(),
+  triggerOn: z
+    .enum(['mousemove', 'click', 'mousemove|click', 'none'])
+    .optional(),
+  alwaysShowContent: z.boolean().optional(),
+  showDelay: z.number().optional(),
+  hideDelay: z.number().optional(),
+  enterable: z.boolean().optional(),
+  renderMode: z.enum(['html', 'richText']).optional(),
+  confine: z.boolean().optional(),
+  appendToBody: z.boolean().optional(),
+  transitionDuration: z.number().optional(),
+  position: z
+    .union([
+      z.enum(['inside', 'top', 'left', 'right', 'bottom']),
+      z.array(z.union([z.number(), z.string()])),
+    ])
+    .optional(),
+  formatter: z.string().optional(), // Only string formatters
+  padding: z.union([z.number(), z.array(z.number())]).optional(),
+  backgroundColor: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderWidth: z.number().optional(),
+  borderRadius: z.number().optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+  textStyle: textStyleSchema.optional(),
+  extraCssText: z.string().optional(),
+  order: z
+    .enum(['seriesAsc', 'seriesDesc', 'valueAsc', 'valueDesc'])
+    .optional(),
+});
+
+// 
=============================================================================
+// DataZoom Schema
+// 
=============================================================================
+
+export const dataZoomSchema = z.object({
+  type: z.enum(['slider', 'inside']).optional(),
+  id: z.string().optional(),
+  show: z.boolean().optional(),
+  disabled: z.boolean().optional(),
+  xAxisIndex: z.union([z.number(), z.array(z.number())]).optional(),
+  yAxisIndex: z.union([z.number(), z.array(z.number())]).optional(),
+  filterMode: z.enum(['filter', 'weakFilter', 'empty', 'none']).optional(),
+  start: z.number().optional(),
+  end: z.number().optional(),
+  startValue: z.union([z.number(), z.string()]).optional(),
+  endValue: z.union([z.number(), z.string()]).optional(),
+  minSpan: z.number().optional(),
+  maxSpan: z.number().optional(),
+  minValueSpan: z.union([z.number(), z.string()]).optional(),
+  maxValueSpan: z.union([z.number(), z.string()]).optional(),
+  orient: z.enum(['horizontal', 'vertical']).optional(),
+  zoomLock: z.boolean().optional(),
+  throttle: z.number().optional(),
+  rangeMode: z.array(z.enum(['value', 'percent'])).optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+  left: numberOrPercentSchema.optional(),
+  top: numberOrPercentSchema.optional(),
+  right: numberOrPercentSchema.optional(),
+  bottom: numberOrPercentSchema.optional(),
+  width: numberOrPercentSchema.optional(),
+  height: numberOrPercentSchema.optional(),
+  backgroundColor: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderRadius: z.number().optional(),

Review Comment:
   **Suggestion:** The `borderRadius` field in the dataZoom schema only accepts 
a single number, but ECharts also supports the common four-corner array form 
(e.g., `[5, 5, 0, 0]`), so using the array form will fail validation and can 
cause the full custom options object to be treated as invalid and ignored. 
[logic error]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ⚠️ Custom dataZoom with array border radius rejected.
   - ⚠️ Users cannot style individual slider corners via editor.
   ```
   </details>
   
   ```suggestion
     borderRadius: z.union([z.number(), z.array(z.number())]).optional(),
   ```
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. In
   
`superset-frontend/plugins/plugin-chart-echarts/src/utils/eChartOptionsSchema.ts:413-461`,
   `dataZoomSchema` defines `borderRadius` as `z.number().optional()`, not 
allowing arrays.
   
   2. From a caller (e.g., the new ECharts options editor), import 
`parseEChartOptionsStrict`
   from `eChartOptionsSchema.ts:820` and call it with options containing:
   
      `{ dataZoom: { type: 'slider', borderRadius: [5, 5, 0, 0] } }`.
   
   3. Inside `parseEChartOptionsStrict`, 
`customEChartOptionsSchema.safeParse(data)`
   validates `dataZoom.borderRadius`; the value `[5, 5, 0, 0]` is an array and 
does not
   satisfy the `z.number()` schema, so `safeParse` returns `success: false`.
   
   4. Because validation fails, `parseEChartOptionsStrict` returns `{}`
   (`eChartOptionsSchema.ts:824-826`), causing all custom dataZoom styling (and 
any other
   custom options) to be ignored when rendering the chart.
   ```
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/plugin-chart-echarts/src/utils/eChartOptionsSchema.ts
   **Line:** 440:440
   **Comment:**
        *Logic Error: The `borderRadius` field in the dataZoom schema only 
accepts a single number, but ECharts also supports the common four-corner array 
form (e.g., `[5, 5, 0, 0]`), so using the array form will fail validation and 
can cause the full custom options object to be treated as invalid and ignored.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37868&comment_hash=d4354c4e7b32aba36ab13eb9a2c4e2600aed841548576e432e7c04a7ef15b6d1&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37868&comment_hash=d4354c4e7b32aba36ab13eb9a2c4e2600aed841548576e432e7c04a7ef15b6d1&reaction=dislike'>👎</a>



##########
superset-frontend/plugins/plugin-chart-echarts/src/utils/eChartOptionsSchema.ts:
##########
@@ -0,0 +1,827 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Unified ECharts Options Schema
+ *
+ * This file serves as the single source of truth for:
+ * 1. Runtime validation (Zod schema)
+ * 2. TypeScript types (inferred from Zod)
+ *
+ * Reference: https://echarts.apache.org/en/option.html
+ */
+
+import { z } from 'zod';
+
+// 
=============================================================================
+// Common Schemas
+// 
=============================================================================
+
+/** Color value - hex, rgb, rgba, or named color */
+const colorSchema = z.string();
+
+/** Numeric or percentage string (e.g., '50%') */
+const numberOrPercentSchema = z.union([z.number(), z.string()]);
+
+/** Line type */
+const lineTypeSchema = z.union([
+  z.enum(['solid', 'dashed', 'dotted']),
+  z.array(z.number()),
+]);
+
+/** Font weight */
+const fontWeightSchema = z.union([
+  z.enum(['normal', 'bold', 'bolder', 'lighter']),
+  z.number().min(100).max(900),
+]);
+
+/** Font style */
+const fontStyleSchema = z.enum(['normal', 'italic', 'oblique']);
+
+/** Symbol type */
+const symbolTypeSchema = z.string();
+
+// 
=============================================================================
+// Text Style Schema
+// 
=============================================================================
+
+export const textStyleSchema = z.object({
+  color: colorSchema.optional(),
+  fontStyle: fontStyleSchema.optional(),
+  fontWeight: fontWeightSchema.optional(),
+  fontFamily: z.string().optional(),
+  fontSize: z.number().optional(),
+  lineHeight: z.number().optional(),
+  width: numberOrPercentSchema.optional(),
+  height: numberOrPercentSchema.optional(),
+  textBorderColor: colorSchema.optional(),
+  textBorderWidth: z.number().optional(),
+  textBorderType: lineTypeSchema.optional(),
+  textBorderDashOffset: z.number().optional(),
+  textShadowColor: colorSchema.optional(),
+  textShadowBlur: z.number().optional(),
+  textShadowOffsetX: z.number().optional(),
+  textShadowOffsetY: z.number().optional(),
+  overflow: z.enum(['none', 'truncate', 'break', 'breakAll']).optional(),
+  ellipsis: z.string().optional(),
+});
+
+// 
=============================================================================
+// Style Schemas
+// 
=============================================================================
+
+export const lineStyleSchema = z.object({
+  color: colorSchema.optional(),
+  width: z.number().optional(),
+  type: lineTypeSchema.optional(),
+  dashOffset: z.number().optional(),
+  cap: z.enum(['butt', 'round', 'square']).optional(),
+  join: z.enum(['bevel', 'round', 'miter']).optional(),
+  miterLimit: z.number().optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+  opacity: z.number().min(0).max(1).optional(),
+});
+
+export const areaStyleSchema = z.object({
+  color: z.union([colorSchema, z.array(colorSchema)]).optional(),
+  origin: z.union([z.enum(['auto', 'start', 'end']), z.number()]).optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+  opacity: z.number().min(0).max(1).optional(),
+});
+
+export const itemStyleSchema = z.object({
+  color: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderWidth: z.number().optional(),
+  borderType: lineTypeSchema.optional(),
+  borderRadius: z.union([z.number(), z.array(z.number())]).optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+  opacity: z.number().min(0).max(1).optional(),
+});
+
+// 
=============================================================================
+// Label Schema
+// 
=============================================================================
+
+export const labelSchema = z.object({
+  show: z.boolean().optional(),
+  position: z
+    .enum([
+      'top',
+      'left',
+      'right',
+      'bottom',
+      'inside',
+      'insideLeft',
+      'insideRight',
+      'insideTop',
+      'insideBottom',
+      'insideTopLeft',
+      'insideBottomLeft',
+      'insideTopRight',
+      'insideBottomRight',
+      'outside',
+    ])
+    .optional(),
+  distance: z.number().optional(),
+  rotate: z.number().optional(),
+  offset: z.array(z.number()).optional(),
+  formatter: z.string().optional(), // Only string formatters allowed, not 
functions
+  color: colorSchema.optional(),
+  fontStyle: fontStyleSchema.optional(),
+  fontWeight: fontWeightSchema.optional(),
+  fontFamily: z.string().optional(),
+  fontSize: z.number().optional(),
+  lineHeight: z.number().optional(),
+});
+
+// 
=============================================================================
+// Title Schema
+// 
=============================================================================
+
+export const titleSchema = z.object({
+  id: z.string().optional(),
+  show: z.boolean().optional(),
+  text: z.string().optional(),
+  link: z.string().optional(),
+  target: z.enum(['self', 'blank']).optional(),
+  textStyle: textStyleSchema.optional(),
+  subtext: z.string().optional(),
+  sublink: z.string().optional(),
+  subtarget: z.enum(['self', 'blank']).optional(),
+  subtextStyle: textStyleSchema.optional(),
+  textAlign: z.enum(['left', 'center', 'right']).optional(),
+  textVerticalAlign: z.enum(['top', 'middle', 'bottom']).optional(),
+  triggerEvent: z.boolean().optional(),
+  padding: z.union([z.number(), z.array(z.number())]).optional(),
+  itemGap: z.number().optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+  left: numberOrPercentSchema.optional(),
+  top: numberOrPercentSchema.optional(),
+  right: numberOrPercentSchema.optional(),
+  bottom: numberOrPercentSchema.optional(),
+  backgroundColor: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderWidth: z.number().optional(),
+  borderRadius: z.union([z.number(), z.array(z.number())]).optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+});
+
+// 
=============================================================================
+// Legend Schema
+// 
=============================================================================
+
+export const legendSchema = z.object({
+  id: z.string().optional(),
+  type: z.enum(['plain', 'scroll']).optional(),
+  show: z.boolean().optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+  left: numberOrPercentSchema.optional(),
+  top: numberOrPercentSchema.optional(),
+  right: numberOrPercentSchema.optional(),
+  bottom: numberOrPercentSchema.optional(),
+  width: numberOrPercentSchema.optional(),
+  height: numberOrPercentSchema.optional(),
+  orient: z.enum(['horizontal', 'vertical']).optional(),
+  align: z.enum(['auto', 'left', 'right']).optional(),
+  padding: z.union([z.number(), z.array(z.number())]).optional(),
+  itemGap: z.number().optional(),
+  itemWidth: z.number().optional(),
+  itemHeight: z.number().optional(),
+  itemStyle: itemStyleSchema.optional(),
+  lineStyle: lineStyleSchema.optional(),
+  textStyle: textStyleSchema.optional(),
+  icon: symbolTypeSchema.optional(),
+  selectedMode: z
+    .union([z.boolean(), z.enum(['single', 'multiple', 'series'])])
+    .optional(),
+  inactiveColor: colorSchema.optional(),
+  inactiveBorderColor: colorSchema.optional(),
+  inactiveBorderWidth: z.number().optional(),
+  backgroundColor: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderWidth: z.number().optional(),
+  borderRadius: z.union([z.number(), z.array(z.number())]).optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+  pageButtonItemGap: z.number().optional(),
+  pageButtonGap: z.number().optional(),
+  pageButtonPosition: z.enum(['start', 'end']).optional(),
+  pageIconColor: colorSchema.optional(),
+  pageIconInactiveColor: colorSchema.optional(),
+  pageIconSize: z.union([z.number(), z.array(z.number())]).optional(),
+  pageTextStyle: textStyleSchema.optional(),
+});
+
+// 
=============================================================================
+// Grid Schema
+// 
=============================================================================
+
+export const gridSchema = z.object({
+  id: z.string().optional(),
+  show: z.boolean().optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+  left: numberOrPercentSchema.optional(),
+  top: numberOrPercentSchema.optional(),
+  right: numberOrPercentSchema.optional(),
+  bottom: numberOrPercentSchema.optional(),
+  width: numberOrPercentSchema.optional(),
+  height: numberOrPercentSchema.optional(),
+  containLabel: z.boolean().optional(),
+  backgroundColor: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderWidth: z.number().optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+});
+
+// 
=============================================================================
+// Axis Schemas
+// 
=============================================================================
+
+const axisLineSchema = z.object({
+  show: z.boolean().optional(),
+  onZero: z.boolean().optional(),
+  onZeroAxisIndex: z.number().optional(),
+  symbol: z.union([z.string(), z.array(z.string())]).optional(),
+  symbolSize: z.array(z.number()).optional(),
+  symbolOffset: z.union([z.number(), z.array(z.number())]).optional(),
+  lineStyle: lineStyleSchema.optional(),
+});
+
+const axisTickSchema = z.object({
+  show: z.boolean().optional(),
+  alignWithLabel: z.boolean().optional(),
+  interval: z.union([z.number(), z.literal('auto')]).optional(),
+  inside: z.boolean().optional(),
+  length: z.number().optional(),
+  lineStyle: lineStyleSchema.optional(),
+});
+
+const axisLabelSchema = z.object({
+  show: z.boolean().optional(),
+  interval: z.union([z.number(), z.literal('auto')]).optional(),
+  inside: z.boolean().optional(),
+  rotate: z.number().optional(),
+  margin: z.number().optional(),
+  formatter: z.string().optional(), // Only string formatters
+  showMinLabel: z.boolean().optional(),
+  showMaxLabel: z.boolean().optional(),
+  hideOverlap: z.boolean().optional(),
+  color: colorSchema.optional(),
+  fontStyle: fontStyleSchema.optional(),
+  fontWeight: fontWeightSchema.optional(),
+  fontFamily: z.string().optional(),
+  fontSize: z.number().optional(),
+  align: z.enum(['left', 'center', 'right']).optional(),
+  verticalAlign: z.enum(['top', 'middle', 'bottom']).optional(),
+});
+
+const splitLineSchema = z.object({
+  show: z.boolean().optional(),
+  interval: z.union([z.number(), z.literal('auto')]).optional(),
+  lineStyle: lineStyleSchema.optional(),
+});
+
+const splitAreaSchema = z.object({
+  show: z.boolean().optional(),
+  interval: z.union([z.number(), z.literal('auto')]).optional(),
+  areaStyle: areaStyleSchema.optional(),
+});
+
+export const axisSchema = z.object({
+  id: z.string().optional(),
+  show: z.boolean().optional(),
+  gridIndex: z.number().optional(),
+  alignTicks: z.boolean().optional(),
+  position: z.enum(['top', 'bottom', 'left', 'right']).optional(),
+  offset: z.number().optional(),
+  type: z.enum(['value', 'category', 'time', 'log']).optional(),
+  name: z.string().optional(),
+  nameLocation: z.enum(['start', 'middle', 'center', 'end']).optional(),
+  nameTextStyle: textStyleSchema.optional(),
+  nameGap: z.number().optional(),
+  nameRotate: z.number().optional(),
+  inverse: z.boolean().optional(),
+  boundaryGap: z
+    .union([z.boolean(), z.array(z.union([z.string(), z.number()]))])
+    .optional(),
+  min: z.union([z.number(), z.string(), z.literal('dataMin')]).optional(),
+  max: z.union([z.number(), z.string(), z.literal('dataMax')]).optional(),
+  scale: z.boolean().optional(),
+  splitNumber: z.number().optional(),
+  minInterval: z.number().optional(),
+  maxInterval: z.number().optional(),
+  interval: z.number().optional(),
+  logBase: z.number().optional(),
+  silent: z.boolean().optional(),
+  triggerEvent: z.boolean().optional(),
+  axisLine: axisLineSchema.optional(),
+  axisTick: axisTickSchema.optional(),
+  minorTick: axisTickSchema.optional(),
+  axisLabel: axisLabelSchema.optional(),
+  splitLine: splitLineSchema.optional(),
+  minorSplitLine: splitLineSchema.optional(),
+  splitArea: splitAreaSchema.optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+});
+
+// 
=============================================================================
+// Tooltip Schema
+// 
=============================================================================
+
+export const tooltipSchema = z.object({
+  show: z.boolean().optional(),
+  trigger: z.enum(['item', 'axis', 'none']).optional(),
+  triggerOn: z
+    .enum(['mousemove', 'click', 'mousemove|click', 'none'])
+    .optional(),
+  alwaysShowContent: z.boolean().optional(),
+  showDelay: z.number().optional(),
+  hideDelay: z.number().optional(),
+  enterable: z.boolean().optional(),
+  renderMode: z.enum(['html', 'richText']).optional(),
+  confine: z.boolean().optional(),
+  appendToBody: z.boolean().optional(),
+  transitionDuration: z.number().optional(),
+  position: z
+    .union([
+      z.enum(['inside', 'top', 'left', 'right', 'bottom']),
+      z.array(z.union([z.number(), z.string()])),
+    ])
+    .optional(),
+  formatter: z.string().optional(), // Only string formatters
+  padding: z.union([z.number(), z.array(z.number())]).optional(),
+  backgroundColor: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderWidth: z.number().optional(),
+  borderRadius: z.number().optional(),
+  shadowBlur: z.number().optional(),
+  shadowColor: colorSchema.optional(),
+  shadowOffsetX: z.number().optional(),
+  shadowOffsetY: z.number().optional(),
+  textStyle: textStyleSchema.optional(),
+  extraCssText: z.string().optional(),
+  order: z
+    .enum(['seriesAsc', 'seriesDesc', 'valueAsc', 'valueDesc'])
+    .optional(),
+});
+
+// 
=============================================================================
+// DataZoom Schema
+// 
=============================================================================
+
+export const dataZoomSchema = z.object({
+  type: z.enum(['slider', 'inside']).optional(),
+  id: z.string().optional(),
+  show: z.boolean().optional(),
+  disabled: z.boolean().optional(),
+  xAxisIndex: z.union([z.number(), z.array(z.number())]).optional(),
+  yAxisIndex: z.union([z.number(), z.array(z.number())]).optional(),
+  filterMode: z.enum(['filter', 'weakFilter', 'empty', 'none']).optional(),
+  start: z.number().optional(),
+  end: z.number().optional(),
+  startValue: z.union([z.number(), z.string()]).optional(),
+  endValue: z.union([z.number(), z.string()]).optional(),
+  minSpan: z.number().optional(),
+  maxSpan: z.number().optional(),
+  minValueSpan: z.union([z.number(), z.string()]).optional(),
+  maxValueSpan: z.union([z.number(), z.string()]).optional(),
+  orient: z.enum(['horizontal', 'vertical']).optional(),
+  zoomLock: z.boolean().optional(),
+  throttle: z.number().optional(),
+  rangeMode: z.array(z.enum(['value', 'percent'])).optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+  left: numberOrPercentSchema.optional(),
+  top: numberOrPercentSchema.optional(),
+  right: numberOrPercentSchema.optional(),
+  bottom: numberOrPercentSchema.optional(),
+  width: numberOrPercentSchema.optional(),
+  height: numberOrPercentSchema.optional(),
+  backgroundColor: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderRadius: z.number().optional(),
+  fillerColor: colorSchema.optional(),
+  handleSize: numberOrPercentSchema.optional(),
+  handleStyle: itemStyleSchema.optional(),
+  moveHandleSize: z.number().optional(),
+  moveHandleStyle: itemStyleSchema.optional(),
+  labelPrecision: z.union([z.number(), z.literal('auto')]).optional(),
+  textStyle: textStyleSchema.optional(),
+  realtime: z.boolean().optional(),
+  showDetail: z.boolean().optional(),
+  showDataShadow: z.union([z.boolean(), z.literal('auto')]).optional(),
+  zoomOnMouseWheel: z
+    .union([z.boolean(), z.enum(['shift', 'ctrl', 'alt'])])
+    .optional(),
+  moveOnMouseMove: z
+    .union([z.boolean(), z.enum(['shift', 'ctrl', 'alt'])])
+    .optional(),
+  moveOnMouseWheel: z
+    .union([z.boolean(), z.enum(['shift', 'ctrl', 'alt'])])
+    .optional(),
+  preventDefaultMouseMove: z.boolean().optional(),
+});
+
+// 
=============================================================================
+// Toolbox Schema
+// 
=============================================================================
+
+export const toolboxSchema = z.object({
+  id: z.string().optional(),
+  show: z.boolean().optional(),
+  orient: z.enum(['horizontal', 'vertical']).optional(),
+  itemSize: z.number().optional(),
+  itemGap: z.number().optional(),
+  showTitle: z.boolean().optional(),
+  feature: z.record(z.string(), z.unknown()).optional(),
+  iconStyle: itemStyleSchema.optional(),
+  emphasis: z
+    .object({
+      iconStyle: itemStyleSchema.optional(),
+    })
+    .optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+  left: numberOrPercentSchema.optional(),
+  top: numberOrPercentSchema.optional(),
+  right: numberOrPercentSchema.optional(),
+  bottom: numberOrPercentSchema.optional(),
+  width: numberOrPercentSchema.optional(),
+  height: numberOrPercentSchema.optional(),
+});
+
+// 
=============================================================================
+// VisualMap Schema
+// 
=============================================================================
+
+export const visualMapSchema = z.object({
+  type: z.enum(['continuous', 'piecewise']).optional(),
+  id: z.string().optional(),
+  min: z.number().optional(),
+  max: z.number().optional(),
+  range: z.array(z.number()).optional(),
+  calculable: z.boolean().optional(),
+  realtime: z.boolean().optional(),
+  inverse: z.boolean().optional(),
+  precision: z.number().optional(),
+  itemWidth: z.number().optional(),
+  itemHeight: z.number().optional(),
+  align: z.enum(['auto', 'left', 'right', 'top', 'bottom']).optional(),
+  text: z.array(z.string()).optional(),
+  textGap: z.number().optional(),
+  show: z.boolean().optional(),
+  dimension: z.union([z.number(), z.string()]).optional(),
+  seriesIndex: z.union([z.number(), z.array(z.number())]).optional(),
+  hoverLink: z.boolean().optional(),
+  inRange: z.record(z.string(), z.unknown()).optional(),
+  outOfRange: z.record(z.string(), z.unknown()).optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+  left: numberOrPercentSchema.optional(),
+  top: numberOrPercentSchema.optional(),
+  right: numberOrPercentSchema.optional(),
+  bottom: numberOrPercentSchema.optional(),
+  orient: z.enum(['horizontal', 'vertical']).optional(),
+  padding: z.union([z.number(), z.array(z.number())]).optional(),
+  backgroundColor: colorSchema.optional(),
+  borderColor: colorSchema.optional(),
+  borderWidth: z.number().optional(),
+  color: z.array(colorSchema).optional(),
+  textStyle: textStyleSchema.optional(),
+  splitNumber: z.number().optional(),
+  pieces: z.array(z.record(z.string(), z.unknown())).optional(),
+  categories: z.array(z.string()).optional(),
+  minOpen: z.boolean().optional(),
+  maxOpen: z.boolean().optional(),
+  selectedMode: z
+    .union([z.boolean(), z.enum(['single', 'multiple'])])
+    .optional(),
+  showLabel: z.boolean().optional(),
+  itemGap: z.number().optional(),
+  itemSymbol: symbolTypeSchema.optional(),
+});
+
+// 
=============================================================================
+// Series Schema
+// 
=============================================================================
+
+const emphasisSchema = z.object({
+  disabled: z.boolean().optional(),
+  focus: z
+    .enum(['none', 'self', 'series', 'ancestor', 'descendant', 'relative'])
+    .optional(),
+  blurScope: z.enum(['coordinateSystem', 'series', 'global']).optional(),
+  scale: z.union([z.boolean(), z.number()]).optional(),
+  label: labelSchema.optional(),
+  itemStyle: itemStyleSchema.optional(),
+  lineStyle: lineStyleSchema.optional(),
+  areaStyle: areaStyleSchema.optional(),
+});
+
+const stateSchema = z.object({
+  label: labelSchema.optional(),
+  itemStyle: itemStyleSchema.optional(),
+  lineStyle: lineStyleSchema.optional(),
+  areaStyle: areaStyleSchema.optional(),
+});
+
+export const seriesSchema = z.object({
+  type: z.string().optional(),
+  id: z.string().optional(),
+  name: z.string().optional(),
+  colorBy: z.enum(['series', 'data']).optional(),
+  legendHoverLink: z.boolean().optional(),
+  coordinateSystem: z.string().optional(),
+  xAxisIndex: z.number().optional(),
+  yAxisIndex: z.number().optional(),
+  polarIndex: z.number().optional(),
+  geoIndex: z.number().optional(),
+  calendarIndex: z.number().optional(),
+  label: labelSchema.optional(),
+  labelLine: z
+    .object({
+      show: z.boolean().optional(),
+      showAbove: z.boolean().optional(),
+      length: z.number().optional(),
+      length2: z.number().optional(),
+      smooth: z.union([z.boolean(), z.number()]).optional(),
+      minTurnAngle: z.number().optional(),
+      lineStyle: lineStyleSchema.optional(),
+    })
+    .optional(),
+  labelLayout: z
+    .object({
+      hideOverlap: z.boolean().optional(),
+      moveOverlap: z.enum(['shiftX', 'shiftY']).optional(),
+    })
+    .optional(),
+  itemStyle: itemStyleSchema.optional(),
+  lineStyle: lineStyleSchema.optional(),
+  areaStyle: areaStyleSchema.optional(),
+  emphasis: emphasisSchema.optional(),
+  blur: stateSchema.optional(),
+  select: stateSchema.optional(),
+  selectedMode: z
+    .union([z.boolean(), z.enum(['single', 'multiple', 'series'])])
+    .optional(),
+  zlevel: z.number().optional(),
+  z: z.number().optional(),
+  silent: z.boolean().optional(),
+  cursor: z.string().optional(),
+  animation: z.boolean().optional(),
+  animationThreshold: z.number().optional(),
+  animationDuration: z.number().optional(),
+  animationEasing: z.string().optional(),
+  animationDelay: z.number().optional(),
+  animationDurationUpdate: z.number().optional(),
+  animationEasingUpdate: z.string().optional(),
+  animationDelayUpdate: z.number().optional(),
+});
+
+// 
=============================================================================
+// Graphic Schema
+// 
=============================================================================
+
+export const graphicElementSchema = z.object({
+  type: z
+    .enum([
+      'group',
+      'image',
+      'text',
+      'rect',
+      'circle',
+      'ring',
+      'sector',
+      'arc',
+      'polygon',
+      'polyline',
+      'line',
+      'bezierCurve',
+    ])
+    .optional(),
+  id: z.string().optional(),
+  $action: z.enum(['merge', 'replace', 'remove']).optional(),
+  left: numberOrPercentSchema.optional(),
+  top: numberOrPercentSchema.optional(),
+  right: numberOrPercentSchema.optional(),
+  bottom: numberOrPercentSchema.optional(),
+  bounding: z.enum(['all', 'raw']).optional(),
+  z: z.number().optional(),
+  zlevel: z.number().optional(),
+  silent: z.boolean().optional(),
+  invisible: z.boolean().optional(),
+  cursor: z.string().optional(),
+  draggable: z
+    .union([z.boolean(), z.enum(['horizontal', 'vertical'])])
+    .optional(),
+  progressive: z.boolean().optional(),
+  width: z.number().optional(),
+  height: z.number().optional(),
+  shape: z.record(z.string(), z.unknown()).optional(),
+  style: z.record(z.string(), z.unknown()).optional(),
+  rotation: z.number().optional(),
+  scaleX: z.number().optional(),
+  scaleY: z.number().optional(),
+  originX: z.number().optional(),
+  originY: z.number().optional(),
+  children: z.array(z.record(z.string(), z.unknown())).optional(),
+});
+
+// 
=============================================================================
+// AxisPointer Schema
+// 
=============================================================================
+
+export const axisPointerSchema = z.object({
+  id: z.string().optional(),
+  show: z.boolean().optional(),
+  type: z.enum(['line', 'shadow', 'none']).optional(),
+  axis: z.enum(['x', 'y']).optional(),
+  snap: z.boolean().optional(),

Review Comment:
   **Suggestion:** The axis pointer schema omits the standard ECharts value 
`'cross'` for the `type` field, so valid configurations using a crosshair axis 
pointer will fail Zod validation and can cause the entire custom options object 
to be rejected (e.g., `parseEChartOptionsStrict` returning `{}`), effectively 
dropping user-specified options. [logic error]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ⚠️ Custom ECharts options with axisPointer cross are discarded.
   - ⚠️ Axis crosshair pointer cannot be enabled via options editor.
   ```
   </details>
   
   ```suggestion
     type: z.enum(['line', 'shadow', 'cross', 'none']).optional(),
   ```
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. In
   
`superset-frontend/plugins/plugin-chart-echarts/src/utils/eChartOptionsSchema.ts:674-721`,
   note `axisPointerSchema` defines `type` as `z.enum(['line', 'shadow', 
'none'])`.
   
   2. In any caller (e.g., the new custom ECharts options editor) import
   `parseEChartOptionsStrict` from `eChartOptionsSchema.ts:820` and call it 
with:
   
      `{ axisPointer: { type: 'cross' }, /* other valid options */ }`.
   
   3. Inside `parseEChartOptionsStrict` (`eChartOptionsSchema.ts:820-826`),
   `customEChartOptionsSchema.safeParse(data)` validates `axisPointer.type` 
against
   `axisPointerSchema`; `'cross'` is not in the enum so `safeParse` returns `{ 
success: false
   }`.
   
   4. Because validation fails, `parseEChartOptionsStrict` returns `{}` (empty 
object)
   instead of the provided options, so all custom options— including the 
intended crosshair
   axis pointer—are silently dropped before chart rendering.
   ```
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/plugin-chart-echarts/src/utils/eChartOptionsSchema.ts
   **Line:** 677:677
   **Comment:**
        *Logic Error: The axis pointer schema omits the standard ECharts value 
`'cross'` for the `type` field, so valid configurations using a crosshair axis 
pointer will fail Zod validation and can cause the entire custom options object 
to be rejected (e.g., `parseEChartOptionsStrict` returning `{}`), effectively 
dropping user-specified options.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37868&comment_hash=27d9ebe41ca1be174c0025a942e8c72f712d76c3e43aa25f18a83e95a7744917&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37868&comment_hash=27d9ebe41ca1be174c0025a942e8c72f712d76c3e43aa25f18a83e95a7744917&reaction=dislike'>👎</a>



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to