Skip to content

feat(inspector): switch the CSV first row between header and data#1942

Merged
datlechin merged 2 commits into
mainfrom
feat/1469-csv-header-toggle
Jul 22, 2026
Merged

feat(inspector): switch the CSV first row between header and data#1942
datlechin merged 2 commits into
mainfrom
feat/1469-csv-header-toggle

Conversation

@datlechin

Copy link
Copy Markdown
Member

Part 4 of 4 toward #1469 (CSV structural editing). The header/data toggle. Stacked on #1941 (base branch feat/1469-csv-split-merge).

What

  • Edit > Switch First Row Between Header/Data (Cmd+Shift+H) flips whether the first row is a header. Turning the header off moves it into the data and names columns Column 1, Column 2, ...; turning it back on promotes the first data row to the header. Undoable.
  • A file that has no header row is no longer saved with a generated Column 1,Column 2,... header line. This also fixes byte round-trip for headerless files.

Why

Auto-detection guesses whether row one is a header. When it guesses wrong, "Delete Column" and every other structural verb operate on the wrong interpretation, and (before this) saving a headerless file silently added a synthetic header. #1469 calls this out: without a header toggle, delete-column semantics are ambiguous.

How (native / HIG)

  • CSVRowStore gains an explicit hasHeaderRow flag (set from the same detection used at open) and toggleHeaderRow(), which moves the header row in or out of the data set and reindexes column names.
  • CSVWriter now emits the header row only when hasHeaderRow is true, so the on-disk file matches the current header state and headerless files round-trip byte-for-byte.
  • Toggle is one undo step through the same snapshot mechanism split/merge use (state captured before and after; hasHeaderRow is part of the snapshot). Inferred types are recomputed.
  • Cmd+Shift+H is free in TablePro and not a macOS reserved shortcut (Finder uses it for Go > Home, which is app-scoped). The Edit-menu item is gated on the CSV window being frontmost.
  • toggleHeaderRow() is added to InspectorDocument with a default no-op (additive PluginKit change, no version bump).

Tests

  • CSVRowStore.toggleHeaderRow: header to data, data to header, and a two-toggle round-trip.
  • CSVWriter: a headerless file writes with no synthetic header row (byte-for-byte round-trip).

Docs / changelog

  • docs/features/csv-inspector.mdx new Header row section; docs/features/keyboard-shortcuts.mdx adds Cmd+Shift+H.
  • CHANGELOG entry under [Unreleased].

Needs a local Xcode build to confirm compilation. PluginKit change is additive; no version bump.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ba0d32fd69

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let firstCells = cells(forRow: 0)
logicalRows.removeFirst()
columnNames = firstCells
headerRef = .materialized(firstCells)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep promoted header cells in sync with column edits

After promoting a headerless CSV and then adding or removing a column without toggling the header back off, this stores the promoted header as a fixed headerRef while insertColumn/removeColumn update only columnNames and data rows. CSVWriter subsequently writes that stale header source, so, for example, promoting 1,2\n3,4\n, adding column c, and saving produces a two-cell header followed by three-cell data rows; the new column name is lost and the saved CSV has mismatched widths.

Useful? React with 👍 / 👎.

@datlechin

Copy link
Copy Markdown
Member Author

Addressed a code review. Findings and fixes:

  • Header demote correctness: demoting read a possibly-stale .materialized header, so after an intervening column insert/remove the demoted row had the wrong width (a malformed CSV on save) or resurrected a deleted column's value. Demote now uses columnNames, which is always width-correct and value-synced. Added a promote → insert-column → demote regression test.
  • No-op guard: toggleHeaderRow on an empty file marked the document dirty and pushed a dead undo entry. Guarded it.
  • Shortcut system: Cmd+Shift+H was hardcoded outside the app's ShortcutAction/KeyboardSettings system (not customizable, not conflict-checked). Added a toggleHeaderRow action with the Cmd+Shift+H default and bound the menu item through shortcut(for:).

@datlechin
datlechin force-pushed the feat/1469-csv-split-merge branch from 8faf1f6 to 9dd6e93 Compare July 22, 2026 03:53
@datlechin
datlechin force-pushed the feat/1469-csv-header-toggle branch from ba0d32f to ef9a509 Compare July 22, 2026 03:53
@datlechin
datlechin force-pushed the feat/1469-csv-split-merge branch from 9dd6e93 to d22b41d Compare July 22, 2026 03:56
@datlechin
datlechin force-pushed the feat/1469-csv-header-toggle branch from ef9a509 to 216a906 Compare July 22, 2026 03:56

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 216a9068ef

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +220 to +221
@objc func inspectorToggleHeaderRow(_ sender: Any?) {
inspectorDocument?.toggleHeaderRow()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clear row selection when toggling the header

When a row is selected, this action changes the backing row indices but leaves state.selectedRowIndices intact. For example, with an unfiltered CSV, selecting the first data row and demoting the header inserts a new row at index 0; invoking Delete Selected Rows then deletes the former header row rather than the row that was selected before the toggle. Clear or rebase the selection before refreshing the grid.

Useful? React with 👍 / 👎.

@datlechin
datlechin force-pushed the feat/1469-csv-header-toggle branch from 216a906 to e3eb043 Compare July 22, 2026 06:52
Base automatically changed from feat/1469-csv-split-merge to main July 22, 2026 06:59
@mintlify

mintlify Bot commented Jul 22, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jul 22, 2026, 7:01 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@datlechin
datlechin merged commit fa398e6 into main Jul 22, 2026
2 checks passed
@datlechin
datlechin deleted the feat/1469-csv-header-toggle branch July 22, 2026 07:01
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