Skip to content

feat: v0.9.0 — Multi-tab editor, schema designer pro, analytics engine, and SSH tunnel#23

Open
Peter-L-SVK wants to merge 116 commits into
mainfrom
stage
Open

feat: v0.9.0 — Multi-tab editor, schema designer pro, analytics engine, and SSH tunnel#23
Peter-L-SVK wants to merge 116 commits into
mainfrom
stage

Conversation

@Peter-L-SVK

@Peter-L-SVK Peter-L-SVK commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Overview

v0.9.0 is the largest release to date. Major new subsystems include a
multi-tab SQL editor, a significantly upgraded schema designer with
undo/redo and bidirectional relationships, a Polars-based analytics
engine with 9 AI analyzers, full SSH tunnel support, and an integrated
terminal.


New Features

Multi-Tab SQL Editor

  • Open multiple editor tabs with Ctrl+T / Ctrl+W shortcuts
  • Unsaved changes indicator (dot) on modified tabs
  • Unsaved changes dialog on close/quit
  • Session restore: tabs reopen on startup with their content
  • Custom popover-based SQL autocomplete with preferences toggle
  • Find and Replace (Ctrl+F / Ctrl+H) with safety limits on highlighting

Schema Designer Pro

  • Color schemes: selectable table color themes with active highlight
  • Bidirectional FK: directional arrows showing relationship direction
  • Toggle FK direction: single click on relationship line to swap
  • Zoom and Pan: zoom in/out with theme-aware background
  • Popover color picker: preset colors + custom color dialog
  • Undo/Redo: full stack for all designer actions
  • Parallel path routing: relationship line paths are computed
    asynchronously across multiple CPU cores using the worker pool's
    ProcessPoolExecutor. The UI remains responsive during routing
    because the computation runs in background processes, bypassing
    Python's GIL. Results stream back via GLib.idle_add for smooth
    canvas updates without blocking the GTK main loop.
  • Line jumps + waypoints: relationship lines intelligently route
    around other lines. Waypoints are computed concurrently for each
    FK pair, with line jumps (gaps where lines cross) rendered after
    all paths are resolved.
  • Arrowhead fixes: correct angles for curve and orthogonal styles
  • Cardinality labels: 1 at source, N at target (FK semantics)
  • Package refactored: split into schema_designer/ with mixins
  • Memory leak fixed: worker pool processes properly cleaned up
    after each routing batch
  • Crash fixed: reopening designer with file drop no longer crashes

Analytics Engine (Polars + AI Tools)

  • 9 AI analyzers built on Polars for table and query analysis
  • Table growth analysis with historical tracking
  • Robust schema parser with multi-format SQL support

Right-Click Analytics Menu

Right-click any table or view in the database browser:

Function Description
Percentile Bands p10, p25, p50, p75, p90, IQR distribution summary
Trend Forecast Linear regression with slope, R-squared, next 5 predictions
Anomaly Detection IQR-based outlier detection (1.5x rule)
Moving Average 7-period rolling mean, last 10 values
Exponential Smoothing ETS span=12, last 10 values
Correlation Matrix Pearson correlation across all numeric columns

SSH Tunnel

  • Full SSH tunnel implementation (replaces v0.8 stub)
  • Single connection reuse across queries
  • Supports password and key-based authentication

Built-in Terminal

  • VTE-based terminal embedded in the application
  • Customizable colors and fonts
  • Correct spawn arguments for bash/zsh/fish

Hooks — New Additions

  • Synthetic Data Generator: configurable test data with presets
    and multi-CPU support for large datasets
  • Keboola Normalizer: cloud integration hook for Keboola platform

Session Restore

  • Open tabs, their content, and editor state restored on startup

Refactoring

  • results.py split into package with separate themes module
  • MainWindow split into package with modular mixins
  • editor_tabs separated into its own package
  • schema_designer separated into its own package
  • Old window.py and editor.py removed, imports updated

Code Quality & CI/CD

  • Black formatting applied project-wide
  • mypy type checking passing
  • flake8 linting passing (E402, E501 configured)
  • CI/CD pipeline: RPM build (Fedora/RHEL), DEB build (Debian/Ubuntu)
  • GitHub Actions PR check workflow

Preferences & Settings

  • confirm_close preference wired to actual close behavior
  • Autocomplete toggle added to preferences dialog

Packaging

  • vte291-gtk4 dependency added for terminal support
  • DEB build script fixes for dependency management
  • RPM build fixes for CI compatibility

Deferred to v1.0.0

  • FK Editor dialog
  • Lazy loading in browser tree
  • Analytics dashboard (View menu item exists)
  • Migration generator (Tools menu item exists)
  • Query analyzer (Tools menu item, now partially implemented)

- Generates realistic supermarket data using Faker library
- Creates supermarket table with 8 columns
- Batch inserts 100 rows per execution
- Useful for testing Auto-Vacuum Advisor and Schema Anomaly Detector
- Three data presets: Supermarket, Users, E-Commerce
- Configurable row count via dialog with dropdown preset selector
- Option to drop existing table before generation
- Uses Faker library for realistic test data
- Lazy import pattern for robustness
- ProcessPoolExecutor splits work across N-1 cores for >1000 rows
- Tested on 1M rows with 31 workers on R9 5950X (~125k rows/sec)
- Checkbox in generator dialog to enable multi-CPU mode
- Drop existing table checkbox for clean regeneration
- _generate_chunk as module-level function for ProcessPoolExecutor
- Automatic fallback to single-CPU for small datasets
- Debounce search-as-you-type with 300ms delay
- Skip highlighting for files larger than 500KB
- Limit to 5000 highlighted matches maximum
- Prevents UI freezes on large files with many matches
…ight

- 8 predefined colors from config.py
- Color buttons in designer toolbar with visual preview
- Click table to select, then click color to apply
- Active color button highlighted with suggested-action CSS class
- FK lines inherit color from source table
- Table borders and selection highlight match table color
- FK direction toggle button (→ forward, ← reverse)
- Arrow and cardinality labels swap based on direction
- Line always connects source and target tables
- ForeignKey.direction attribute stores current direction
- Zoom with Ctrl+Scroll, Ctrl+/-, Ctrl+0, and 1:1 reset button
- Pan with middle mouse drag and arrow keys
- Canvas background color from config.py (theme-aware)
- Fix FK line selection: closest-distance algorithm with 15px threshold
- Prevents wrong FK selection when multiple lines overlap
- ToggleButton opens popover with FlowBox preset colors
- Custom colors section (last 16 picked via GTK ColorDialog)
- Pick Custom Color button uses Gtk.ColorDialog (GTK 4.10+)
- Color button updates when table selection changes
- Proper closure capture for lambda callbacks in loops
- Fix: use set_can_target(False) so clicks pass through DrawingArea
- Each direction has its own arrowhead calculation and label placement
- Forward: arrow from source to target, 1 at source, N at target
- Reverse: arrow from target to source, 1 at target, N at source
- Labels offset based on direction to avoid line overlap
- Removes broken arrow_x1/arrow_y1 swap logic
- Pan: subtract previous cumulative offset to get per-frame delta
- Zoom: natural scroll direction (up=zoom in, down=zoom out)
- Add explanatory comments for non-obvious design decisions
- FK arrowhead and labels now inherit line color from FK object
- from_table = child = N (many)
- to_table = parent = 1 (one)
- Labels now correct regardless of arrow direction
- Undo/redo buttons in toolbar with Ctrl+Z/Ctrl+Y shortcuts
- State saved before add/delete/drag/color/FK operations
- Deep copy of tables and relationships for each state
- Max 50 states, redo stack cleared on new action
- Table index rebuilt on state restore
- EditorTabs container with GtkNotebook for multiple SQL files
- EditorTab with GtkSourceView, SQL syntax highlighting
- Tab close buttons and drag-reorder support
- Ctrl+T new tab, Ctrl+W close tab
- File > New Tab / Close Tab menu items
- Replaces single SQLEditor in MainWindow
- Line jumps (bridges) at FK line intersections with background-colored
  circles and semicircle arcs — cleaner than DBeaver Community
