Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds export functionality to the web UI, allowing users to download query data in JSON or Markdown format. The feature includes analytics aggregation (count, avg, p95, max durations) similar to the existing TUI export functionality.
Changes:
- Added two export buttons (JSON and Markdown) to the header
- Implemented data export with filtering support and analytics aggregation
- Added timestamp-based filename generation for exported files
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| web/static/style.css | Added CSS for export button group layout |
| web/static/index.html | Added JSON and Markdown export buttons in header control group |
| web/static/app.js | Implemented export functionality including data building, JSON/Markdown rendering, analytics calculation, and file download |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
web/static/app.js
Outdated
| if (data.filter) { | ||
| exportLine += ` (filter: ${data.filter})`; |
There was a problem hiding this comment.
The filter text is directly interpolated into the markdown output without escaping. If the filter contains markdown special characters (like #, *, [, ], etc.), it could potentially break the markdown rendering. Consider escaping or sanitizing the filter text before including it in the markdown output, similar to how query text is escaped with escPipe for table cells.
web/static/app.js
Outdated
| a.click(); | ||
| URL.revokeObjectURL(url); |
There was a problem hiding this comment.
The anchor element is not appended to the document before triggering the click, and URL.revokeObjectURL is called immediately after click. The anchor element should be appended to document.body before calling click() to ensure the download works reliably across browsers. Additionally, URL.revokeObjectURL should be delayed (e.g., using setTimeout) to ensure the browser has time to start the download before the URL is revoked, otherwise the download may fail in some browsers.
| a.click(); | |
| URL.revokeObjectURL(url); | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| setTimeout(() => { | |
| URL.revokeObjectURL(url); | |
| }, 0); |
| const args = q.args.length > 0 | ||
| ? '[' + q.args.map(a => "'" + a + "'").join(', ') + ']' | ||
| : ''; | ||
| md += `| ${i + 1} | ${q.time} | ${q.op} | ${fmtDurExport(q.duration_ms)} | ${escPipe(q.query)} | ${args} | ${escPipe(q.error)} |\n`; |
There was a problem hiding this comment.
The args values in markdown export are not properly escaped. When building the args string, the values are wrapped in single quotes but special characters like single quotes, pipes, and backslashes in the argument values themselves are not escaped. This could break markdown table formatting or potentially lead to injection issues. Consider escaping special characters (especially pipe characters which break markdown tables, and quotes) in argument values before including them in the markdown output.
No description provided.