Skip to content

feat: Charts actions: only show on hover - #336

Merged
ilyabo merged 5 commits into
mainfrom
feat--Charts-actions--only-show-on-hover
Feb 4, 2026
Merged

ilyabo merged 5 commits into
mainfrom
feat--Charts-actions--only-show-on-hover

Conversation

@ilyabo

@ilyabo ilyabo commented Feb 3, 2026 •

Copy link
Copy Markdown
Collaborator
Kapture.2026-02-03.at.18.13.44.mp4

Summary by CodeRabbit

  • Bug Fixes

    • Chart actions now show on hover/focus with smooth opacity transitions and ignore pointer events otherwise, reducing visual clutter.
  • Improvements

    • Chart editor accepts specs as raw text or objects.
    • Initial spec handling and parsing are more robust, improving editor initialization and stability.

@coderabbitai

coderabbitai Bot commented Feb 3, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@ilyabo has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 41 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Updated Vega chart UI and editor input handling: action container styling now hides/disables actions until hover/focus; VegaLiteArrowChart render tree gains an extra wrapper; editor APIs accept spec as string or object and use a normalization helper to initialize and detect spec changes.

Changes

Cohort / File(s) Summary
Actions styling
packages/vega/src/VegaChartActions.tsx
Replaced static classes with Tailwind utilities to control pointer-events and opacity transitions; actions start opacity-0 and become opacity-100 (and enabled) on :focus-within, :hover, and peer interactions while retaining overlay positioning.
Chart wrapper / imports
packages/vega/src/VegaLiteArrowChart.tsx
Wrapped chart/error rendering inside an extra <div class="peer relative">; reordered imports; preserved conditional rendering of ToolErrorMessage vs. chart content.
Spec typing (container)
packages/vega/src/editor/VegaChartContainer.tsx
Expanded VegaChartContainerProps.spec type from VisualizationSpec to `VisualizationSpec
Spec typing (types)
packages/vega/src/editor/types.ts
Expanded UseVegaChartEditorOptions.initialSpec type from VisualizationSpec to `VisualizationSpec
Editor normalization & state
packages/vega/src/editor/useVegaChartEditor.ts
Added normalizeInitialSpec helper to produce normalized/formatted/parsed representations and parseOk; initialize editor state from normalized values; compare currentInitialSpec.normalized to prevInitialSpecString to detect updates; maintain editedSpecString, appliedSpecString, originalSpecString, prevInitialSpecString, and lastValidSpec based on parsed results.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble on specs, string or tree,
I tidy them up so charts run free.
Actions hide, then sparkle on hover,
I hop, I parse, I tidy cover-to-cover. 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the primary change: making chart actions visible only on hover. This aligns with the file-level changes in VegaChartActions.tsx that add opacity transitions triggered by hover, focus-within, and peer interactions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat--Charts-actions--only-show-on-hover

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/vega/src/VegaChartActions.tsx`:
- Line 39: The action container currently hides controls by default using
"opacity-0 pointer-events-none" and only reveals them on hover; update the class
string in VegaChartActions (the container rendering the actions) to also reveal
on keyboard focus and touch by adding the Tailwind variants
"group-focus-within:pointer-events-auto group-focus-within:opacity-100" and a
touch fallback such as "pointer-coarse:pointer-events-auto
pointer-coarse:opacity-100" (or the equivalent non-hover/touch-safe variants
your Tailwind version supports) so actions become visible and interactive for
keyboard and touch users.

Comment thread packages/vega/src/VegaChartActions.tsx Outdated
Signed-off-by: Ilya Boyandin <[email protected]>
Signed-off-by: Ilya Boyandin <[email protected]>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/vega/src/editor/useVegaChartEditor.ts`:
- Around line 33-58: The current normalizeInitialSpec treats any truthy result
from safeJsonParse as a valid VisualizationSpec; change normalizeInitialSpec to
validate that parsed is a non-null plain object (typeof parsed === 'object' &&
parsed !== null && !Array.isArray(parsed)) before returning parseOk: true and
using parsed; if the parsed value is not an object (array or primitive), treat
it as a failed parse (parsed: null, parseOk: false) and return
normalized/formatted as the original spec string so onSpecChange and Vega embed
only receive actual object specs.

Comment on lines +33 to +58
const normalizeInitialSpec = (spec: VisualizationSpec | string) => {
if (typeof spec === 'string') {
const parsed = safeJsonParse(spec) as VisualizationSpec | null;
if (parsed) {
return {
parsed,
normalized: JSON.stringify(parsed),
formatted: JSON.stringify(parsed, null, 2),
parseOk: true,
};
}
return {
parsed: null,
normalized: spec,
formatted: spec,
parseOk: false,
};
}

return {
parsed: spec,
normalized: JSON.stringify(spec),
formatted: JSON.stringify(spec, null, 2),
parseOk: true,
};
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find the file and understand its structure
find . -name "useVegaChartEditor.ts" -type f

Repository: sqlrooms/sqlrooms

Length of output: 109


🏁 Script executed:

# Locate VisualizationSpec type definition
rg -t ts "VisualizationSpec" --max-count 20

Repository: sqlrooms/sqlrooms

Length of output: 2584


🏁 Script executed:

# Find safeJsonParse implementation
rg -t ts "safeJsonParse" -B 2 -A 10

Repository: sqlrooms/sqlrooms

Length of output: 9927


🌐 Web query:

vega-embed VisualizationSpec type definition documentation

💡 Result:

VisualizationSpec in vega-embed is a TypeScript type alias:

Meaning: wherever vega-embed accepts a VisualizationSpec, you can pass either:

  1. a Vega-Lite spec (VlSpec) — see Vega-Lite spec docs (JSON structure) (vega.github.io) and the Vega-Lite TypeScript typing entrypoint (TopLevelSpec) (vega.github.io)
  2. a Vega spec (VgSpec) — see Vega spec docs (JSON structure) (vega.github.io)

Where to find the VisualizationSpec definition docs:

Citations:


🏁 Script executed:

# Check the full context of useVegaChartEditor.ts to see how parsedSpec is used
cat packages/vega/src/editor/useVegaChartEditor.ts

Repository: sqlrooms/sqlrooms

Length of output: 8403


Validate parsed string specs are objects before treating them as VisualizationSpec.

safeJsonParse can return arrays or primitives (e.g., "[]" or "\"foo\""), which are truthy and currently treated as valid specs. That can flow into onSpecChange and Vega embed with an invalid shape. Consider an explicit object check.

🔧 Suggested guard
-  const normalizeInitialSpec = (spec: VisualizationSpec | string) => {
+  const normalizeInitialSpec = (spec: VisualizationSpec | string) => {
+    const isSpecObject = (value: unknown): value is VisualizationSpec =>
+      typeof value === 'object' && value !== null && !Array.isArray(value);
     if (typeof spec === 'string') {
-      const parsed = safeJsonParse(spec) as VisualizationSpec | null;
-      if (parsed) {
+      const parsed = safeJsonParse(spec);
+      if (isSpecObject(parsed)) {
         return {
-          parsed,
+          parsed,
           normalized: JSON.stringify(parsed),
           formatted: JSON.stringify(parsed, null, 2),
           parseOk: true,
         };
       }
       return {
-        parsed: null,
+        parsed: null,
         normalized: spec,
         formatted: spec,
         parseOk: false,
       };
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const normalizeInitialSpec = (spec: VisualizationSpec | string) => {
if (typeof spec === 'string') {
const parsed = safeJsonParse(spec) as VisualizationSpec | null;
if (parsed) {
return {
parsed,
normalized: JSON.stringify(parsed),
formatted: JSON.stringify(parsed, null, 2),
parseOk: true,
};
}
return {
parsed: null,
normalized: spec,
formatted: spec,
parseOk: false,
};
}
return {
parsed: spec,
normalized: JSON.stringify(spec),
formatted: JSON.stringify(spec, null, 2),
parseOk: true,
};
};
const normalizeInitialSpec = (spec: VisualizationSpec | string) => {
const isSpecObject = (value: unknown): value is VisualizationSpec =>
typeof value === 'object' && value !== null && !Array.isArray(value);
if (typeof spec === 'string') {
const parsed = safeJsonParse(spec);
if (isSpecObject(parsed)) {
return {
parsed,
normalized: JSON.stringify(parsed),
formatted: JSON.stringify(parsed, null, 2),
parseOk: true,
};
}
return {
parsed: null,
normalized: spec,
formatted: spec,
parseOk: false,
};
}
return {
parsed: spec,
normalized: JSON.stringify(spec),
formatted: JSON.stringify(spec, null, 2),
parseOk: true,
};
};
🤖 Prompt for AI Agents
In `@packages/vega/src/editor/useVegaChartEditor.ts` around lines 33 - 58, The
current normalizeInitialSpec treats any truthy result from safeJsonParse as a
valid VisualizationSpec; change normalizeInitialSpec to validate that parsed is
a non-null plain object (typeof parsed === 'object' && parsed !== null &&
!Array.isArray(parsed)) before returning parseOk: true and using parsed; if the
parsed value is not an object (array or primitive), treat it as a failed parse
(parsed: null, parseOk: false) and return normalized/formatted as the original
spec string so onSpecChange and Vega embed only receive actual object specs.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/vega/src/editor/useVegaChartEditor.ts (1)

223-249: ⚠️ Potential issue | 🟡 Minor

Reset should still restore invalid original spec strings.

Now that initialSpec can be a string, resetToOriginal no-ops when the original JSON is invalid, so users can’t revert to the original raw spec. Consider falling back to the raw string and original SQL in the catch branch.

🛠️ Proposed fallback for invalid original spec
       } catch {
-        return prev;
+        return {
+          ...prev,
+          editedSpecString: prev.originalSpecString,
+          editedSql: prev.originalSql,
+          appliedSpecString: prev.originalSpecString,
+          appliedSql: prev.originalSql,
+          prevInitialSpecString: prev.originalSpecString,
+          prevInitialSql: prev.originalSql,
+        };
       }

@ilyabo
ilyabo merged commit 87a21ce into main Feb 4, 2026
3 checks passed
@ilyabo
ilyabo deleted the feat--Charts-actions--only-show-on-hover branch February 4, 2026 00:03
@coderabbitai coderabbitai Bot mentioned this pull request Mar 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants