Skip to content

feat(json): JSON/JSONB editor, viewer & in-table affordances#181

Merged
debba merged 15 commits into
TabularisDB:mainfrom
NewtTheWolf:feat/json-jsonb-editor-viewer
May 17, 2026
Merged

feat(json): JSON/JSONB editor, viewer & in-table affordances#181
debba merged 15 commits into
TabularisDB:mainfrom
NewtTheWolf:feat/json-jsonb-editor-viewer

Conversation

@NewtTheWolf
Copy link
Copy Markdown
Contributor

@NewtTheWolf NewtTheWolf commented May 11, 2026

Closes #24.

Adds JSON/JSONB handling to the data grid: per-cell affordances, inline expansion editor, a native Tauri window for full-screen viewing/editing, and a per-connection toggle for detecting JSON inside plain text columns. PostgreSQL driver now binds structured JSON values directly on INSERT and UPDATE.

Notable bits

  • Viewer is a separate Tauri window, not a modal. Multiple cells can have viewers open at the same time. Session state lives in a Rust Mutex<HashMap> keyed by ULID; save flows back to the grid via the json-viewer:saved Tauri event.
  • detect_json_in_text_columns is per-connection, not global. Toggle in the connection edit modal. Same flag gates native array detection (text[], int[], Firestore arrays).
  • PG driver binds serde_json::Value directly for json / jsonb columns via tokio-postgres' with-serde_json-1 impl. insert_record fetches column types once and threads them through bind_pg_value.

Files of interest for review

  • src-tauri/src/json_viewer.rs — new module, 3 commands, ULID sessions
  • src/pages/JsonViewerPage.tsx — standalone window page
  • src/components/ui/DataGrid.tsx — Tauri event listener + window-open paths
  • src-tauri/src/drivers/postgres/binding.rs + mod.rs — json/jsonb binding

Test plan

  • jsonb column: chevron expansion, braces opens viewer, double-click opens viewer in edit mode
  • Viewer save round-trips through pending changes; Submit commits cleanly
  • Two viewer windows open at once on different cells, both save independently
  • PG INSERT and UPDATE of object/array values into json / jsonb succeed
  • Per-connection JSON-detect toggle: off → TEXT/array cells render plain; on → JSON-cell affordance
  • NULL cells show no icons; readonly grid → viewer shows Close-only
  • Inline chevron expansion switches content correctly when toggled between cells
  • i18n strings render in all locales
  • pnpm tsc --noEmit, pnpm vitest run, cargo test green

Design refs: DBeaver Value Panel, DataGrip Value Editor.

@NewtTheWolf NewtTheWolf changed the title feat(i18n): add JSON viewer keys and extend jsonInput section feat(json): JSON/JSONB editor, viewer & in-table affordances (#24) May 11, 2026
@NewtTheWolf
Copy link
Copy Markdown
Contributor Author

NewtTheWolf commented May 11, 2026

image image image image

@NewtTheWolf NewtTheWolf changed the title feat(json): JSON/JSONB editor, viewer & in-table affordances (#24) feat(json): JSON/JSONB editor, viewer & in-table affordances May 11, 2026
@NewtTheWolf
Copy link
Copy Markdown
Contributor Author

@NewtTheWolf NewtTheWolf force-pushed the feat/json-jsonb-editor-viewer branch 3 times, most recently from d0ace89 to 7792388 Compare May 16, 2026 07:09
@NewtTheWolf NewtTheWolf marked this pull request as ready for review May 16, 2026 08:17
Move detection from a global toggle to a connection-level setting so users can enable JSON cell affordances only on connections where text columns actually contain JSON.
…ditor

- JsonCell renders detected JSON with syntax highlighting and a Braces affordance for opening the viewer
- JsonExpansionEditor provides an inline Monaco editor for cell-level edits without leaving the grid
- isJsonContent heuristic + jsonHighlight tokenizer + formatCellValue updates round out the rendering path
- Refactor JsonInput to host three swappable editing modes: Monaco code editor, json-edit-react tree view, and raw text fallback
- Add JsonCodeEditor + JsonTreeView wrappers, leaf-only tree editing, fillHeight layout mode
- Pull in json-edit-react dependency
@NewtTheWolf NewtTheWolf force-pushed the feat/json-jsonb-editor-viewer branch from 3e3f0fa to 593eb58 Compare May 16, 2026 08:20
- Bind serde_json::Value directly for json/jsonb columns instead of text-with-cast, fixing object/array round-trips
- JSON-encode scalar values for json/jsonb cell display so quoted strings, numbers and bools render correctly
…cell dedup

- Add json_viewer Rust module: session store, ULID session IDs, WebviewWindowBuilder, cell-key dedup, last-bounds memory
- Replace JsonViewerModal with a standalone Tauri window (JsonViewerPage + /json-viewer route)
- Grant window close/focus/unminimize capabilities for the new window label pattern
- Support opening the viewer for free-form SELECT results (no PK); falls back to read-only when no save-back path exists
- DataGrid + JsonInput now invoke open_json_viewer_window with a derived cell key so reopens focus instead of duplicating
- i18n: dataGrid.newRow added; jsonViewer keys updated across en/de/es/fr/it/zh
Satisfies react-hooks/set-state-in-effect — the empty sessionId case is a derived value from the URL, so resolve it inline instead of setState'ing inside useEffect.
editorTheme.styles?.container is typed as string | object | undefined by
json-edit-react, so the spread tripped TS2698 even though buildTheme always
assigns an object. Cast to Record<string, unknown> at the spread site.
- Add missing detect_json_in_text_columns field to SavedConnection literal in export_import_tests.rs (introduced by this branch, broke the test added on main)
- Delete tests/components/modals/JsonViewerModal.test.tsx — the modal it tests was removed when the viewer became a native Tauri window
@NewtTheWolf NewtTheWolf force-pushed the feat/json-jsonb-editor-viewer branch from 593eb58 to 01b5568 Compare May 16, 2026 08:27
- README + 4 localized READMEs: mention syntax-highlighted JSON/JSONB cells, dedicated editor window, and the per-connection "Detect JSON in text columns" opt-in
- PLUGIN_GUIDE: callout under get_columns explaining that data_type "JSON"/"JSONB" (case-insensitive) enables the viewer, and that execute_query row values may be native JSON or JSON-formatted strings
JSON cells now light up like other modified cells (object-aware
equality check fixed the false-negative). Pending edits get a stronger
blue background + left accent border so they're visible against the
token colors.

Expansion editor opens with a Monaco inline diff by default and a
toggle to fall back to the plain code editor. JSON viewer window and
JsonInput toolbar get the same Diff toggle (off by default in the
window). Tooltip now serialises objects via formatCellValue so JSON
cells stop showing "[object Object]".

Backend session carries the original value through so the diff has a
baseline to compare against.
Adds a dedicated json_demo table to both seeded databases with the
common JSON shapes (flat object, nested, arrays, deeply nested, mixed
types, empty values, unicode/emoji, plus a 40-item list on postgres).
Includes a text column carrying JSON strings so the
"detect JSON in text columns" opt-in can be exercised from the same
table.
@debba
Copy link
Copy Markdown
Collaborator

debba commented May 17, 2026

Tested it.
Looks wonderful, can we enable it in sidebar too?

@NewtTheWolf
Copy link
Copy Markdown
Contributor Author

done

RowEditorSidebar now receives the original (un-merged) row data and
the per-connection detect_json_in_text_columns flag from the grid, and
forwards both through FieldEditor to JsonInput. With a baseline value
in scope, the sidebar's JSON field shows the same Diff toggle as the
expansion / viewer-window paths. Text columns with JSON content are
recognised as JSON fields when the opt-in is on.
The non-fillHeight branch rendered the json-edit-react editor without a
height or overflow constraint, so deeply nested objects with long URL
strings expanded the tree past the sidebar's bounds and overlapped the
toolbar / following fields. Wrapped it in a max-h scrollable container
matching the fillHeight branch, with overflow-wrap:anywhere so long
strings wrap instead of pushing horizontal scroll.
@debba debba merged commit bec3cb3 into TabularisDB:main May 17, 2026
1 check passed
NewtTheWolf added a commit to NewtTheWolf/tabularis that referenced this pull request May 17, 2026
Mirrors the JSON cell affordance from PR TabularisDB#181 for plain string columns
whose value is long enough to be cumbersome in the inline grid cell.
Closes TabularisDB#207.

- isLongTextCellTarget — text-like column + value > 80 chars or contains
  a newline. JSON / BLOB / geometry / date paths stay untouched.
- TextCell renders a single-line preview with a chevron (multi-line
  values get a ⏎ substitution). No separate viewer window — chevron
  is the only entry point per design discussion.
- TextExpansionEditor opens inline below the row: Monaco in plaintext
  mode with a manual Diff toggle (default off) against the original.
- FieldEditor swaps the plain textarea for Monaco-based TextInput when
  the sidebar value is long, so the diff toggle is available there too.
- Width-aware Side-by-side toggle (Columns2) appears next to the Diff
  button whenever the editor's container is ≥ 480px. Below that the
  unified diff is the only mode, so the button doesn't promise a
  layout that won't fit. Same gating applied uniformly to JSON.
- Json/Text Monaco wrappers are unified into a single CellCodeEditor /
  CellDiffEditor pair that takes a language prop, replacing the four
  per-language wrappers.
debba added a commit that referenced this pull request May 21, 2026
* Add password visibility toggles to inputs

* feat: Add connection export and import functionality

This commit introduces the ability to export and import connection
configurations.

The `export_connections_payload` command serializes all connection
groups, saved connections, and SSH connections into a JSON payload. It
also resolves and includes passwords from the keychain for both database
and SSH connections.

The `import_connections_payload` command takes an `ExportPayload`,
merges it with existing configurations, and securely stores any
passwords in the system's keychain. It then saves the updated
configurations to disk.

Additionally, this commit includes:
- A new `ExportPayload` model.
- A unit test for serialization and deserialization of the
  `ExportPayload`.
- UI updates in `Connections.tsx` to add export and import buttons.
- Localization updates for export/import related strings.
- Styling adjustments for `ConfirmModal` to support different variants.

* fix(notebook): render db selector dropdown via portal to avoid clipping

The cell wrapper has overflow-hidden, which clipped the absolutely
positioned database selector dropdown when many databases existed.
Render the dropdown through a portal with fixed positioning computed
from the trigger button so the full list (with its 230px scroll area)
is reachable regardless of cell height.

* feat(sidebar): add Discord community callout and update links

* chore: switch Discord invite URLs to discord.com/invite/K2hmhfHRSt

* feat(connections): add import button to empty state
bump tabularis in src-tauri/Cargo.lock to 0.10.2

* feat(plugins): add firestore plugin to registry

* docs: fix Discord shields.io badge server id

* feat: database trigger management (PostgreSQL, MySQL, SQLite)

* chore(plugins): bump firestore plugin to v0.1.0 (stable, Codeberg-hosted)

- Version 0.1.0-beta.1 -> 0.1.0 (semver-strict; the registry schema
  rejects pre-release suffixes). The plugin's manifest description
  carries the "still effectively a beta" wording in the UI.
- Asset URLs moved from github.com to codeberg.org — Codeberg is the
  release host now; GitHub Actions only runs the cross-platform build
  and pushes the zips back to Codeberg via the Forgejo API.
- linux-arm64 added (openssl-sys cross-compile blocker fixed by
  switching the firestore crate to tls-webpki-roots / rustls).
- Author URL switched to the Codeberg profile to match.

End-to-end install tested against a local custom_registry_url
serving this registry.json — Tabularis pulled the linux-x64 zip
from Codeberg and activated the plugin successfully.

* style(changelog): wrap long version links

* 0.10.3

* fix(export): expand SSH params and refactor into testable utilities

The export command resolved connection params without first expanding
the SSH connection record, so exports failed with "Missing SSH Host"
on SSH-tunneled MySQL/Postgres/SQLite connections. Mirror the pattern
used by every other Tauri command (expand_ssh_connection_params then
resolve_connection_params_with_id).

Also:
- emit a final export_progress event so the UI counter reflects exports
  of fewer than 100 rows
- split the macro-based row writer into per-driver streamers
  (drivers/{mysql,postgres,sqlite}/export.rs) and pure helpers
  (export/{format,progress,sink}.rs)
- delegate JSON array framing to serde_json::ser::CompactFormatter
- add 26 unit tests covering format parsing, CSV/JSON sinks, and the
  progress emitter

Closes #184

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* style(editor): polish Export dropdown trigger and menu

- add ChevronDown indicator with rotation when open
- highlight trigger on hover with the project's blue-500/15 convention
- keep highlighted state while the menu is open
- skip hover styles via enabled:hover: when the button is disabled
- give menu items file-format icons and accent hover

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(utils): add newConsole helper, tests, and wire to sidebar

* feat(tabularis-discord-release): add tabularis-discord-release skill and
template

* chore(discord-release): add link buttons and @everyone mention to template

Adds a top-level components row with four link buttons (Release post,
GitHub Release, Changelog, Website) so the Discord announcement no
longer relies on the embed's Links field, which has been removed.
Prefixes the content line with @everyone so the release announcement
pings the server. SKILL.md guard against components is narrowed to
only forbid reactions and polls; link buttons must be preserved
through substitution.

* fix(editor): restore correct SQL string color across all themes

* feat(editor): make Enter accept autocomplete suggestion configurable

Closes #186.

Adds an opt-in setting (Appearance → Editor → "Accept Suggestion with Enter")
that lets Enter accept the active autocomplete suggestion alongside Tab,
mapped to Monaco's "smart" mode so it only triggers when the suggestion
actually changes the text. Default stays off to preserve current behaviour.

* feat(editor)!: enable Enter-accepts-suggestion by default

BREAKING CHANGE: Pressing Enter while an autocomplete suggestion is
highlighted now accepts it instead of inserting a newline. Users who
preferred the previous behaviour can turn it off under
Appearance -> Editor -> "Accept Suggestion with Enter".

This matches what most code editors do out of the box and is the
behaviour users expect (issue #186).

* chore: update roadmap [skip ci]

* chore: update roadmap [skip ci]

* chore: update roadmap [skip ci]

* chore: update roadmap [skip ci]

* chore: update roadmap [skip ci]

* chore: update roadmap [skip ci]

* chore: update roadmap [skip ci]

* feat(editor): foreign key click-to-navigate in result grid

* fix(drivers): share a single connection across multi-statement scripts

Closes #199

The editor's "run all" path dispatched every statement through its own
parallel invoke("execute_query"), with each invoke acquiring a fresh
connection from the driver pool. Statements that depended on
connection-local session state — SET @var, LAST_INSERT_ID() / LASTVAL(),
BEGIN/COMMIT transactions, TEMPORARY TABLE, PREPARE/EXECUTE — silently
failed because the state lived on a connection nothing else in the batch
ever saw.

This change introduces an execute_batch method on DatabaseDriver, a
matching execute_query_batch Tauri command, and rewires the frontend
runMultipleQueries to make one round-trip per script. Built-in drivers
(MySQL/Postgres/SQLite) override execute_batch to acquire one pooled
connection and run every statement on it in order; plugin drivers fall
back to a default implementation that sequentially calls execute_query
(no session-state continuity but ordering is preserved).

MySQL additionally routes transaction-control statements
(BEGIN / COMMIT / ROLLBACK / SAVEPOINT / LOCK TABLES) through
sqlx::raw_sql() so they bypass the prepared-statement protocol that
rejects them (server error 1295).

Side fix included: the three built-in drivers were hardcoding
affected_rows: 0 for every statement, masking silent failures with a
plausible-looking "0 rows affected". Non-result-set statements
(INSERT/UPDATE/DELETE/DDL) now go through the execute() path and
return the real count.

Integration tests (#[ignore]'d, require local MySQL/Postgres):
- @var + LAST_INSERT_ID() chain (Issue's exact repro)
- BEGIN/COMMIT atomicity (text-protocol routing)
- TEMP TABLE visibility inside a Postgres transaction
- INSERT/UPDATE/DELETE affected_rows reporting (MySQL + Postgres)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(i18n): add export/import translations and export warning in locales

* docs(gitnexus-cli): add --drop-embeddings option and clarify post-hook
behavior

* fix(commands): preserve all in-flight abort handles per connection

`QueryCancellationState` used a `HashMap<String, AbortHandle>`, so the
second `execute_query`/`execute_query_batch`/`explain_query_plan` call on
the same `connection_id` orphaned the first task's abort handle. The
earlier Tokio task kept running and `cancel_query` could only abort the
most recently registered one.

Store handles per slot as `Vec<Arc<AbortHandle>>`:

- `register_abort_handle` pushes the new handle and prunes finished ones.
- `unregister_abort_handle` removes the specific handle by `Arc` identity
  so a task's own cleanup never targets a sibling.
- `cancel_query` drains the slot and aborts every handle.

Adds unit tests for the helper invariants and an integration test that
runs two `SELECT SLEEP(5)` against the same connection and verifies a
single cancel aborts both.

Closes #201

* fix: incorrect get_app_config_dir when running in mpc mode in windows

* feat(i18n): add Japanese (ja) translation

* feat(json): per-connection "detect JSON in text columns" setting

Move detection from a global toggle to a connection-level setting so users can enable JSON cell affordances only on connections where text columns actually contain JSON.

* feat(json): in-cell JSON highlighting (JsonCell) + inline expansion editor

- JsonCell renders detected JSON with syntax highlighting and a Braces affordance for opening the viewer
- JsonExpansionEditor provides an inline Monaco editor for cell-level edits without leaving the grid
- isJsonContent heuristic + jsonHighlight tokenizer + formatCellValue updates round out the rendering path

* feat(json): multi-mode JsonInput (Code / Tree / Raw editors)

- Refactor JsonInput to host three swappable editing modes: Monaco code editor, json-edit-react tree view, and raw text fallback
- Add JsonCodeEditor + JsonTreeView wrappers, leaf-only tree editing, fillHeight layout mode
- Pull in json-edit-react dependency

* fix(postgres): bind JSON/JSONB columns natively + JSON-encode scalars

- Bind serde_json::Value directly for json/jsonb columns instead of text-with-cast, fixing object/array round-trips
- JSON-encode scalar values for json/jsonb cell display so quoted strings, numbers and bools render correctly

* feat(json): native Tauri JSON viewer window with bounds memory + per-cell dedup

- Add json_viewer Rust module: session store, ULID session IDs, WebviewWindowBuilder, cell-key dedup, last-bounds memory
- Replace JsonViewerModal with a standalone Tauri window (JsonViewerPage + /json-viewer route)
- Grant window close/focus/unminimize capabilities for the new window label pattern
- Support opening the viewer for free-form SELECT results (no PK); falls back to read-only when no save-back path exists
- DataGrid + JsonInput now invoke open_json_viewer_window with a derived cell key so reopens focus instead of duplicating
- i18n: dataGrid.newRow added; jsonViewer keys updated across en/de/es/fr/it/zh

* fix(json): compute missing-session-id error during render, not in effect

Satisfies react-hooks/set-state-in-effect — the empty sessionId case is a derived value from the URL, so resolve it inline instead of setState'ing inside useEffect.

* fix(json): cast JsonTreeView container style spread to satisfy tsc -b

editorTheme.styles?.container is typed as string | object | undefined by
json-edit-react, so the spread tripped TS2698 even though buildTheme always
assigns an object. Cast to Record<string, unknown> at the spread site.

* fix(test): repair test suite after main merge

- Add missing detect_json_in_text_columns field to SavedConnection literal in export_import_tests.rs (introduced by this branch, broke the test added on main)
- Delete tests/components/modals/JsonViewerModal.test.tsx — the modal it tests was removed when the viewer became a native Tauri window

* docs(json): note JSON/JSONB cell viewer + plugin contract

- README + 4 localized READMEs: mention syntax-highlighted JSON/JSONB cells, dedicated editor window, and the per-connection "Detect JSON in text columns" opt-in
- PLUGIN_GUIDE: callout under get_columns explaining that data_type "JSON"/"JSONB" (case-insensitive) enables the viewer, and that execute_query row values may be native JSON or JSON-formatted strings

* feat(demo): add Docker Compose demo with seeded databases

Includes MySQL, PostgreSQL, MSSQL init scripts, README

* feat(branding): add logo SVG files to public

* refactor(cancellation): unify query and explain slots by connection id

extract cancel_query_impl and add tests for multi-handle abort

* fix(export,dump): apply same per-slot abort-handle fix to export/dump/import

`ExportCancellationState` and `DumpCancellationState` had the same
overwrite bug as `QueryCancellationState`: a second concurrent
export/dump/import on the same connection_id orphaned the first abort
handle, so `cancel_*` could only stop the most recent task.

Switch both to `HashMap<String, Vec<Arc<AbortHandle>>>` and reuse the
`register_abort_handle` / `unregister_abort_handle` helpers from
`commands.rs` at all three register sites (`export_query_to_file`,
`dump_database`, `import_database`). `cancel_export`, `cancel_dump` and
`cancel_import` now drain the slot and abort every handle.

Refs #201

* refactor(cancellation): introduce AbortHandleMap alias and import_slot_key helper

Polish from a review pass:

- Add a `pub(crate) type AbortHandleMap = HashMap<String, Vec<Arc<AbortHandle>>>`
  alias in `commands.rs` and use it in the three cancellation state structs and
  the helper signatures. Removes repeated 5-level generic types from the
  signatures and lets `export.rs` / `dump_commands.rs` drop their direct
  `tokio::task::AbortHandle` and `std::collections::HashMap` imports.
- Extract `import_slot_key(&str) -> String` in `dump_commands.rs` so the
  `"{}_import"` key composition lives in one place instead of being inlined
  at both `cancel_import` and `import_database`.

No behavior change. All 8 cancellation_state unit tests still pass.

* feat(json): inline diff for pending changes + fix JSON cell highlight

JSON cells now light up like other modified cells (object-aware
equality check fixed the false-negative). Pending edits get a stronger
blue background + left accent border so they're visible against the
token colors.

Expansion editor opens with a Monaco inline diff by default and a
toggle to fall back to the plain code editor. JSON viewer window and
JsonInput toolbar get the same Diff toggle (off by default in the
window). Tooltip now serialises objects via formatCellValue so JSON
cells stop showing "[object Object]".

Backend session carries the original value through so the diff has a
baseline to compare against.

* feat(demo): seed json_demo table in postgres + mysql init

Adds a dedicated json_demo table to both seeded databases with the
common JSON shapes (flat object, nested, arrays, deeply nested, mixed
types, empty values, unicode/emoji, plus a 40-item list on postgres).
Includes a text column carrying JSON strings so the
"detect JSON in text columns" opt-in can be exercised from the same
table.

* feat(json): sidebar gets diff toggle + detect-JSON-in-text support

RowEditorSidebar now receives the original (un-merged) row data and
the per-connection detect_json_in_text_columns flag from the grid, and
forwards both through FieldEditor to JsonInput. With a baseline value
in scope, the sidebar's JSON field shows the same Diff toggle as the
expansion / viewer-window paths. Text columns with JSON content are
recognised as JSON fields when the opt-in is on.

* fix(json): bound the sidebar tree view so long strings stop overlapping

The non-fillHeight branch rendered the json-edit-react editor without a
height or overflow constraint, so deeply nested objects with long URL
strings expanded the tree past the sidebar's bounds and overlapped the
toolbar / following fields. Wrapped it in a max-h scrollable container
matching the fillHeight branch, with overflow-wrap:anywhere so long
strings wrap instead of pushing horizontal scroll.

* chore: update roadmap [skip ci]

* feat(text): chevron expand + Monaco diff for long text/longtext cells

Mirrors the JSON cell affordance from PR #181 for plain string columns
whose value is long enough to be cumbersome in the inline grid cell.
Closes #207.

- isLongTextCellTarget — text-like column + value > 80 chars or contains
  a newline. JSON / BLOB / geometry / date paths stay untouched.
- TextCell renders a single-line preview with a chevron (multi-line
  values get a ⏎ substitution). No separate viewer window — chevron
  is the only entry point per design discussion.
- TextExpansionEditor opens inline below the row: Monaco in plaintext
  mode with a manual Diff toggle (default off) against the original.
- FieldEditor swaps the plain textarea for Monaco-based TextInput when
  the sidebar value is long, so the diff toggle is available there too.
- Width-aware Side-by-side toggle (Columns2) appears next to the Diff
  button whenever the editor's container is ≥ 480px. Below that the
  unified diff is the only mode, so the button doesn't promise a
  layout that won't fit. Same gating applied uniformly to JSON.
- Json/Text Monaco wrappers are unified into a single CellCodeEditor /
  CellDiffEditor pair that takes a language prop, replacing the four
  per-language wrappers.

* fix(diff): word-wrap the original pane in side-by-side mode

Long-standing Monaco bug: the read-only original editor in a
side-by-side DiffEditor refuses to wrap long lines no matter how
wordWrap or diffWordWrap is set on the diff editor or via
updateOptions on the inner editors. Fixes here:

- useInlineViewWhenSpaceIsLimited: false — Monaco's inline-fallback
  code path poisons the original pane's wrap state even when the
  fallback isn't actually being used (Monaco discussions #4454, #4701).
- key={renderSideBySide ? "sbs" : "inline"} — toggling renderSideBySide
  at runtime can leave the original pane's wrap unrecoverable (issue
  #3346). React-key remounting sidesteps it by forcing a clean Monaco
  instance per mode.
- diffWordWrap: "on" — explicitly inherit wrap into the diff layer.
- Drop originalEditable: false and apply readOnly per-editor via
  updateOptions, which Monaco respects without breaking layout.

* feat(sidebar): drag-resizable row editor

The fixed 384px (w-96) width left no room for the new Monaco-based
text input, especially with the side-by-side diff toggle. Adds a
4px drag handle on the left edge of RowEditorSidebar driven by a
new useRowEditorResize hook: width is clamped to [320px, 90% of
viewport] and persisted to localStorage. Default stays 384px so
nothing changes for users who don't drag.

* feat(demo): seed text_demo table for issue #207 manual testing

Adds a focused seed alongside json_demo for the long-text chevron +
Monaco diff feature. Nine rows, each targeting a specific edge case:
short / long single-line / multi-line short / markdown article /
code snippet / multi-line SQL / long VARCHAR(500) / all-NULL / unicode.

MySQL relies on its default \\n-in-string handling; Postgres uses
E'...' escape-string syntax for the multi-line bodies.

* fix(demo): set utf8mb4 client charset on MySQL seeds

The mysql client used by docker-entrypoint-initdb.d does not default
to utf8mb4 in MySQL 8's official image; emoji and CJK literals in
the JSON / blog seeds were double-encoded on insert, so drivers that
honor utf8mb4 read back mojibake even though the columns themselves
are utf8mb4. Adding SET NAMES utf8mb4 at the top of each seed makes
the insert path round-trip cleanly. Requires a fresh volume
(docker compose down -v) to take effect.

* Add support for verify-ca and verify-full SSL modes in libpq

* restore some comments

* chore(demo-connections): split demo postgres into separate
tabularis_demo and analytics_demo

* 0.11.0

* docs(changelog): add 0.11.0 version link

* feat(demo): add MySQL triggers demo and bump version to 0.11.0

* docs(readme): add Japanese README and update language links

* fix(drivers): show pagination for SELECTs with leading SQL comments

`is_select_query` did not strip leading `--` / `/* */` comments before
checking for `SELECT`, while `returns_result_set` and
`is_explainable_query` did. A SELECT preceded by a comment header was
therefore routed to the fetch path but bypassed the pagination branch
in `exec_on_*_conn`, returning `pagination: None` so the UI hid the
controls entirely. Align it with the other helpers.

Fixing the classifier alone exposed a latent bug in `strip_limit_offset`:
it tokenised then rejoined with `" "`, collapsing newlines. For a query
like `-- header\nSELECT ... LIMIT 50` the rejoined output became
`-- header SELECT ... LIMIT 50 OFFSET 0`, which engines parse as one
`--` comment line — silently no-op. Switched the tokenizer to track
byte offsets and made `strip_limit_offset` slice the original input,
preserving comments and whitespace verbatim.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* perf: eliminate per-query disk I/O and unblock result display from metadata fetch

* fix(settings): center SettingToggle knob

* fix(drivers): preserve i64/u64 precision past Number.MAX_SAFE_INTEGER

Snowflake-style ids and other BIGINT values above 2^53 - 1 lost their
last digits in the UI because the read path serialised them as JSON
numbers, which JSON.parse on the frontend rounds to the nearest IEEE 754
double.

Add a common helper that emits i64/u64 values as JSON strings only when
they fall outside JS's safe range, and wire it through every place a
driver hands a 64-bit integer to the renderer: MySQL's scalar fallback
(both signed and unsigned), Postgres' INT8 / XID8 / MONEY paths, and
SQLite's INTEGER path. Small values stay native numbers so sorting,
filtering and editing keep working untouched.

Mirror the change on the write-back side: a JSON string that parses as
an i64 outside the safe range is bound as a native integer, so editing
or deleting by a large bigint primary key still matches the row. The
Postgres PK predicate wraps the bind in CAST AS bigint to satisfy
tokio-postgres' strict type checking; MySQL and SQLite rely on their
implicit numeric coercion.

Closes #210

* feat(demo): seed bigint_demo table for issue #210 manual testing

Mirrors the text_demo / json_demo pattern with seven rows that exercise
the JS-safe-integer boundary from both sides: small values that stay
JSON numbers, the 2^53 / 2^53 - 1 boundary, the snowflake id from the
original report, i64::MAX, a u64 value above i64::MAX, and a negative
snowflake. Postgres also covers xid8 and money since both are backed by
64-bit integers.

* feat: Delete selected rows with keyboard shortcut

* chore(icons): redesign macOS dock icon

Replace icon.icns with a Tahoe-style squircle on a light glass background.
Windows .ico and Linux PNGs are left untouched.

* fix(pg): correct handling of TLS/SSL modes in PostgreSQL connection

* revert: remove unintended CHANGELOG changes

* Apply suggestion from @kilo-code-bot[bot]

Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>

* Apply suggestion from @kilo-code-bot[bot]

Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>

* test(connection_cache): cover Hit/Miss/Cold transitions and populate/invalidate cycle

* test(updater): gate Linux-only installation source tests

* test(updater): gate Linux-only installation source tests

* docs(sponsors): add DigitalOcean sponsor to README and SPONSORS

* feat(i18n): add Russian locale and count-based tab pluralization

---------

Co-authored-by: cole <imzhpe@qq.com>
Co-authored-by: ymadd <18324681+ymadd@users.noreply.github.com>
Co-authored-by: Andrea Debernardi <andrea@debbaweb.it>
Co-authored-by: NewtTheWolf <dominik@spitzli.dev>
Co-authored-by: Thomas Müller-Wasle <thomas.mueller1401@gmx.de>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: starasenko <starasenko-ext@goodgamestudios.com>
Co-authored-by: vlor <vlor@foxmail.com>
Co-authored-by: Nikolay Zhuravlev <s.verbaux@gmail.com>
Co-authored-by: vlor <614316365@qq.com>
Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>
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.

JSON/JSONB Editor & Viewer

2 participants