Modernize eXide: CM6 editor, REx parser, REST API, LSP integration#778
Modernize eXide: CM6 editor, REx parser, REST API, LSP integration#778joewiz wants to merge 56 commits into
Conversation
Replace eXide's bundled xqlint parser with a REx-generated parser built from the W3C XQuery 3.1 + Update 3.0 + Full Text 1.0 EBNF grammar. Includes adapter layer, static analysis, and comprehensive tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Migrate from Ace to CM6 with full language support, syntax highlighting, autocompletion, and keybinding infrastructure. Includes editor-utils shim layer and CM6 bundling script. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bundle Prettier with plugins via esbuild. Replaces xqlint CodeFormatter with multi-language formatting support. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace jQuery AJAX with fetch, jQuery UI dialogs with native <dialog> via dialog-utils.js, and AG Grid with vanilla HTML tables. Remove all jQuery/AG Grid CSS themes and scripts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 15+ legacy XQuery modules with a Roaster-based OpenAPI REST API. Endpoints for storage, query execution, packages, authentication, templates, search, admin, sync, and editor operations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
569033d to
c226471
Compare
Comprehensive UI overhaul including redesigned toolbar, status bar with mode selector, native dialog system, tab overflow controls, filterable outline panel, dark mode with system theme detection, keyboard shortcuts dialog, and responsive layout improvements. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CM6 autocompletion with LSP-aware function completions, semantic highlighting via CM6 decorations, hover tooltips, function documentation, and language helpers for JSON, Less, Markdown, JavaScript, CSS, and XML. Remove dead Ace-era code (ace-modes.js, popup.js, splitter.js). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cypress E2E tests for autocomplete, diagnostics, outline, query execution, preferences, formatting, and more. Remaining frontend updates: layout, find/replace, menus, directory browser, drag-and-drop, history, and monitoring panel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Editor features: - Matching tag highlight for XML/HTML (open/close tag partner) - XML/HTML scope breadcrumb in status bar (XPath at cursor) - XML Rename Element with multi-cursor (both tags updated) - XPath signatures with positional predicates in outline - Navigation flash on all jump-to actions (gotoLine, gotoSymbol, gotoDefinition, outline click, rename element) - Selection match highlighting, Cmd+D multi-select - Diagnostics panel (Cmd+Shift+D), autocomplete-on-type preference - Dynamic namespace registry from server, local hover fallback - Deferred splash screen, bracket matching restyled blue - Dialog focus on primary action button instead of close × - Enable multiple selections for multi-cursor operations - Unprefixed default namespace completion (conc → fn:concat) - Preserve namespace prefix on completion acceptance - Fix duplicate outline entries for self-closing XML elements - Fix JS gotoSymbol when outline not yet populated exist-next compatibility: - controller.xq, auth.xqm: xs:dayTimeDuration cast - admin.xqm: dynamic file module detection via util:eval - sync.xqm: dynamic file:sync() via util:eval - packages.xqm: fix collection-to-package resolution - query.xqm: make lsp:* import optional - compileError: parse line/column from message when lsp returns line 0 CI: - Skip autocomplete/outline tests when lsp:* unavailable - Move LSP-dependent Cypress tests to lsp/ subdirectory - Add 4 Cypress tests for prefix/unprefixed completion scenarios Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
POST /api/query/references with {query, line, column, base} returns
all references to the symbol at position. Uses dynamic function-lookup
for lsp:references (gracefully returns empty when unavailable).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
940e50f to
00bafd0
Compare
eXide Desktop wraps eXide's web UI in a native desktop application via Tauri v2. In dev mode, it loads from the local eXist-db server; in production, it bundles the web build. - src-tauri/: Rust entry point, Tauri config, default icons - npm run dev:desktop: opens native window against localhost:8080 - npm run build:desktop: produces .dmg/.exe/.AppImage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add tauri-plugin-fs and tauri-plugin-dialog (Rust + JS) - Register plugins in lib.rs, enable permissions in capabilities - Add src/desktop-bridge.js: detects Tauri runtime and exposes readDir, readFile, writeFile, pickFolder, pickFile, pickSaveFile - No-op in browser mode (eXide.desktop.isDesktop = false) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| - name: Build frontend Using node.js ${{ matrix.node-version }} and build EXPath Package | ||
| run: npm run build | ||
|
|
||
| - name: Download Roaster dependency |
There was a problem hiding this comment.
I would recommend using gh cli, and download both latest release and the min version as per expath-pkg.xml in a matrix
There was a problem hiding this comment.
[This response was co-authored with Claude Code. -Joe]
Done in commits ea7d771–1ddca80: replaced curl with gh release download (using the built-in GITHUB_TOKEN) for the Roaster dependency. The existing release/latest Docker matrix covers the most important cases — testing against the specific semver-min from expath-pkg.xml (6.1.0) would require an old Docker image that predates the Roaster/lsp:* dependencies this PR needs. Happy to add that as a separate matrix entry once we settle on the minimum version the full feature set requires.
| * #error-status. We need to wait for that to settle before testing. | ||
| */ | ||
| function waitForInitialLoad() { | ||
| cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1') |
There was a problem hiding this comment.
random timeouts are usually an anti pattern, is there something we can put on a named route, so we can wait on a request and its response?
There was a problem hiding this comment.
[This response was co-authored with Claude Code. -Joe]
Line 22 is already cy.wait('@compile', { timeout: 10000 }) — a named intercept alias set up in beforeEach via cy.intercept('POST', '**/api/query/compile').as('compile'). The remaining cy.wait(50) at line 30 is waiting for a browser MutationObserver microtask to fire after writing to #error-status, which can't be replaced with a network route. It's a minimal delay for a synchronous DOM write, not a timing guess.
| const el = win.document.getElementById('error-status') | ||
| el.textContent = '' | ||
| }) | ||
| cy.wait(50) |
There was a problem hiding this comment.
[This response was co-authored with Claude Code. -Joe]
Same as above — this is the MutationObserver microtask wait (50ms), not a network timing guess.
There was a problem hiding this comment.
is this covered in our tests?
There was a problem hiding this comment.
[This response was co-authored with Claude Code. -Joe]
Not yet — the deployment dialog involves a multi-step UI (selecting server, entering credentials, triggering package install) that's harder to automate without a test server with the public repo accessible. Tracked in the future test gaps list.
There was a problem hiding this comment.
Why do we store and commit binaries and java here?
There was a problem hiding this comment.
[This response was co-authored with Claude Code. -Joe]
REx.java is the source for the REx parser generator (by Gunther Rademacher, bottlecaps.de), included for transparency and reproducible builds — the site hosts the tool but isn't always reachable. The compiled REx.class has been removed from git (commit ea7d771); npm run generate-parser now compiles it on first use via javac. The .java source stays so contributors can inspect and rebuild without depending on an external download.
eXide Desktop wraps eXide's web UI in a native desktop application via Tauri v2. Connects to a running eXist-db server for full functionality (query execution, LSP, file management). Features: - Native macOS/Windows/Linux desktop app - Custom app icon: colorful glass marbles on wood (Gemini design) - Filesystem and dialog Tauri plugins for future local file access - Desktop bridge JS module (detects Tauri, exposes native APIs) - Release build navigates to eXist-db server URL (configurable via EXIDE_SERVER env var, defaults to localhost:8080) - Dev mode loads from localhost:8080 with hot reload - build-desktop.js: XHTML→HTML5 conversion for WebKit compatibility - Injects eXide.configuration defaults for standalone rendering Build: - npm run dev:desktop — development with hot reload - npm run build:desktop — produces .app/.dmg/.exe/.AppImage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On startup, shows a connection dialog where users can enter an eXist-db server URL. Saves recent servers in localStorage. Clicking Connect navigates directly to the server. - Tauri store plugin for persistent preferences - connect.html: sleek dark-themed connection UI with recent servers - Defaults to localhost:8080/exist/apps/eXide - EXIDE_SERVER env var skips the dialog for automated setups Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
duncdrum
left a comment
There was a problem hiding this comment.
I m starting to be a bit concerned about a mix of Java, Rust, Js,… it adds quite a bit of complexity.
That being said love tauri.
Should be build on ci though.
Full native menu bar matching eXide's web menus: - eXide Desktop: About, Preferences (Cmd+,), Hide, Quit - File: New XQuery (Cmd+N), New (Cmd+Shift+N), Open, Save, Close (Cmd+W) - Edit: Undo/Redo, Cut/Copy/Paste, Find/Replace, Format, Toggle Comment - View: Toggle Collections, Results, Dark Mode - Navigate: Command Palette, Go to Definition (F3), Find References (Shift+F3), Go to Symbol, Go to Line, Function Doc, Diagnostics - Run: Run Query (Cmd+Enter), Launch Application - XQuery: Expand Selection, Rename, Extract Function/Variable - App: Launch, Deploy, Download, Synchronize - Window: Minimize, Maximize, Fullscreen, Next/Prev Tab - Help: Keyboard Shortcuts, About eXide Menu events execute JS in the webview via window.eval(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
File > Open Local Folder opens a native macOS folder picker, then displays a LOCAL tab in the west panel with a browsable file tree (pre-loaded 3 levels deep from Rust). Folders expand/collapse on click. Architecture: since Tauri's JS IPC isn't available on server-loaded pages, all filesystem I/O is done Rust-side. The directory tree is serialized as JSON and injected via window.eval() into the eXide page. Known limitations (future work): - File opening on double-click not yet implemented (needs a mechanism for JS to request file content from Rust without IPC) - LOCAL tab styling needs polish to match COLLECTIONS/OUTLINE - Deep directories beyond 3 levels aren't pre-loaded Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clicking a file in the local filesystem pane now opens it in the editor. Uses a Tauri custom URI scheme protocol (localfs://read/path) so the webview can fetch local file contents without IPC. Rust serves the file content with appropriate MIME types. The JS creates a new editor tab with the file content and correct syntax mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Local files: update tab label to show filename instead of untitled-N - Connect page: test server reachability with 8-second timeout before navigating. Shows Cancel button during connection, Retry on failure. Handles unreachable servers gracefully. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Client-side WebSocket transport (src/ws-transport.js) providing: - Persistent bidirectional connection to eXist-db - JSON-RPC message framing (compatible with LSP protocol) - Request/response with callbacks for hover, completions, etc. - Server-push notifications for diagnostics, monitoring events - Auto-reconnect with exponential backoff - Auto-connect deriving WebSocket URL from page location - Graceful fallback when WebSocket unavailable API: eXide.ws.connect(), .send(), .notify(), .on(), .off() Designed for use with eXist-db's WebSocket support (PR #6145). Phase 1 target: real-time monitoring (replace HTTP polling). Phase 2: LSP over WebSocket (push diagnostics, incremental edits). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Auto-connect to ws://host/exist/ws on eXide startup - Monitor: try WebSocket streaming before falling back to HTTP polling (listens for exist/metrics, exist/queryStarted, exist/queryEnded) - LSP hover: use WebSocket request/response when connected, HTTP fallback when not - WebSocket URL derived from page location using monex's pattern (rootContext + "/ws") Requires eXist-db with WebSocket support (PR #6145). Graceful fallback to HTTP when WebSocket unavailable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Ignore plain-text "ping" keepalive messages from eXist-db WebSocket (server sends "ping" not JSON, was causing parse errors) - Add 5 Cypress tests: auto-connect, endpoint connection, ping handling, API surface, graceful fallback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Displays phase-level timing from lsp:eval: Compile, Eval, Total, Items. Falls back to simple Elapsed display if the timing map isn't available (e.g., older exist-lsp without the timing breakdown). Also passes timing map through query.xqm execute endpoint. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ground Generated from the x.svg in dashboard/resources/images at 16x16, 32x32, and 48x48. Added favicon link to src-tauri/connect.html (the only page template that was missing it). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The lsp:* functions have been consolidated into the exist-api package (http://exist-db.org/pkg/api) which provides a unified platform API for eXist-db. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Used by Node.js test scripts to verify the /ws/eval endpoint protocol (eval, cancel, progress messages) outside of the browser. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This is a major feature release: CM6 editor, REx parser, Roaster REST API, LSP integration, WebSocket monitoring, and Prettier formatting. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The LGPL header was accidentally dropped when syncing the schema from eXist-db/exist. Restored from the upstream source. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Before: before() and after() in file_save_spec each duplicated the same XQuery cleanup request. Now both call cy.cleanupTestFiles(), and other specs that create /db files can reuse it without duplication. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…omplete_spec cy.wait(300/200/500) was used as a proxy for "completions API response arrived". Now each test that accepts a completion intercepts POST /api/query/completions and waits on the named alias, making timing deterministic rather than guessed. Also converts leftover var → const in the filter test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
REx.class is a compiled Java binary — it shouldn't be in version control. REx.java (the source, by Gunther Rademacher, bottlecaps.de) stays committed for transparency and reproducible builds. generate-parser.js now compiles REx.java via javac if the class file is absent, so the workflow is unchanged: run `npm run generate-parser` and it just works. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Run `npm test` (node --test) before building — catches JS regressions early - Validate OpenAPI schema with @redocly/cli before deploying - Replace curl with `gh release download` for the Roaster dependency, using the built-in GITHUB_TOKEN so no secrets are needed - Simplify package.json test script to `node --test` (file discovery is automatic for the *-test.js naming convention used in test/) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
a9ce2ba to
1ddca80
Compare
Previously, "Copy all displayed results to clipboard" concatenated items without any separator. Now each item is joined with a newline, making the pasted output readable when the query returns multiple items. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously, changing the number-of-results dropdown only updated the internal variable without refreshing the display. Now it resets to page 1 and re-fetches with the new page size immediately. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Indent and Highlight Matches buttons previously used identical colors for both hover and active states. Active buttons now show a solid blue background with white text (dark mode: semi-opaque blue), making it clear at a glance whether the option is on or off. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously, clicking an already-open menu header would close it and immediately re-open it. Now clicking an open menu header closes it, consistent with standard menubar behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
xmldb:copy-collection() returns the destination path as a string. The copy action was returning this string alongside the status map, producing a 2-item sequence that roaster cannot serialize as JSON (err:SERE0023). Wrap the for loop in let $_ := to discard the return value and return only the status map. Fixes the Cypress DB manager copy test and makes copy/paste of collections and resources functional. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replacing cy.wait(300) with cy.wait('@Completions') waits for the
network response but not for CM6's internal state to settle. By
the time acceptCompletion() or {enter} ran, the popup had been
dismissed. Replace with li[aria-selected="true"].click() which
interacts directly with the visible popup item before it can close.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
eXide previously used its own /api/query endpoints. Route all query calls to exist-api instead — cookie auth works because both apps share the org.exist.login cookie domain (fixed in exist-api's cookieAuth spec). eXide.js changes: - POST/DELETE/GET URLs: api/query → ../exist-api/api/query - Request body: base → module-load-path - POST response: data.id → data.cursor, data.count → data.items - GET results: data.items (wrapped) → data (plain array) Version bumped to 4.0.0 to signal the new exist-api dependency, which is already declared in expath-pkg.xml. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
query_execution_spec.cy.js: two tests verifying eXide renders adaptive output correctly — xs:string is double-quoted, xs:integer is bare. adaptive_serialization_spec.cy.js: 16 UI-level tests covering all major XDM atomic types drawn from XQTS ser/method-adaptive.xml test cases: xs:integer, xs:decimal, xs:double, xs:float, xs:boolean, xs:string (with internal quote doubling), xs:anyURI, xs:untypedAtomic, xs:dateTime, xs:date, xs:time, xs:duration, xs:base64Binary, xs:hexBinary, xs:QName. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ry library modules Quick fix system: - Update quickfix regexes to match new static analysis error formats (XPST0081 for namespace prefixes, XPST0008 for undeclared variables) - Add Quick Fix to XQuery menu and command palette - Change keybinding from Cmd+Ctrl+Q (conflicts with macOS Lock Screen) to Ctrl+. - Import action resolves namespace URI from cached module registry instantly - Filter out java: class references from import location hints - Add CM6 diagnostic actions so "Import module" appears in error tooltips - Style diagnostic actions as block-level links below the error message - Auto-refresh module cache on cache miss; expose app.refreshModuleCache() Completions for XQuery library modules: - Supplement lsp:completions() with inspect:inspect-module-uri() for modules like kwic, templating that are bundled as .xql files in the uber jar - Build proper function signatures with XQuery cardinality symbols (?, *, +) - Filter out stub entries from inspect XML Hover documentation: - Enhance hover fallback to fetch full function docs from api/editor/completions for prefixed function calls when lsp:hover() returns nothing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@joewiz is it possible to split this PR up into
I do like the direction you are heading in and I would like to get this into the next release as a bundled application. |
|
@line-o Thanks for considering this PR for the next release! Sure, I can update the PR to set aside the eXide Desktop feature. |
|
Superseded by #794, which contains only the web-app modernization (no eXide Desktop). The desktop scope will follow in a separate PR. Closing this PR but leaving the |
Summary
A comprehensive modernization of eXide, replacing legacy dependencies with modern alternatives and adding LSP-powered language intelligence. This consolidates work from multiple prior PRs (#774, #777) into a single reviewable PR with grouped commits.
Commits (grouped for review)
1. Replace xqlint parser with REx-generated XQuery 3.1 parser
REx-generated parser from W3C EBNF grammar (XQuery 3.1 + Update 3.0 + Full Text 1.0). Adapter layer preserves the existing visitor infrastructure. Includes unit tests for parser, visitors, and static analysis.
2. Replace Ace editor with CodeMirror 6
Full migration from Ace to CM6 with language support, syntax highlighting, keybinding infrastructure, and editor-utils compatibility layer.
3. Add Prettier-based code formatting
Multi-language formatting via Prettier + plugins (XQuery, XML, HTML, CSS, Less, Markdown), bundled with esbuild. Replaces xqlint's CodeFormatter.
4. Remove jQuery, jQuery UI, and AG Grid dependencies
All AJAX calls use fetch, dialogs use native
<dialog>via dialog-utils.js, DOM manipulation uses vanilla JS. AG Grid replaced with vanilla HTML tables.5. Add OpenAPI/Roaster REST API and remove legacy XQuery modules
Replace 15+ legacy XQuery modules with a Roaster-based OpenAPI REST API. All backend operations (storage, query, packages, auth, sync, search, admin) handled through typed JSON endpoints.
6. Redesign UI
Toolbar, status bar with mode selector, native dialog system, tab overflow controls, filterable outline panel, dark mode with proper syntax highlighting (One Dark palette via CSS classHighlighter), system theme detection, keyboard shortcuts dialog, monitoring panel, and responsive layout.
7. Add CM6 autocomplete, semantic highlighting, and multi-language helpers
CM6 autocompletion with LSP-aware function completions (prefixed and unprefixed default namespace), semantic highlighting via decorations, hover tooltips with AST fallback, go-to-definition (same-file and cross-module), multi-cursor rename, and language helpers for JSON, Less, Markdown, JavaScript, CSS, and XML. Native HTML tag/attribute, CSS property/value, and JavaScript local variable completions via CM6 language packs.
8. Add Cypress integration tests and remaining frontend updates
E2E tests for autocomplete, diagnostics, outline, query execution, preferences, formatting, and more (172+ tests). Remaining frontend updates: layout, find/replace, menus, directory browser, drag-and-drop, build scripts, login page.
9. Add tag matching, exist-next compat, and CI fixes
10. Add Find All References
11. WebSocket transport and monitoring
ws-transport.js) for real-time LSP and monitoring push12. Server-side cursor query execution
Replaces the old XQueryServlet + session.xq flow with a cursor-based architecture using
lsp:eval/lsp:fetch/lsp:closefrom the exist-lsp package.Before:
After:
Key properties:
documentURIandnodeIdavailable per result itemThis follows the same pattern used by SQL clients (server-side cursors), BaseX GUI (in-process result references), and eXist's own Java admin client (XML-RPC result handles).
Cursor store cache policy
Results are held in a Caffeine cache with configurable eviction:
cursor.maximumSizecursor.expireAfterAccesscursor.maximumWeightConfiguration via
exist.xmlmodule parameters:13. Query cancellation
What it does:
AbortController.abort()drops the HTTP connectionsystem:kill-running-xquery()(admin only)Risks — cancelling state-modifying queries can leave the database inconsistent:
insert/delete/replace/rename)update value,update insert)xmldb:store()/xmldb:remove()sm:chmod()/sm:chown()file:write()/file:delete()Mitigations:
xmldb:*,sm:*,file:*mutations14. Results toolbar and menubar polish
Temporary workarounds (remove after upstream merges)
xquery version "4.0"code with a user-friendly message. Remove once prettier-plugin-xquery adds 4.0 support.Known limitations
lsp:eval/lsp:fetch/lsp:closeare eXist-specific IDE support functions in the exist-lsp package. Reviewers are invited to weigh in on whether this scope is appropriate or if a separate package would be preferred.Requirements
Test plan
npm run build && xst package install local build/eXide-3.5.5.xar --force)npm test— 178 tests)autocomplete_spec(11),diagnostics_panel_spec(5),error_status_spec(8),native_autocomplete_spec(7)goto_references_spec(F3 jump + clickable class)goto_references_spec(QuickPicker opens + Escape closes)autocomplete_speccovers prefixed, unprefixed,fn:,repo:, variable completionslayout_spec(dark mode layout)prettier_format_spec(4 tests)outline_navigation_spec(12 tests)tag_scope_selection_spec(CSS defined, XML/XQuery scope, selection match)query_execution_spec"paginates results with next/previous buttons"1 to 10000) —query_execution_spec"handles large result sets" (10 DOM nodes for 10K items)query_execution_spec"re-fetches page when serialization mode changes without re-executing"query_execution_spec"re-fetches page when indent toggle changes"query_execution_speccancel button tests (existence, visibility)monitor_spec"shows running query and allows killing it" (flaky timing)🤖 Generated with Claude Code