Skip to content

feat: multi SQL tabs, chart suite, Redis engine, perf/memory + UI fixes - #56

Draft
broisnischal wants to merge 30 commits into
feat/ui-design-system-revampfrom
feat/multi-sql-tabs
Draft

broisnischal wants to merge 30 commits into
feat/ui-design-system-revampfrom
feat/multi-sql-tabs

Conversation

@broisnischal

@broisnischal broisnischal commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Large multi-area branch (30 commits). Base: feat/ui-design-system-revamp.

SQL editor

  • Open multiple SQL editor tabs from Cmd-K ("New SQL Editor")

Split panes

  • Preserve all view modes (table/json/record/text/chart/erd) when a pane is demoted to a snapshot
  • Stop dimming inactive panes; never evict a visible pane's rows

Charts

  • Crash-safety: loop-based min/max, 50k-row sampling cap, error boundary
  • Fix sankey/tree/dendrogram/word-cloud; theme-aware colors across all chart paths
  • Bar value-sorting, x-label ellipsis, pie/donut/funnel aggregation, resize perf, compact Y axis, shadow-hover, horizontal scroll

Performance / memory

  • Kill scroll lag on revisited tabs (raw rows, not deep-proxied copy)
  • Allocation-free withAlpha cache; bound _tableListCache; cap infinite-scroll rows; remove stray NUL byte

Redis engine (new, end-to-end)

  • Rust redis driver wired through every match arm; connection form + capability gating
  • Keyspace browser with per-type value viewers + key actions, and a working redis-cli console; status bar Redis · db N

UI / design-system fixes

  • Search toggles use real lucide icons; macOS typography; tab menu "Reopen last tab"; submenu surfaces match parents
  • Database Objects search + tab restyle; ER Diagram toolbar; sidebar view counts; Redis replaces BigQuery; remote SQLite via libSQL

Add a 'New SQL Editor' command to the Cmd-K palette that always opens a
fresh SQL editor tab instead of focusing the single existing one. The
existing per-tab SqlTabState snapshot swap already keeps each tab's
buffer/results isolated, so multiple query editors can now be open and
switched between at once.

- studio-tabs: createSqlTab accepts an optional title (defaults unchanged)
- StudioShell: openNewSqlTab() appends a numbered 'Query Editor N' tab,
  wired to the new onnewsql palette callback
- CommandPalette: new 'New SQL Editor' item (plus icon)
@changeset-bot

changeset-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 86c1294

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

…idy tab menu

- TableToolbar: replace loud solid-blue (bg-primary) active state on the
  Aa/ab/.* search toggles with a subtle bg-primary/15 text-primary chip,
  rounded-md, lighter idle color (DESIGN_SYSTEM §3: no full-primary fill on
  small controls)
- DataTable: pin the inline insert (+Add) row with position:sticky top:HEADER_H
  instead of an absolute row whose top was recomputed from _physScrollTop every
  scroll frame — removes the vertical jitter + ghost row and keeps it in
  lock-step with columns during horizontal scroll
- TabBar: relabel 'Reopen Closed Tab' -> 'Reopen last tab'
- context-menu-item: add whitespace-nowrap so menu items never wrap to two rows
- StudioShell: never evict rows for a tab that is a visible split pane's active
  tab (would blank it to the 'Focus this pane to load' placeholder)
…orms

macOS was the only platform left on subpixel ('auto'), which renders UI text
noticeably heavier than the design intent. Switch the base html rule to
-webkit-font-smoothing: antialiased / -moz-osx-font-smoothing: grayscale so
macOS matches the crisp, lighter weight already used on Windows/Linux. Leaves
font-synthesis untouched (documented WebKit assertion on Linux startup).
PaneSnapshot always rendered the plain grid, so a split pane showing the chart
view reverted to the table when it lost focus. Add a chart branch (dataViewMode
=== 'chart', which already round-trips through the tab snapshot) that mounts
ChartView, mirroring the live view guard in StudioShell.
…-labels

- Plain bar charts now sort categories by descending magnitude (ECharts kept
  arbitrary first-seen order), so a 50-category bar chart reads as a clean
  ranking. Skipped for timestamp axes (chronological order matters) and for
  line/area/step (sorting would scramble the connected path).
