feat: dashboard table/logs tiles + per-tile result caps (#149 D9)#168
Closed
BorisTyshkevich wants to merge 1 commit into
Closed
feat: dashboard table/logs tiles + per-tile result caps (#149 D9)#168BorisTyshkevich wants to merge 1 commit into
BorisTyshkevich wants to merge 1 commit into
Conversation
Favorites that aren't chart-shaped now render instead of being skipped: a saved view:'table' renders as a sortable/resizable grid, and non-chartable multi-row results render as a compact logs tile when log-shaped (DateTime-ish + message column, optional level for colors) — covers system.text_log and OTel log tables. Also adds best-effort server-side result caps (max_result_rows/max_result_bytes) plus a client-side 5,000-row trim for every tile query. Closes #164 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018GesXYWGvXv898YAiMzNAt
Collaborator
Author
|
Decision (2026-07-11): this PR will not be merged as-is. The Panels refactor (#166) supersedes the standalone landing — its implementation pulls this branch's code (table/logs renderers, caps, slot lifecycle) and lands it together with the registry, closing #164. Keeping the PR open as the code source / reference until #166's PR is up. |
BorisTyshkevich
added a commit
that referenced
this pull request
Jul 12, 2026
* refactor: extract the chart renderer into src/ui/chart-render.js (#166 prep) Move renderChart/chartCfgFor/the config bar/installChartZoomFix out of results.js into their own module, and visCap + the shared truncationFooter into grid-render.js, so the upcoming panels registry can consume the chart and grid renderers without a results.js import cycle. rerender is now a caller-supplied seam (results.js passes its own repaint) instead of a default that imported results.js back. No behavior change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDPs4oomqSfpGGRpEqHahT * feat: dashboard table/logs groundwork — logs core+renderer, per-tile result caps (#164, from PR #168) Absorb PR #168's D9 code (that PR is superseded by #166 and will close unmerged): src/core/logs.js (log-shape detection, level classes, row shaping), src/ui/logs.js (the compact scroll-only logs view, sharing grid-render's truncation footer), best-effort server caps on every tile fetch (max_result_rows sentinel + max_result_bytes + overflow 'break') with the guaranteed client trim in parseJsonResult(json, cap), and the honest 'first 5,000 rows fetched' tile-footer note. classifyTile is untouched here — #166's autoPanel replaces it in a later commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDPs4oomqSfpGGRpEqHahT * feat: panel-cfg union + safe markdown subset (core, #166) src/core/panel-cfg.js: the panel-config union (chart family | table | logs | text) — validation per arm (chart delegates to chart-data's index checks; logs resolves {time,msg,level} column NAMES with per-role convention fallback — a failed name lookup is that arm's mismatch signal), deep clone with unknown-field preservation (the ignore-and-preserve forward-compat policy), normalize, the autoPanel heuristic ladder (logs > autoChart > table; text never auto-proposed), and resolvePanel — the shared mismatch policy: retain the explicit type, re-derive roles within it, fall back to autoPanel + a diagnostic only when the type is impossible for the shape. src/core/markdown-lite.js: pure parser for the text panel's safe Markdown subset (headings, bold/italic, lists, links, inline code) to a plain-object AST — the DOM is built by the UI layer from the tree, never innerHTML, so raw HTML is inert by construction; link hrefs are http(s)-only. core/logs.js: per-role convention scanners exported for panel-cfg's name-based fallback. Both new modules at 100%. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDPs4oomqSfpGGRpEqHahT * feat: library panel field — upgradeSavedEntry at every ingress, dual-write chart mirror (#166) The saved-entry format moves chart:{cfg,key} → panel:{cfg,key?} as an additive version-1 change (imports still hard-reject version > 1; old builds drop the unknown field and read the mirror). - upgradeSavedEntry (pure, idempotent) runs at every ingress: localStorage startup (createState), JSON import (before cleanView, so a legacy view:'chart' maps to 'panel' instead of being stripped), replace/append/ merge, tab restoration (loadIntoNewTab), and share decode — including main.js's oauth_shared sessionStorage stash, a second deserialization point that bypasses decodeShare. - view:'table' with a latent chart migrates losslessly: the roles ride in a nested panel.cfg.chart stash (a flat spread would collide on type). - withChartMirror derives the legacy chart field from panel (present iff chart-family, deleted otherwise) at all five write sites — saveQuery, replaceLibrary (whose field whitelist now carries panel), mergeSaved's by-id branch, buildExportDoc, encodeShare — so mirror and panel never drift. Removing the mirror next minor requires the upgrader to strip chart when panel exists (noted in the module header). - saveQuery relaxes per type: sql:'' is allowed for {type:'text'} only; share/bootstrap gates become sql||panel so an SQL-less text-panel link is not dropped; text panels export their content to Markdown and are skipped by the .sql script export. - Tab model renamed chartCfg/chartKey → panelCfg/panelKey (tabPanel packs the key only for chart-family cfgs) across tabs/main/chart-render/ dashboard and their tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDPs4oomqSfpGGRpEqHahT * feat: panel registry + workbench Panel drawer tab (#166) The results-pane Chart tab becomes Panel: a Type picker (Bar…Pie · Table · Logs · Text) plus per-type config, dispatching through the new src/ui/panels.js registry (PANEL_TYPES[type].controls/renderPanel → {node, destroy?}) that the dashboard will share, so drawer preview ≡ tile by construction. - Previews never execute SQL: every preview renders from the tab's last explicit Run result; the text panel renders cfg.content with no result at all (and renders through a Markdown AST → DOM builder, never innerHTML); query-backed types show an empty-preview hint before a Run and while one streams. - Dirty pin: the preview renders resolvePanel's clone — render never writes tab.panelCfg; only picker/control edits write back (and mark the tab dirty exactly like a SQL edit). renderChart gains cfg/onCfgChange/ typeControl seams for this; its old holder mode stays for the detached snapshot. - switchPanelType (core, pure): type switches keep role continuity — the chart roles stash into cfg.chart when leaving the family and are consumed on the way back (the same lossless shape the view:'table' migration writes). - Run-less saved-row open: an entry that can't auto-run (a text panel's empty SQL) still restores its remembered drawer view. - Detached Data Pane: Table|JSON|Panel, the panel rendered read-only from the source tab's resolved cfg at expand time (no editor, v1 scope). - resultView/SAVED_VIEWS become table|json|panel ('chart' maps forward). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDPs4oomqSfpGGRpEqHahT * feat: dashboard partitions favorites before execution; tiles dispatch through the panel registry (#166, closes-#164 behavior) - Favorites are partitioned BEFORE any SQL runs: a text panel renders immediately (synchronously, zero queries — app.runTile is never called for it); everything else joins the query wave as before. - applyTileResult dispatches through renderResolvedPanel: an explicit saved panel wins (resolvePanel's mismatch policy — retain type, re-derive roles, diagnostic fallback for impossible/unknown types) and never vanishes — a zero-row explicit panel renders an honest '0 rows' state and stays out of the skip tally; unconfigured results go through autoPanel (logs outrank autoChart, table is the universal fallback), so nothing multi-row is skipped anymore. Only unconfigured empty and single-row (future KPI, #154) results still skip. - classifyTile is deleted (core/dashboard.js keeps route helpers, the JSON transform and the caps); the slot lifecycle holds the registry's destroy + schema-keyed sort/width state (persists across Refresh, resets on schema change; header-click sorts repaint locally, no re-query). D9's table/logs acceptance (#164) passes on this path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDPs4oomqSfpGGRpEqHahT * fix: finalize panel drawer persistence and preview behavior (#166) * fix: align var-strip fields vertically, mark required with *, preview server time Fixes two layout regressions in the #166 var-field/var-combo grid rework: the live preview crowding into the input's own row instead of wrapping below it, and shorter sibling fields (no preview line) sitting vertically offset from taller ones due to outer flex containers centering instead of top-aligning. `.var-combo` reverts to a flex column; `.dash-filters`, `.var-strip`, and `.var-field` align to flex-start/start instead of center. Also, per review: - the relative-time live preview now renders in UTC ("server time") instead of the browser's local zone — simpler and unambiguous than the earlier "(your time)" disclaimer, which this replaces outright. - the optional-field "?" suffix (silently color-only after an earlier removal, and untested — CSS ::after content isn't observable via textContent) is replaced with a "*" marker on required fields instead. - drops the now-vestigial `.var-field` grid-template-rows/row-gap (verified pixel-identical without them — both children already spanned the full height, so a single-row grid was always sufficient). - corrects the stale preview-format examples in README.md and CHANGELOG.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHAyFzCon72V6GbjvX475Z --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
view:'table'renders as a sortable/resizable grid, and non-chartable multi-row results render as a compact logs tile when log-shaped (DateTime-ish column + amessage/msg/body/log/linecolumn, optional level column for row colors) — coverssystem.text_logand OTel log tables even when a numeric column would otherwise auto-chart.max_result_rows/max_result_bytes,result_overflow_mode=break) for every tile query, plus a client-side 5,000-row trim; grid/logs tiles display the first 1,000 rows with a truncation footer.Closes #164
Test plan
npm test— 52/52 files, 1522/1522 tests, 100/100/100/100 coverage held on touched pure/DOM filessystem.text_log/OTel-shaped favorite, confirm logs tile renders with level colors + extras, confirm grid sort/width persist across refresh🤖 Generated with Claude Code
https://claude.ai/code/session_018GesXYWGvXv898YAiMzNAt