Skip to content

feat(linux): add result grid context menu with Copy as and Export Results dialog - #1

Open
Seha16 wants to merge 5 commits into
linuxfrom
feat/export-results-dialog
Open

feat(linux): add result grid context menu with Copy as and Export Results dialog#1
Seha16 wants to merge 5 commits into
linuxfrom
feat/export-results-dialog

Conversation

@Seha16

@Seha16 Seha16 commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • Right-click menu on every result grid, including query results in the SQL editor, which had no menu before: Copy, Copy as (Rows, With Headers, JSON, CSV, CSV with Headers, Markdown, IN Clause, INSERT Statement), Copy column name, Show Row as JSON, Set Value (Empty, NULL), Export Results..., Insert row, Duplicate, Delete. Editable-only items are hidden on read-only grids.
  • Export Results dialog (adw::Dialog) with a format picker (CSV, JSON) and CSV options that match the macOS app: convert NULL to empty, convert line breaks to spaces, field names in first row, sanitize formula-like values, delimiter, quote mode, line break, decimal separator. Reset to Defaults. Options persist in preferences.json.
  • Pure renderers live in tablepro_core::export with unit tests. The old render_csv in the app went through the grid's display formatter, so it truncated text at 10k chars and wrote the NULL sentinel into files; the new path renders full values.
  • serde_json gets preserve_order so JSON output keeps column order.

Deferred to follow-up PRs: Paste rows, Set Value > NOW() / CURRENT_TIMESTAMP (needs a raw SQL expression marker in the change tracker), SQL / Markdown / HTML / XML / XLSX file export.

Test plan

  • ./scripts/ci-local.sh (fmt, clippy -D warnings, build, 338 unit tests; 21 new for the renderers)
  • SQLite smoke DB with a comma, a line break, a " and a =SUM(A1) value. Right-clicking on the editor result grid shows the read-only menu; on a table tab, it shows the full menu.
  • Copy as Markdown / JSON / IN Clause / CSV with Headers and plain Copy read back from the Wayland clipboard with the expected text.
  • Export dialog: switches and dropdowns write preferences.json on change, Reset to Defaults restores defaults, Export writes the file and shows the toast. Output with semicolon + line breaks to spaces: 2;has,comma;'-2.25;"line1 line2";false and 3;'=SUM(A1);10;"say ""hi""";false.
  • Set Value > Empty marks the cell as modified and shows the unsaved-changes bar.
  • Show Row as JSON opens a read-only dialog with column order preserved.
  • Paginator export button opens the same dialog.
  • Dark theme

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

There are confirmed correctness and performance issues in the new export and grid-edit flows that should be addressed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR adds a Linux result-grid context menu and a new Export Results dialog, and moves export rendering into tablepro_core so exports are not truncated by grid display formatting.

Changes:

  • Added shared result-grid context menus across browse tables and SQL editor results, including Copy variants, row JSON, and Export Results.
  • Implemented an Export Results dialog (CSV and JSON) with persisted CSV options in preferences.json.
  • Introduced pure export renderers in linux/crates/core (CSV, TSV, JSON, Markdown, IN clause) with unit tests, and enabled ordered JSON output via serde_json preserve_order.
File summaries
File Description
linux/ROADMAP.md Updates roadmap to mark context menu and export items as complete and list follow-ups.
linux/po/POTFILES.in Adds the new export dialog source file to translation inputs.
linux/crates/core/src/lib.rs Exposes the new export module publicly.
linux/crates/core/src/export.rs Adds core export renderers and unit tests for CSV, TSV, JSON, Markdown, and IN-clause output.
linux/crates/app/src/ui/preferences.rs Plumbs csv_export into preferences saving from the Preferences dialog.
linux/crates/app/src/ui/mod.rs Registers the new export_dialog UI module.
linux/crates/app/src/ui/grid.rs Implements shared grid context menus and adds actions for Copy-as variants, row JSON, set value, and export.
linux/crates/app/src/ui/export_dialog.rs Adds the Export Results dialog UI and file-save flow.
linux/crates/app/src/ui/editor.rs Enables grid context-menu routing from SQL editor result grids via a GridMsg channel.
linux/crates/app/src/ui/browse_tab.rs Wires new grid messages (set value, export) and adds paginator export button behavior.
linux/crates/app/src/ui/app/workspace_tabs.rs Forwards editor and browse export/copy outputs into app-level messages.
linux/crates/app/src/ui/app/mod.rs Replaces old export actions with a unified ExportResults message and presents the new dialog.
linux/crates/app/src/ui/app/browse.rs Removes the old export implementation that built bytes directly in the app layer.
linux/crates/app/src/services/preferences.rs Adds csv_export to the persisted Preferences model with defaults.
linux/Cargo.toml Enables serde_json preserve_order to keep JSON column order stable.
linux/Cargo.lock Updates lockfile for serde_json feature changes and new dependencies.
Review details
  • Files reviewed: 15/16 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +2150 to +2154
if let Some(row_obj) = self.row_object_at(row_position)
&& let Some(draft_id) = row_obj.draft_id()
{
crate::services::change_tracker::with_tab(self.tab_id, |t| {
t.track_draft_cell_edit(draft_id, col_index, value.clone());
Comment on lines 164 to 178
let save_all: std::rc::Rc<dyn Fn()> = {
let page_size = page_size_row.clone();
let confirm = confirm_row.clone();
let font = font_size_row.clone();
let retention = retention_row.clone();
let timeout = timeout_row.clone();
std::rc::Rc::new(move || {
preferences::save(&Preferences {
default_page_size: page_size.value() as u64,
confirm_destructive: confirm.is_active(),
editor_font_size: font.value() as u32,
history_retention_days: retention.value() as u32,
query_timeout_secs: timeout.value() as u32,
csv_export: preferences::load().csv_export,
});
Comment on lines +188 to +192
if opts.header_row {
let header: Vec<String> = columns.iter().map(|c| escape_field(&c.name, opts, false)).collect();
out.push_str(&header.join(delim));
out.push_str(line_break);
}
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.

3 participants