- Waypoint support: drag to reposition, double-click to delete
- Multi-core path computation via ProcessPoolExecutor with spawn context
- _draw_segment_styled for consistent curve/ortho/straight rendering
- Iterative collision avoidance with corner-based detour routing
- Debounced async path scheduling to prevent UI lag during drag
- Arrowhead labels corrected to 1->N (parent->child)
- Worker pool margin unified to 10px, spawn context prevents GTK fork issues
- Add _on_close() to invalidate in-flight worker callbacks by incrementing
  _path_serial before the designer widget is destroyed
- Connect dialog close-request to _on_close() in _on_designer_clicked
- Prevents idle_add calls on destroyed canvas when workers return late
- WorkerBridgeMixin: async path computation via ProcessPoolExecutor
- 31 workers with spawn context for full CPU utilization
- Line jumps (bridges) at FK intersections
- Corner-based detour routing around obstacles
- Stable 1/N labels anchored to table positions
- Split into package: models, routing, drawing, worker_bridge, designer
- Fix: _on_close prevents crash when reopening designer
…es visual style

- Add is_detour detection: only first/last segment gets curve/ortho style
- Middle detour segments always render as straight lines
- _visual_approach_angle computes correct arrowhead direction for
  curve (horizontal) and ortho (last segment horizontal) styles
- Prevents crazy line artefacts from double-bending detour segments
- Add WorkerPool.restart() to recycle worker processes after batch completion
- Call restart() when all FK paths are computed or on timeout
- Shutdown pool in _on_close() when designer dialog is destroyed
- Cancel futures after reading results to free callback references
- Limit path points to 50 to prevent runaway memory usage
- Prevents OOM crash when importing large schemas
- ssh_tunnel.py: paramiko-based local port forwarding via direct-tcpip
- Support RSA, Ed25519, ECDSA keys with ~ expansion
- SSHTunnelConfig dataclass for clean parameter passing
- ConnectionProfile extended with SSH fields and keyring storage
- Connection dialog with SSH tab (host, port, user, auth toggle)
- Auth switch between password, key file (with file browser), agent
- DatabaseConnector reuses active tunnel instead of creating new ones
- Tunnel cleanup on disconnect
- Tested and working with localhost SSH + PostgreSQL
Polars Engine:
- AnalyticsEngine class replacing pandas with Rust-backed Polars
- table_stats, table_stats_safe, column_profile, correlation_matrix
- compare_tables: schema diff between two tables
- Export methods prepared for CSV, JSON, Parquet
- Safe defaults for UI consumption via table_stats_safe

AI Tools (extracted to src/ui/ai_tools.py):
- AIToolsPopover class with 9 analysis tools
- Index Advisor: FK + naming pattern recommendations (HIGH/MEDIUM/LOW)
- Missing FKs: detect *_id columns without foreign key constraints
- Duplicate Rows: find duplicate data across all tables via Polars
- Unused Indexes: find indexes with zero scans from pg_stat_user_indexes
- Slow Queries: pg_stat_statements with auto-enable, falls back to pg_stat_activity
- Table Growth: sizes, dead rows, vacuum status, index/data ratio
- Column Statistics: min, max, null %, unique % for all columns via Polars
- Query Analyzer: table stats with null detection via table_stats_safe
- Performance Insights: cache hit ratio, top 10 largest tables
- All tools show results in results table with status bar messages
- English docstrings on all public methods
- toolbar.py simplified to UI layout only
Schema Parser:
- Remove inline comments before parsing to prevent column confusion
- Handle NUMERIC(precision,scale) without spaces
- Support SERIAL types with auto PK detection
- Parse inline REFERENCES in CREATE TABLE bodies
- Skip CHECK, UNIQUE, EXCLUDE constraint lines
- Deduplicate foreign keys by (from->to) pair

Table Growth:
- Fix column name: tablename -> relname in pg_stat_user_tables
- Consistent MB formatting for all size columns (0.0 MB)
- Show last vacuum/autovacuum/analyze timestamps
- Replace GtkSource.CompletionWords with custom Gtk.Popover autocomplete
- Case-insensitive matching via word.upper() comparison
- Popover appears after 2+ characters typed with 100ms debounce
- Arrow keys navigate popover list, Enter/Tab apply selection
- Escape closes popover without applying
- 80+ SQL keywords including PostgreSQL-specific extensions
- Popover positioned at cursor location via buffer_to_window_coords
- ListBox with flat style for native look
- No Gio.Task, GObject, or CompletionProvider — avoids PyGObject crashes
- Completing flag prevents recursive buffer change triggers
- Preferences dialog now has 'Enable SQL keyword autocomplete' checkbox
- Setting saved persistently via Settings manager
- EditorTab._autocomplete_enabled class flag controls all tabs globally
- _on_buffer_changed skips completion when disabled
- Setting applied immediately on Apply button click
- Hide popover when deleting characters (shrinking text)
- Require minimum 3 characters before showing suggestions
- Limit to 15 suggestions to avoid overwhelming the user
- Track text length to detect typing vs deleting
- KebolaNormalizerHook: CSV data validation and quality checks
- Validators: email, date, payment method, order status, category, price, quantity
- Local mode for offline validation without cloud upload
- Optional upload to Keboola Storage via kbcstorage client
- KeboolaConfigDialog: GUI configuration for API token, bucket, tables
- Test connection button to verify Keboola credentials
- Recommendations generated based on validation results
- Hook metadata for discovery and scheduling
- Configuration persisted to ~/.config/sql-schema-studio/keboola_config.json
- English docstrings and comments throughout
…g, and Debian deps

- terminal: add missing child_setup_data parameter to Vte.Terminal.spawn_async()
  fixing "Argument 9 does not allow None as a value" TypeError
- hooks: wrap triggers metadata in a list so registry iterates triggers, not
  individual characters from a bare string
- packaging: replace libvte-2.91-gtk4-dev with gir1.2-vte-3.91 runtime binding,
  add python3-gi-cairo dependency, move pip-only kbcstorage to Suggests
- Fixed incorrect package names in installation instructions (python3-sklearn → python3-scikit-learn, python3-psycopg → python3-psycopg2)
- Added pipx as recommended installation method for optional Python packages
- Separated required vs optional dependencies with clear documentation
- Improved WSL2 setup instructions with proper GUI and PostgreSQL configuration
- Added dependency breakdown section explaining purpose of each package
- Added installation troubleshooting guide for common issues
- Added development installation section for contributors
- Added quick start section for users preferring pre-built DEB packages
- Aligned README with updated DEB package dependencies structure
- Enhanced user guidance for optional features (faker, kbcstorage)
@Peter-L-SVK Peter-L-SVK added the documentation Improvements or additions to documentation label Jun 21, 2026
- Fixed incorrect package names in installation instructions
- Aligned README with updated DEB package dependencies structure
@Peter-L-SVK

Peter-L-SVK commented Jun 21, 2026

Copy link
Copy Markdown
Owner Author

Tested on VMs

Updated section for Ubuntu/Debian steps now works properly. Tested also on Mint 22.2 MATE

Screenshot from 2026-06-20 21-59-12

Tested with white scheme on Zorin 18

- removed pip from intsall steps, Ubuntu distros now using apt for py3 deps
- fixed blank space error in build-deb.sh
Adds 8 pre-defined color themes and font selection for the terminal
in Preferences. Settings are persisted across sessions.

- Added TERMINAL_THEMES with Dark, Light, Tango, Solarized, Monokai, Nord
- Added terminal font selection
- Terminal applies theme/font on initialization and live update
@Peter-L-SVK

Copy link
Copy Markdown
Owner Author
Screenshot from 2026-06-22 16-45-17

Showcase of new Terminal setting tab

… module

Extract TERMINAL_THEMES into its own module (terminal_themes.py) and
move ResultsPanel to panels.py under src/ui/results/ package.

- Add # fmt: off/on block to preserve multiline palette formatting
- Re-export TERMINAL_THEMES as ResultsPanel class attribute for backward
  compatibility
Preferences "Warn when closing with unsaved changes" checkbox was
saved but never read — the close dialog always appeared regardless
of the setting. Now _on_close_request reads the setting and silently
discards unsaved tabs when the warning is disabled.
- Add file_path attribute to EditorTab, set on save/open
- Save list of tab file paths to settings on window close
- On startup, if "Restore last session" enabled, reopen saved files
- Remove obsolete _current_file, replace with per-tab file_path
…nalysis

Add ForecastingHook (GPLv3+) providing data scientist tools
operating on Polars DataFrames for zero-copy integration:

  • trend_forecast:      linear regression — slope, intercept, R^2
  • moving_average:      rolling window smoothing
  • exponential_forecast: ETS exponential smoothing
  • percentile_bands:    P10/P25/P50/P75/P90/IQR distribution
  • anomaly_detect:      IQR and z-score outlier detection

Add right-click context menu on browser tree tables/views
with all five forecasting functions, running async and
displaying formatted results directly in the Results panel.

Fix Hook Manager _show_result to detect and display
forecasting output (percentile bands, trend info, anomaly
counts) alongside the existing recommendation format.
Both the Hook Manager (execute_sync) and browser right-click
forecasting now skip surrogate key columns (id, serial, *_id)
and prefer numeric/real/double precision types over integers
when auto-discovering which column to analyze.

Trend forecast now uses a synthetic row index as the x-axis
when only one column is available, eliminating the y=x tautology
that produced slope=1.0, R²=1.0 on all inputs.

Forecasting is still under construction — "Analyze Last Query
Result" integration (respecting WHERE filters from the editor)
will be added in a follow-up commit.
@Peter-L-SVK Peter-L-SVK changed the title test v0.9.0 <WIP> Jun 28, 2026
…aping, column detection

- Fix phantom grab / "Broken accounting" warnings: claim event sequence
  after popup() and drop stale _active_popover references without
  calling unparent(), which corrupts GTK4's widget tree.
- Fix IndexError in psycopg2 parameter parser: double-escape percent
  in NOT LIKE '%%\\_id' so the LIKE pattern reaches PostgreSQL as
  '%\_id' instead of being misinterpreted as a parameter placeholder.
- Fix left-click gesture firing on right-clicks: set_button(1) on
  the click gesture controller.
- Fix popover not dismissable by clicking outside: set_autohide(True).
- Fix lambda late-binding across GLib.idle_add boundary: store
  schema/table/func_id as plain Python attributes on each button
  widget instead of relying on nested-lambda closure capture.
- Fix surrogate-key column selection: skip id/serial/*_id columns and
  prefer real/numeric types over plain integers via CASE data_type
  ordering.
- Defensive fetchall: guard against malformed (None / zero-width)
  cursor rows before indexing r[0].
Add "📊 Correlation Matrix" as a new right-click option (below a
separator from the single-column forecast functions).  Unlike the
existing hooks that pick the "best" numeric column, this fetches ALL
numeric columns (excluding id/serial/*_id), builds a multi-column
Polars DataFrame, computes the Pearson correlation via .corr(), and
pretty-prints a grid with column headers and 4-decimal values.

Changes in browser.py:
- _on_right_click: add separator + "📊 Correlation Matrix" button
- do_analysis: add func_id == "correlation_matrix" branch —
  queries all numeric columns, fetches data with null handling
- on_loaded: add func_id == "correlation_matrix" branch —
  corr_df.corr(), pretty-print grid, early return before
  single-column code path
- Type-annotate vals: list[float | None] to satisfy mypy
@Peter-L-SVK Peter-L-SVK changed the title v0.9.0 <WIP> feat: v0.9.0 — Multi-tab editor, schema designer pro, analytics engine, and SSH tunnel Jun 29, 2026
…ce-all

When whole-word matching was enabled (toggle active), the highlight
pass correctly used \b<word>\b regex but the navigation pass used
GTK's forward_search/backward_search which only filtered by case
and text-only, ignoring word boundaries entirely.  This caused the
cursor to jump to partial matches (e.g. "ordering" when searching
"order") while only the substring was highlighted, creating visual
inconsistency.

Fix:
- _search_text: when whole-word is active, use Python re.search()
  with \b boundaries instead of GTK's forward_search/backward_search,
  maintaining wrap-around behavior.
- _on_replace_all: wrap pattern with \b boundaries when whole-word
  is active so replacement also respects the toggle.
Reformat with black (default config) — no logic changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant