1064 Commits
Author SHA1 Message Date
Alexey MilovidovandClaude Opus 5.5 f7225e875e Address review: keep a leading BOM in the Web UI lexer tokens
`tokenizeSync` decoded each token with a default `TextDecoder`, which strips a
leading `U+FEFF`. The token lengths then no longer matched the original text,
so the statement offsets used by the multi-statement "Format" button were off
by one and a BOM-prefixed `select 1;` was sent as `select `. Decode with
`ignoreBOM: true` and add a regression case to the harness.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 02:14:40 +00:00
Alexey MilovidovandClaude Opus 5.5 83010829c2 Address review: harden multi-statement Format in the Web UI
- `splitAllQueries`: the lexer stops at the first error (e.g. an unterminated
  string literal or comment), so the unlexed remainder was dropped and Format
  would edit only the valid prefix. The remainder now belongs to the last
  statement, so the server reports the error and the editor is unchanged.
- Format: re-check the connection, tab and text after awaiting the splitter
  (which loads the lexer on first use), so the text is never sent with the
  credentials of a connection selected after the click.
- Update the Format fixture of `test_play_reconcile_startup` for the new `f<N>`
  aliases and cover multiple statements, all-or-nothing errors, lexer errors and
  a connection change during the lexer load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:08:57 +00:00
Alexey MilovidovandClaude Opus 5.5 4b7bc34d8c Support multi-statement input in the "Format" button of the Web UI
Split the editor's text into statements with `splitAllQueries`, format all of
them in one request (`SELECT formatQuery({q0:String}) AS f0, ...`), and replace
each statement in place. The `;` delimiters, whitespace, and comments between
statements are kept as is.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 06:22:41 +02:00
Alexey Milovidov 200935e998 Merge remote-tracking branch 'origin/master' into HEAD 2026-10-05 05:18:51 +00:00
Alexey MilovidovandClaude Opus 5.5 3e608fb18f Address review: guard the Format request and accept ACCESS_DENIED as proof of authentication
The `Format` button sent `password_elem.value` without `dropAutofilledStalePassword`, so a stale
autofilled password could still be sent by it. It now goes through the guard like the other
requests built from the live connection fields.

`rememberAuthenticationOutcome` ignored every `403`, but `ACCESS_DENIED` (a missing privilege) is
also answered with `403` and is thrown only after a successful authentication. The
`X-ClickHouse-Exception-Code` header tells it apart, so a `403` with `ACCESS_DENIED` now updates
the passwordless entry like any other error of the query. The harness pins both cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:49:02 +00:00
Alexey Milovidov 6ce3574faf Merge remote-tracking branch 'origin/master' into play-remember-passwordless-login 2026-10-03 13:46:13 +00:00
Alexey Milovidov afb2e862be Merge remote-tracking branch 'origin/master' into HEAD 2026-10-03 04:59:47 +00:00
Alexey MilovidovandClaude Opus 5.5 4ec7c56da9 Address review: do not save stale parameters of a notebook cell
`buildTabsMarkdown` wrote `cell.params` verbatim, while `loadTabsFromMarkdown` stamps the
loaded pair as coherent (`paramsSyncedQuery = query`). A cell whose bindings were committed
for an older text therefore had them saved, and made durable on Load, under the new text.
Prune them against the text with `pruneParamsToText` unless `paramsSyncedQuery` matches,
as `writeHistoryEntry` does, and cover it with the `save-drops-stale-parameters` scenario.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 12:12:52 +00:00
Alexey MilovidovandClaude Opus 5.5 2c882a661b Merge remote-tracking branch 'origin/master' into web-ui-notebook-mode
Conflict in the `Format` button handler of `programs/server/play.html`: master added the
`connectionGeneration` check; the branch checks that the editor is still docked in the same
notebook cell. Both checks are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 12:11:01 +00:00
Alexey MilovidovandClaude Opus 5.5 fd719b9126 Address review: forget the passwordless marker after any authenticated request
`rememberLoginPassword` was only called from `checkCredentials` and from a 2xx
response in `postImpl`. On deployments where `checkURL` never reaches
`checkCredentials` (the ping probes the origin, not the configured path), the
database panel, `auxiliaryQuery` or the completions can be the first request
to authenticate with a newly typed password, and a query that fails after
authenticating (a syntax error, an unknown table) also proves the credentials.

The new `rememberAuthenticationOutcome` is called from `auxiliaryQuery`,
`loadCompletions` and `postImpl` (on the response the framing retries settled
on). `401`/`403` never update the entry; another error updates it when the
server itself produced it (`X-ClickHouse-Exception-Code`); without that proof
a non-empty password still forgets the entry, but an empty one is not
remembered. Adds the `test_play_passwordless_logins` harness test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 07:08:33 +00:00
Alexey Milovidov b3d9f650eb Merge remote-tracking branch 'origin/master' into play-remember-passwordless-login 2026-09-27 07:04:55 +00:00
Alexey MilovidovandClaude Opus 5.5 8abe8f1119 Web UI: keep the "Format" button result from being lost or misapplied
Two follow-ups to review comments on https://github.com/ClickHouse/ClickHouse/pull/121652:

- The click handler did not call `markBootstrapDirty` before its server round-trip, so a
  click during startup reconciliation let `reconcileStartup` activate the saved tab or restore
  the saved editor contents, and the formatted text was then discarded as stale.
- `auxiliaryQuery` still returns the payload after a connection change, and the handler did not
  re-check `connectionGeneration`, so a `formatQuery` result from the previous server or user
  could replace the editor text.

Both are covered by new scenarios in `test_play_reconcile_startup` (each one fails without its fix).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 22:19:39 +00:00
Alexey Milovidov 934e39224c Merge pull request #119839 from ClickHouse/play-ui-digit-group-separators
Web UI: group the digits of a long number with a gap, not an underline
2026-09-26 21:18:11 +00:00
Alexey Milovidov 43147b23f7 Merge branch 'master' into play-ui-digit-group-separators 2026-09-26 02:53:12 +02:00
Alexey Milovidov 45040ff940 Merge remote-tracking branch 'origin/master' into play-json-pretty-print 2026-09-25 21:39:04 +00:00
Alexey MilovidovandClaude Opus 5.5 b0dbf2a612 Address review: pretty-print a JSON cell from its text, keeping every lexeme
`prettyPrintJSON` rendered the value after a round trip through `JSON.parse`,
so the selected view could show a different document than the compact cell:
numbers became IEEE-754 doubles (`9007199254740993` shown as
`9007199254740992`, `1e309` as the non-JSON `Infinity`), strings were
re-escaped, a repeated key was dropped and integer-like keys moved first.

It now walks the original text with a small recursive-descent parser that
copies every scalar lexeme verbatim and only changes the whitespace; text that
is not JSON throws a `SyntaxError`, as before.

The new integration test `test_play_json_pretty_print` executes the real
`prettyPrintJSON` from the served page in Node.js (the same setup as
`test_play_result_shaping`) and checks the layout, the verbatim value, the
highlighting spans and the rejection of invalid text; against the previous
implementation 7 of its checks fail.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 21:38:57 +00:00
Alexey Milovidov 13d9bd0401 Merge remote-tracking branch 'origin/master' into play-cell-copy-feedback 2026-09-25 08:20:51 +00:00
Alexey MilovidovandClaude Opus 5.5 925c788e63 Web UI: use a renamed tab's title as the window title
If the tab title is not the default generated one (`Query A`, `Query B`, ...),
the window title is exactly the tab title, without the `ClickHouse Query: `
prefix. Tabs with default titles keep the previous behavior.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 21:08:57 +02:00
Alexey MilovidovandClaude Opus 5.5 9e6ce70e15 Address review: record notebook changes that keep the active cell at once
Adding, editing or deleting a Markdown cell, moving a cell, or deleting an
inactive query cell only armed the 400 ms `scheduleSave` debounce. No cell
activation or run follows such a change, so nothing else recorded it: a reload
inside that window found the pre-change notebook both in `IndexedDB` and in
the current history entry (whose `cells` recreate the tab on Back/Forward).

- `commitNotebookChange(tab)` refreshes the active tab's history entry in place
  and saves at once (`saveNow`, which takes over a pending debounce); used by
  `addCell` (Markdown cell), `moveCell`, `deleteCell` and the Markdown editor's
  `blur`. Keystrokes stay debounced, like the query editor's.
- `addCell` for a query cell saves once the activation has loaded the new cell.
- A save still pending when the page goes away is started on `pagehide`.

New `notebook_harness.js` scenarios: `notebook-change-survives-immediate-reload`
(each change snapshotted 30 ms after it and rebooted; all 8 checks fail on the
previous `play.html`), and `save-load-round-trips-notebook` for the notebook-aware
Save / Load from the master merge. `waitForNextPersist` now waits until no save
is pending, since a notebook change no longer leaves a debounced one behind.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 22:41:39 +00:00
Alexey MilovidovandClaude Opus 5.5 240e654e2c Merge remote-tracking branch 'origin/master' into web-ui-notebook-mode
Conflicts in `programs/server/play.html`, all resolved in favour of master's
single layout while keeping the notebook structure:
- markup: the connection `#inputs` now live in the drop-down (master); the
  editor keeps its `#editor-home` wrapper and gains master's `Format` button;
- `renderTabBar`: `syncConnectionUI` is gone (master), `focusEditor` kept;
- `saveHistory`: `cell.result` kept with master's comment;
- startup blank-tab pruning comment merged.

Reconciled with master's new code:
- master dropped the `controls_elem` declaration that the notebook run row
  still needs; re-declared as `controls_form_elem` (so `05234`, which pins that
  the connection inputs never move, keeps its meaning);
- Save / Load of tabs as Markdown now covers every cell of a notebook: each
  query cell as before (query, parameters, result tables), each Markdown cell
  as a `markdown` code block; Load turns every block under a tab heading into
  a cell. A single-query tab saves exactly as before. `05233` updated;
- the `Format` button also drops its result when the editor moved to another
  cell while the request was in flight.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 22:30:14 +00:00
Alexey Milovidov 69fdf02354 Merge branch 'master' into play-remember-passwordless-login 2026-09-23 02:25:30 +02:00
Alexey MilovidovandClaude Opus 5.5 fdc1a5424b Web UI: Format button is yellow on hover and accented yellow while formatting
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 00:35:43 +02:00
Alexey MilovidovandClaude Opus 5.5 8544d0e18d Web UI: capitalize the Format button, show its errors in the result pane
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 00:35:07 +02:00
Alexey MilovidovandClaude Opus 5.5 344d6fb7b3 Add a format button to the query editor in Web UI
The button is in the bottom right corner of the editor. It sends the query
to the server as a parameter of `SELECT formatQuery({q:String})` in the
request body and replaces the editor contents with the result (undoable).
A formatting error is shown in a balloon above the button.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 00:20:34 +02:00
Alexey MilovidovandClaude Opus 5 4bd829d8f1 Web UI: disable Grammarly in the Save and Load textareas
The two dialog textareas carried only `spellcheck="false"`. They hold
SQL, Markdown and JSON, so give them the same opt-out the query editor
already has: `autocorrect`, `autocomplete` and `data-gramm` too.
Grammarly in particular adopts a textarea and injects its own overlay
into it, littering the monospace text with underlines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 22:52:13 +02:00
Alexey MilovidovandClaude Opus 5 b98b103619 Web UI: one layout, always with the tab bar
The page had two layouts. A fresh visit - one tab that had never run -
hid the tab bar and showed the url/user/password inputs inline above the
editor; anything else showed the bar and relocated that same `#inputs`
element into the drop-down behind the `🔑` button. The bar appeared when
a second tab was opened, when a run started, and when a run left a
result, and collapsed back when the last of those went away.

The consequence was that everything living in the bar - now including
loading a saved workspace, which is exactly what one wants on a fresh
page - was unreachable until a query had been run.

Keep only the second layout. The bar is always shown, and the connection
inputs sit in the drop-down from the start: the markup puts them there,
so there is no relocation and no first-paint shift. With one layout,
`syncConnectionUI` and the focus hand-offs it needed across the
transition are gone, as is the bar's visibility rule and the two
re-renders (`beginFlight`, `endFlight`) that existed to drive it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 22:39:23 +02:00
Alexey MilovidovandClaude Opus 5 e27ba98083 Web UI: trim the description in the Save tabs dialog
The hint spelled out the whole document layout and which parts of it are
read back. That belongs in the code comments, where it already is; the
dialog only needs to say what the button does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 22:32:53 +02:00
Alexey MilovidovandClaude Opus 5 9ca45287fa Web UI: align the tab save/load icons on a shared box
The two icons were drawn to different vertical extents on their shared
24x24 grid - the floppy's body is square (y from 3 to 21) while the open
folder is a unit shorter (y from 3 to 20) - so side by side their tops
and bottoms did not line up.

Shorten the floppy's body to 17 units so both span y from 3 to 20. It is
the floppy that gives way, not the folder: the folder's height is set by
two arcs tangent to its box, and moving those would reshape its corners.

Measured in the browser: both `getBBox` extents are now y 3.00 to 20.00,
and at 4x device scale the rendered ink of both spans exactly the same
rows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 22:23:34 +02:00
Alexey MilovidovandClaude Opus 5 6f87f4a320 Web UI: line-art icons for the tab save/load buttons
The two buttons carried the `💾` and `📂` emoji. Replace them with
inline SVG line art in the same weight and 24x24 grid as the toolbar's
copy and download icons, stroked in `currentColor`, so they are muted
in the tabs' own `--misc-text-color` when idle and lit on hover - a
colour glyph takes no text color, so it could be neither.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 21:56:41 +02:00
Alexey MilovidovandClaude Opus 5 a058b90fb3 Web UI: save and load the list of tabs as Markdown
Two buttons at the right end of the tab bar, left of the `🔑` connection
button, save the whole workspace - the list of tabs, the query of every
tab and its query parameters - as a Markdown document, and load it back.

Each opens a modal. Save shows the document in a textarea with `Copy`
and `Download`; the textarea is editable and both actions read it back,
so the document can be trimmed before it leaves the page. Load takes a
document pasted into its textarea, or uploaded from a file into that
same textarea, so a file is reviewed on the same screen as a paste
before it replaces the workspace.

The document has one top-level heading per tab (its title), the tab's
editor content in a fenced code block, its parameter values as JSON
under a `## Parameters` sub-heading, and - when the tab has one - its
result as a Markdown table:

    # Query A

    ```
    SELECT {n:UInt8}
    ```

    ## Parameters

    ```json
    {
        "n": "1"
    }
    ```

    | 1 |
    |---|
    | 1 |

Loading reads back the titles, the queries and the parameters. The
parameters are written as JSON rather than as a table so that they
round-trip exactly: they are restored as data, and a table would mangle
a value holding a `|` or a newline. The result tables, in contrast, are
deliberately not read back - a table is a lossy rendering of a result
(no column types, cells escaped for the table syntax, rows capped), so
restoring one as data would fabricate a result the server never
returned. A loaded tab is therefore a query that has not been run yet.

The tables are built by replaying each tab's stored result snapshot into
a duck-typed stand-in for `query-result` that collects the header and
the rows instead of rendering them, so the framing and the compact row
shapes are parsed by the very code that rebuilds a table on screen
(`makeEventStreamHandler`) rather than by a second parser.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 20:47:22 +02:00
Alexey Milovidov ccde71521f Merge branch 'master' into play-json-pretty-print 2026-09-19 18:01:29 +02:00
Alexey Milovidov 9bf07e1949 Merge branch 'master' into play-remember-passwordless-login 2026-09-19 18:01:22 +02:00
Alexey Milovidov 2b0d72b9c4 Merge remote-tracking branch 'origin/master' into play-ui-digit-group-separators 2026-09-19 08:56:40 +00:00
yanglongwei 447a1b2ec2 Play UI: shorten cell copy checkmark duration to 1s. 2026-09-19 06:57:55 +00:00
Alexey MilovidovandClaude Fable 5.1 357db4fca7 Address review: budget the whole notebook payload of a history entry and stop clearing the shared row after a repaint
Two findings of the AI review on `programs/server/play.html`.

1. `serializeCellsForHistory` counted only the result snapshots against
   `HISTORY_NOTEBOOK_BUDGET`, so a text-heavy notebook could exceed the
   browser's per-entry history-state limit through its Markdown alone and
   `writeHistoryEntry` fell through to the `cells: []` fallback, after which
   `Back` could recover only the active query cell of a closed tab. The
   budget now covers the whole serialized cell (query, parameters, text,
   result): result snapshots are stripped first, largest first, then
   Markdown text is trimmed by no more than the remaining excess. Queries
   are never cut, because a truncated statement can be a different, still
   valid statement. `writeHistoryEntry` gained a rung between "results
   stripped" and "no notebook copy" that drops the Markdown text but keeps
   the structure and every query.

2. `deleteCell` stopped a run in the deleted cell with an unconditional
   `progressEl.clear()` after `endFlight` had already repainted the shared
   row from the surviving active cell, wiping that cell's stats. The delete
   path now goes through `cancelTabRun`, whose own trailing clear is made
   conditional on `cellOwnsChrome(cell)`: the widget is cleared only while
   it still describes the stopped cell.

Tests: `notebook_harness.js` gains `delete-running-cell-keeps-active-chrome`
and `history-budget-covers-text`, and `stop-after-editor-moved-repaints-chrome`
now records the calls made on the shared widget. All three fail against
the previous page. `04548_play_ui_framing_fallbacks` expects five
`cancelTabRun(tab);` call sites.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 15:16:50 +00:00
Alexey Milovidov e0303acd83 Merge remote-tracking branch 'origin/master' into web-ui-notebook-mode
# Conflicts:
#	programs/server/play.html
2026-09-17 15:09:32 +00:00
yanglongwei dacfcd40e5 Show a checkmark after copying a cell value in Play UI.
Swap the result-table copy icon to a checkmark for 1.2s after a successful clipboard write so users get immediate feedback.
2026-09-17 14:47:23 +00:00
Alexey Milovidov 5208785b63 Merge remote-tracking branch 'origin/master' into play-graph-theme-and-font 2026-09-17 14:45:07 +00:00
Alexey MilovidovandClaude Fable 5.1 ebb32b9c0c Address review: never tag a duplicated column name as JSON
A result header can carry the same output name more than once, and both the
row lookup and the per-column type flags are keyed by that name, so a `JSON`
namesake of a non-`JSON` column tagged the latter's cells with `_jsonText`;
selecting one of them then sent plain text through `JSON.parse` in
`_expandJSON`, which threw. Withhold the tag for a duplicated name, the way
sort and filter are already withheld from it (`duplicateColumnNames`), so the
compact form simply stays. The test pins the guarded line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 11:36:32 +00:00
Alexey Milovidov 07df841800 Merge remote-tracking branch 'origin/master' into play-json-pretty-print 2026-09-16 11:31:36 +00:00
Alexey MilovidovandClaude Fable 5.1 8ef393b09a Web UI: do not require the CSS global at page-script load time
The `:autofill` selector detection called `CSS.supports` while the page script was being
evaluated. The headless harnesses of `test_play_image_preview` and
`test_play_reconcile_startup` run the extracted script in a Node `vm` context without a
`CSS` global, so the whole script failed with `ReferenceError: CSS is not defined`. The
detection now yields no selector where `CSS.supports` is unavailable, which leaves the
stale-password handling inert there, as it already is in a browser supporting neither form.

CI report: https://s3.amazonaws.com/clickhouse-test-reports/praktika.html?PR=118638&sha=5e5b5018080d4bf074737d61603ce90eaa0df59c&name_0=PR&name_1=Integration%20tests%20%28amd_asan_ubsan%2C%20db%20disk%2C%20old%20analyzer%2C%201%2F6%29
PR: https://github.com/ClickHouse/ClickHouse/pull/118638

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 11:28:42 +00:00
Alexey MilovidovandClaude Fable 5.1 a19b55d18f Address review: guard every outbound read of the live password and make the passwordless-login bookkeeping best-effort
`dropAutofilledStalePassword` now also runs before `loadDatabases` (opening the databases
panel), before the download rerun, before `storeCredentials` and in `sendCredentialsToTerminal`,
so a stale autofilled password for a login remembered as passwordless is dropped on every path
that sends or stores it, not only on credential checks, query submission and completions.

`rememberLoginPassword` and `loadPasswordlessLogins` swallow `localStorage` failures
(`QuotaExceededError`, storage policy exceptions), so a successful authentication check or
query is never turned into a client-side error by the bookkeeping.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 11:26:54 +00:00
Alexey Milovidov 717fe5a290 Merge remote-tracking branch 'origin/master' into play-remember-passwordless-login 2026-09-16 11:25:59 +00:00
Alexey MilovidovandClaude Opus 5 f44b3107af Web UI: group the digits of a long number with a gap, not an underline
A long number in the result table was grouped by underlining one digit at
each group-of-three boundary, mirroring `clickhouse-client`. In a proportional
page - unlike a terminal - a gap reads better, and the classic typographic
grouping is a thin space: `1 234 567`.

A real separator character would be copied along with the number, so the
groups are separated by an EMPTY element instead - `<span class="num-sep">`,
an inline-block a quarter of a digit wide (`0.25ch`, which in the table's
monospace font is exactly a quarter of a digit). The gap is painted, but the
text of the cell is still nothing but the digits: selecting the number, or
copying the cell, yields `1234567`.

The same grouping is applied to the row-number column, whose numbers pass
five digits on a later page of a large result.

The digit groups in the query editor are left as they are: its backdrop must
match the metrics of the textarea underneath it character for character, so
the highlight cannot change the advance width of anything.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-13 21:38:05 +02:00
Alexey MilovidovandClaude Fable 5.1 6766e72deb Web UI: restore the row-number offset of a failed run for its framed snapshot too
The offset a failed paginated run's rows were numbered from (`skipped_rows`)
was only put back into the result element in the plain `!result.ok` branch of
`materializeResult`. A default-format run of a current server is a framed
`EventStream`, and its failed snapshot is replayed by the `event_stream`
branch before that, so on a reload, Back/Forward, or a tab reopen the rows of
a failed page 2 were still renumbered from 1. Set `_skipped_rows` before the
branches, so every replay of a failed snapshot numbers its rows from the offset.

The `/play` startup harness (`tests/integration/test_play_reconcile_startup`)
stubs the result element, and `postSingle` now reads `_rowNumberOffset` from
it before dropping the page of a failed run: give the stub the method (the
stub never paginates), which fixes the `TypeError: el._rowNumberOffset is not
a function` failures of `test_play_reconcile_startup` in CI:
https://s3.amazonaws.com/clickhouse-test-reports/praktika.html?PR=116614&sha=20587bdb4074a205308cbce86b6328f7ab35856e&name_0=PR&name_1=Integration%20tests%20%28amd_tsan%2C%203%2F6%29
https://github.com/ClickHouse/ClickHouse/pull/116614

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 18:54:26 +00:00
Alexey MilovidovandClaude Opus 5 69aa6edad1 Address review: re-own the history entry when the active notebook cell is deleted
`deleteCell` handed the editor to a neighbouring query cell but left the current
history entry and the URL hash describing the cell it had just deleted. Both are
read back as authoritative for the tab, so the deleted query came back:

- `reconcileStartup` substitutes the URL query into the saved tab when it equals
  neither its `query` nor its `lastSavedQuery`, and nulls the result with it;
- `window.onpopstate` writes the entry's query into whatever cell is active.

Reproduced in `notebook_harness.js`: with cells `A` (URL-synced) and `B`, making
`A` active and deleting it left the hash at `A`'s query, and the following reload
replaced `B`'s query with `A`'s and dropped `B`'s result. A Back to that entry
did the same in-session.

`deleteCell` now re-owns the entry from the post-delete notebook once the editor
handover completes, via `refreshCurrentHistoryEntry` - in place, because a
deletion is a structural change and must not push a Back target - exactly as
`setActiveCell` stamps history after its own activation.

Deleting the LAST query cell is now refused outright. That cell is where the
shared editor lives and everything the editor, history, URL and persistence code
reads through `tab.<field>` forwards to it, so a notebook of text cells alone has
no query to describe: `tab.query` was `undefined` and `renderTabBar` threw out of
`queryToColor`. The cell header already disables `✕` for that cell, so the rule
is now enforced in `deleteCell` for every other caller too, instead of papering
over the resulting state downstream.

New scenario `delete-active-cell-reowns-history` covers the entry, the URL hash,
the absence of a pushed entry, the reload, the Back, and the refusal. Verified
load-bearing: all five restore checks fail against the previous `play.html`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 12:08:34 +00:00
Alexey MilovidovandClaude Fable 5.1 8d9b82d079 Web UI: pretty-print and highlight the value of a selected JSON cell
Clicking a cell of a `JSON` column in `play.html` now shows the value pretty-printed with
two-space indentation, one member per line, with keys, strings, numbers and the literals
`true`, `false`, `null` highlighted. The compact one-line form is restored when the
selection leaves the cell, and stays the value used for copying and filtering.

The pretty form is built lazily for the selected cell only (`_expandJSON`), from the compact
text the cell already holds, which is the same on the page's default `JSONCompactStrings`
format (the value arrives as JSON text) and on the `JSON` format (a parsed object). The
expanded overlay of a selected cell is color-inverted, so the pretty-printed block inverts
itself back and takes its palette from new `--json-*` variables defined per theme.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 20:56:04 +02:00
Alexey MilovidovandClaude Fable 5.1 5e5b501808 Web UI: remember passwordless logins and drop a stale autofilled password
The page offers the login to the browser's password manager after each
Run, but the Credential Management API cannot express an empty password
(`PasswordCredential` rejects it with a `TypeError`) and there is no API
to delete a saved login. So after a server/user was saved with a password
and later authenticates with an empty one, the browser keeps the stale
entry and autofills it on every visit, and the page sends the stale secret
with every request.

Remember in `localStorage` which server/user logins last succeeded with an
empty password (recorded from the credential probe and from a successful
query run, forgotten again when a non-empty password succeeds), and drop
a browser-autofilled password (`:autofill`) for such a login when it shows
up: on the `input` event autofill raises, and at the points that read the
field to build a request. A typed password is always kept.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 20:43:15 +02:00
Alexey Milovidov 20587bdb40 Web UI: keep the row-number offset of a failed paginated run in its snapshot
Address review: `postSingle` drops the tab's shape (`clearResultShape`) when a run fails, so a
paginated run that streamed rows before an in-band failure was snapshotted with `{ page: 0, size: 0 }`,
and `renderFailedSnapshot` replayed those rows through `appendRow` numbered from 1 - while live they
were numbered from `size * (page - 1)`. Capture the offset (`_rowNumberOffset`) before the shape is
dropped, persist it with the failure snapshot as `skipped_rows`, and let the restored element count
from it (`_skipped_rows`) while no page is selected. Successful runs keep counting from the restored
`pagination`; legacy entries read as a zero offset.

The stateless test checks the persistence and replay lines in the served `/play` source.
2026-09-02 16:07:24 +00:00
Alexey Milovidov 061f77e9e3 Merge remote-tracking branch 'origin/master' into web-ui-notebook-mode 2026-08-31 21:40:22 +00:00