- Truncated categorical x-axis labels now show an ellipsis (…) so cut values
  (e.g. long emails) are obviously truncated; the full value remains in the
  axis tooltip.
…earch clear icon

- EChartPanel: coalesce ResizeObserver bursts into one chart.resize() per
  animation frame (pane drags/window resizes fired a synchronous resize on
  every event, janking the UI)
- chart-utils: pie/donut/funnel now aggregate by category (aggPairs) instead of
  one slice per row — duplicate categories collapsed into a single summed slice,
  consistent with the bar chart; slices sorted by value desc
- ChartView: stop the axis-seed effect from clobbering the user's Category/Value/
  Group picks on every column-list change; only re-guess a selector when its
  current column no longer exists
- TableToolbar: align the search clear (x) button with the refined toggles
  (subtle idle/hover, transition) and size the icon to size-3.5
…tive panes

- PaneSnapshot now renders json/record/text/erd (not just chart), each mirroring
  the live StudioShell guard, so a demoted pane keeps whatever view it was in
  instead of reverting to the plain grid. Threads connectionId (chart) and
  schemas (erd) in as new props; replicates dataViewColumns/dataViewRows
  hidden-column filtering locally.
- PaneLayout: remove the bg-background/45 overlay that dimmed inactive split
  panes — every pane now renders at full brightness (there was no setting for
  this; it was hardcoded).
- studio-tabs: widen TableTabState.dataViewMode union to include chart | erd.
buildOption gains an accent param; it shadows the module palette with the live
theme accent as the lead color, so single-series charts and accents follow the
app's --primary token instead of a hardcoded indigo. ChartView resolves
--primary to rgb() via a probe element (zrender can't parse oklch) and re-resolves
on theme change.
…drogram/word-cloud

Crash-safety / perf:
- Replace Math.max(...arr)/Math.min(...arr) spreads (gauge/bubble/heatmap/radar/
  histogram/bullet) with loop-based arrMax/arrMin — spreads throw RangeError past
  ~100k elements, i.e. exactly the large-result case.
- ChartView caps chart input to 50k rows via uniform sampling before the O(n)
  coercion + buildOption, so ~1M-row results stay responsive; toolbar shows a
  'sampled N of M' note.
- Wrap buildOption in try/catch and the chart render in <svelte:boundary> with a
  failed snippet + Retry, so a bad chart shows an inline error instead of
  crashing the tab/app.

Correctness:
- Sankey: require a target column, aggregate duplicate source→target pairs
  (was one link per row → 8k-link hairball), drop reverse edges of 2-cycles
  (ECharts sankey needs a DAG), cap to 200 links.
- Tree/dendrogram: shared buildTreeData — dedup by name, single parent per node,
  cycle guard, capped node count (old build shared node refs across parents and
  crashed on cycles).
- Word cloud: textStyle.color was an array (invalid → black-on-black in dark
  theme); now a per-word themed-palette function; aggregate + cap to top 150.
…orizontal scroll

- Bar charts hover with a 'shadow' axisPointer (highlights the whole category
  column — the expected bar hover) instead of the thin crosshair line that made
  hover feel disconnected. Line/area keep the line pointer.
- Harden the --primary accent probe: if the var doesn't resolve to a real color
  (unset / unparseable in the engine) it silently inherited the foreground, which
  painted every bar white in the dark theme. Detect that (compare to a control
  that inherits) and fall back to the default palette.
- Value axis ticks use a compact formatter (1200000 -> 1.2M, 3500 -> 3.5k).
- bar-horizontal gains a vertical scroll/zoom slider when categories exceed ~25
  (the generic dataZoom only covered the horizontal x-axis).
…ALL charts

- ConnectionModal: swap the BigQuery provider entry for Redis (coming-soon,
  disabled), update DRIVER_ORDER + engine tint + guards.
- chart-utils: add resolveChartAccent() and resolve it inside buildOption as the
  default accent, so every chart path — table view, SQL editor, AI charts, chart
  previews — follows the theme's --primary, not just the table ChartView. Falls
  back to the default palette when --primary can't resolve. ChartView now shares
  this resolver (dedup) and keeps re-resolving on theme change.
- Client-side search filters the active object list (tables/views/functions/
  triggers) across all string columns; count strip shows 'N of M' while filtering
  and clears on tab switch. (Ctrl/Cmd+R refresh was already wired.)
- Sub-tabs: drop the mono font, add font-medium on the active tab, brighten
  inactive hover, and swap the 1px underline for a rounded h-0.5 accent bar to
  match the design-system tab treatment.
…e form

Surface the existing (fully working) libSQL/Turso remote driver from the SQLite
connection form: a Local file / Remote segmented toggle switches dbType between
'sqlite' and 'libsql', reusing the proven remote backend (no new plumbing). The
libSQL form gets the reciprocal toggle back to a local file.
Plain-view rows used a 2-column grid with no count cell, so views never showed
the row count that tables and materialized views display — the data pipeline
already fetches counts for views. Add the trailing count column (matching the
materialized-view row) so 'count' works for views too.
- Toolbar height h-10 -> h-9; title icon size-4 -> size-3.5, drop font-mono from
  chrome labels (sans for UI text).
- Schema + Export triggers/panels use the design-system recipes: border-input
  bg-input/30 triggers with data-[state=open] accent, popovers rounded-[10px]
  border-border/60 elevate-2-rim with padded rounded-md items (text-ui-sm, gap-2),
  no more font-mono/shadow-lg/raw file-type icon colors.
- Search input uses the compact input recipe (border-input bg-input/30, size-3.5
  leading icon).
- Connected toggle uses the canonical prominent-selected recipe (bg-primary/10
  text-foreground ring-primary/25) instead of the forbidden /10-vs-/15 mix.
- Layout + refresh icon buttons are now both the borderless canonical icon-button
  (were inconsistently styled); status text /45 -> /60 with tabular-nums.
StudioShell had a literal NUL (\x00) as the separator in
tabs.map(t => t.id).join(...), which made git/grep/editors treat the whole file
as binary (every search needed rg -a). Replace it with a printable '|'
separator — same reactivity-key behaviour, text-clean file.
…scroll lag)

The live rows var is $state.raw so draw() can index rows[r][c] per cell per frame
without proxy traps. But tabs is a deep-proxied $state, so a rows array stored in
a tab and read back on re-activation returned a PROXY, which the canvas then
indexed per cell per frame — reintroducing exactly the proxy-trap cost $state.raw
exists to avoid (the scroll-lag regression on any revisited table tab).

Keep the raw array in a non-reactive Map keyed by tab id and restore that raw
reference (not tab.state.rows) when re-activating a cached table tab. Released on
eviction and tab close. Additive and reversible — no change to data loading.
- canvas-table withAlpha: nested color->alpha Map so the per-cell cache-hit path
  allocates nothing (the old color+'@'+a key minted a throwaway string on every
  call — hundreds of thousands/sec during scroll → GC jank).
- StudioShell: cap _tableListCache at 64 entries (was unbounded; grew one entry
  per connection:schema for the whole session).
…ate-2-rim)

context-menu-sub-content and dropdown-menu-sub-content used rounded-xl + shadow-lg
/shadow-sm, so every submenu rendered with a different radius and shadow than its
parent menu (the inconsistent, unpolished look on the transforms submenu). Align
both to the design-system popover recipe used by the parent content
(border-border/60, rounded-[10px], elevate-2-rim) so nested menus match app-wide.
Infinite-scroll mode appended every fetched page into _infiniteRows with no
ceiling, so scrolling a large table kept the whole result set resident (and
deep-proxied) — unbounded memory. Stop auto-loading once 200k rows accumulate.
(Kept the array as $state to preserve the in-place-push length signal the grid
relies on; the cap bounds the proxy footprint.)
Drop the clear-search X from size-3.5 to size-3 so it sits lighter in the 20px
control alongside the Aa/ab/.* toggles.
The Aa / ab / .* toggles read chunky. Shrink the box size-5 -> size-4, the glyph
text-ui-2xs -> text-ui-3xs with font-semibold for legibility at the smaller size,
and dim idle a touch (/50 -> /45). Keeps the subtle bg-primary/15 active tint.
Replace the ad-hoc 'Aa'/'ab'/'.*' text labels with the proper lucide
case-sensitive / whole-word / regex icons, and style the buttons exactly like the
clear (x) button — size-5 rounded-md, size-3.5 icon, subtle idle/hover, and the
bg-primary/15 text-primary active tint. Consistent, crisp, no more text-glyph
chunkiness.
The toggle cluster animated in with transition:fly, whose transform rasterizes
its SVG children onto a sub-pixel bitmap — the blurry icons. Switch to an
opacity-only fade (no transform) so the case/word/regex icons stay pixel-crisp.
Drop the now-unused fly + cubicOut imports.
…blurry)

On the app's 14px rem base, size-3.5 = 12.25px — a fractional SVG box, so these
detailed case/word/regex icons landed off the pixel grid and rendered blurry.
Use integer px: 14px icon centred in an 18px button (2px each side) so strokes
align to whole pixels.
…risp

14px read too small; bump to a 16px icon in a 22px button (3px each side). Both
integer px so the icons stay on the pixel grid and crisp.
Backend:
- add redis 1.4.1 crate; new db/redis.rs driver (open/ping/query raw command ->
  SqlResult) with SQL-shaped stubs for the shared data contract
- RedisConfig + Redis variant on AnyConnectionConfig/ActiveConnection + driver();
  connect_redis/test_redis_connection; test_redis/connect_redis_db commands +
  lib.rs registration
- Redis arms added to every exhaustive match (query.rs, schema.rs, backup.rs,
  mcp/tools.rs) — SQL-only ops return a clean 'not supported' / empty; execute_sql
  routes raw commands to redis::query
Frontend:
- Redis selectable in the connection modal with host/port/password/DB-index/TLS
  form; api.js testRedis/connectRedis; DbType + SavedConnection + db-capabilities
  (kept out of SQL feature sets; added isKeyValue helper)

cargo check + npm run build both clean.
…y gating

- RedisKeyspacePage.svelte: left key list (grouped by ':' prefix, filter, count),
  right per-type value viewer (string/hash/list/set/zset + TTL + type chip), and a
  bottom redis-cli console (history, scrollback, auto-refresh after writes). Runs
  entirely on the executeSql->redis::query routing.
- studio-tabs: 'redis' tab kind + createRedisTab/findRedisTab + tabDisplayTitle.
- StudioShell: isRedis capability; onConnected (and auto-reconnect/connectByType/
  switch-db) skip the relational catalog load and open the Redis tab; keep-alive
  render of RedisKeyspacePage; hide the tables sidebar for redis; gate SQL-only
  welcome-grid cells + command-palette items behind !isRedis; add a Redis
  connect branch to auto-reconnect/connectByType.
- CommandPalette: isRedis/onopenredis props; SQL pages gated, 'Redis Keyspace' item.
- StatusBar: show 'Redis' (was falling through to 'PostgreSQL') + a 'db N' indicator.
The console pushed a plain object into the $state scrollback then mutated it in
place — bypassing the proxy, so replies never rendered ('cli not working').
Replace the entry immutably once the reply lands. Add redis-cli-style formatting
((nil), (integer) N, quoted strings, numbered arrays, (error) …) and handle
'clear' locally like a terminal.
- Per-key TYPE badges in the tree (string/hash/list/set/zset/stream, color-coded
  lucide glyphs; types fetched concurrently, capped at 500).
- Value panel: key toolbar (copy key, delete-with-confirm, TTL chip, size,
  reload); string viewer with JSON/Raw toggle + copy; hash/zset/list/set as clean
  sticky-header 2-col tables with per-row copy.
- Chrome: 'Keys' header + count, compact filter input recipe, canonical
  bg-primary/10 selection, group chevrons; refined redis-cli console styling.
- All key interpolations quoted via quoteArg; loaders gated on active+redis.
@broisnischal broisnischal changed the title feat(sql): open multiple SQL editor tabs from Cmd-K feat: multi SQL tabs, chart suite, Redis engine, perf/memory + UI fixes Jul 22, 2026
@broisnischal
broisnischal force-pushed the feat/ui-design-system-revamp branch from 0ac32a9 to a2da516 Compare July 22, 2026 04:07
@broisnischal
broisnischal force-pushed the feat/ui-design-system-revamp branch from a2da516 to d311a8f Compare August 8, 2026 04:32
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.

1 participant