Skip to content

feat(execution-history): content-fit runs table columns + centered text - #25

Merged
aryasaatvik merged 2 commits into
devfrom
feat/runs-table-content-fit
Jun 15, 2026
Merged

aryasaatvik merged 2 commits into
devfrom
feat/runs-table-content-fit

Conversation

@aryasaatvik

Copy link
Copy Markdown
Owner

What

Rebuilds the runs list as a real semantic <table> with content-fit columns (table-layout: auto), replacing the bespoke fixed-width flexbox layout.

  • Content-fit: each column sizes to the maximum width its rows need — the sticky <thead> and <tbody> rows share one column model, so columns size and stay aligned automatically (no more shared COL_* fixed-width constants, no min-w scroll floor).
  • Alignment: short / numeric / badge columns (status, duration, tools, interaction, log) are centered; text columns (started, trigger, actor, code) are left-aligned.
  • No blowout: the two columns that can run long — actor (token id / email) and code — are width-capped + truncated (hover shows the full value).
  • The row <Button> becomes a <tr role="button" tabIndex onClick onKeyDown> (click + keyboard, accessible). The live divider / loading / empty / end-of-history states become colSpan rows.

Why this shape (vs openstatus)

Studied openstatus-data-table: it renders a real shadcn <table> but sets fixed per-column size values (CSS vars → inline widths) — not content-fit. True "snap each column to its content" comes from a real table's default table-layout: auto, which is what this uses. Sticky-header border rendering uses border-separate border-spacing-0 (the same browser-bug workaround openstatus uses).

Notes

  • <table> carries a narrow oxlint-disable react/forbid-elements (with a boundary reason) — the runs table needs its own infinite-scroll container + sticky thead, which the shared <Table>'s built-in overflow wrapper would conflict with. Same suppression the shared Table component itself uses.
  • Trade-off: content-fit means the table is its natural content width — narrower than the pane → left-aligned with slack on the right; wider → horizontal scroll.

Verification

typecheck 0 · lint 0/0 · format clean · tests 19/19.

@greptile-apps

greptile-apps Bot commented Jun 15, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR replaces the bespoke flexbox runs list with a real semantic <table> using table-layout: auto, letting the browser's column model align the sticky <thead> and <tbody> rows without shared fixed-width COL_* constants. All utility rows (loading, error, live divider, end-of-history) become <tr><td colSpan> elements, and each interactive run row becomes a <tr role="button" tabIndex> with Enter/Space keyboard handling.

  • Column sizing: border-separate border-spacing-0 workaround keeps sticky-header borders rendering; max-w-[160px]/max-w-[320px] caps the two long-text columns (actor, code) with truncation; isEmpty is correctly gated as !isLoading && !isError && rows.length === 0 so the loading/error <tr> children are always shown when the empty state is suppressed.
  • Previous findings: the title={run.codePreview} attribute on the code cell landed in 8128880cf, addressing the prior hover-reveal gap; codePreview remains bounded to 256 chars at write time in the store, so no render-side slice is needed.
  • Changeset: patch-level entry for @executor-js/plugin-execution-history accurately describes the change.

Confidence Score: 5/5

Safe to merge — the change is a self-contained UI refactor with no data-layer or auth changes, all states are correctly accounted for, and the previous hover-reveal gap is addressed.

The conversion from flexbox to semantic table is mechanically correct: isEmpty is properly gated, all utility rows use valid colSpan rows, column order in header and rows is consistent, sticky-header border rendering uses the established workaround, and the two long-text columns are capped with truncation. No data flow, auth, or store logic was touched.

No files require special attention.

Important Files Changed

Filename Overview
packages/plugins/execution-history/src/react/shell.tsx Replaces fixed-width div wrapper with a semantic <table border-separate border-spacing-0>; exports RUNS_TABLE_COLSPAN=12 (intentional over-count); isEmpty guard correctly excludes loading/error states; sticky thead and onScroll infinite-scroll logic preserved.
packages/plugins/execution-history/src/react/run-row.tsx Button replaced with <tr role="button" tabIndex={0}> with Enter/Space keyboard handler; title={run.codePreview} added to code cell (addresses previous P1); truncation on actor uses inner min-w-0 truncate span, code cell uses truncate directly on <td max-w-[320px]>; COL_* constants removed.
packages/plugins/execution-history/src/react/column-header.tsx Header row rebuilt as <tr> with <th scope="col"> cells; COL_* imports removed; className prop dropped from SortButton; HEAD constant adds bg-background for sticky bleed prevention; column order matches run-row.tsx conditional rendering.
packages/plugins/execution-history/src/react/RunsPage.tsx Loading/error/load-more-error states converted from divs to <tr><td colSpan> rows; isEmpty correctly gated as !isLoading && !isError && rows.length === 0, so loading/error body <tr> children are always shown when isEmpty=false.
packages/plugins/execution-history/src/react/live-row.tsx LiveDivider wrapped in <tr><td colSpan={RUNS_TABLE_COLSPAN}> for valid tbody placement; imports RUNS_TABLE_COLSPAN from shell.tsx (no circular dependency).
.changeset/execution-history-content-fit-table.md Patch-level changeset for @executor-js/plugin-execution-history; accurately describes the content-fit table rebuild.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    RunsPage --> |isEmpty = !loading AND !error AND rows=0| RunsShell
    RunsShell --> thead["thead sticky - RunsColumnHeader"]
    RunsShell --> tbody["tbody"]
    tbody --> emptyBranch{isEmpty?}
    emptyBranch -->|yes| EmptyRow["tr - td colSpan=12 - empty state"]
    emptyBranch -->|no| children["children body"]
    children --> loadingBranch{view.isLoading?}
    loadingBranch -->|yes| LoadingRow["tr - td colSpan=12 - Loading runs"]
    loadingBranch -->|no| errorBranch{view.isError?}
    errorBranch -->|yes| ErrorRow["tr - td colSpan=12 - Unable to load"]
    errorBranch -->|no| rowItems["rowItems array"]
    rowItems --> RunListRow["tr role=button - RunListRow per run"]
    rowItems --> LiveDivider["tr - td colSpan=12 - LiveDivider"]
    rowItems --> LoadMoreError["tr - td colSpan=12 - Load more error"]
    children --> loadingMore{isLoadingMore?}
    loadingMore -->|yes| LoadingMoreRow["tr - td colSpan=12 - Loading more"]
    children --> endOfHistory{not hasMore AND not isLoadingMore?}
    endOfHistory -->|yes| EndRow["tr - td colSpan=12 - End of history"]
Loading

Reviews (3): Last reviewed commit: "fix(execution-history): add a title to t..." | Re-trigger Greptile

Comment thread packages/plugins/execution-history/src/react/run-row.tsx Outdated
Comment thread packages/plugins/execution-history/src/react/run-row.tsx
Rebuild the runs list as a real semantic <table> with content-fit columns
(table-layout: auto) instead of the fixed-width COL_* flex slots: each column
sizes to its widest cell across the header + rows, and the sticky <thead> shares
one column model with the rows so they align for free. Short/numeric/badge
columns center; text columns left-align. actor + code are width-capped and
truncated so content-fit can't blow the table out. The row <Button> becomes a
<tr role=button> (click + keyboard); the divider / loading / empty / end-of-
history states become colSpan rows.
Greptile P1: the content-fit code cell truncates with no title, so the clipped
preview wasn't reachable on hover (only the actor cell had one). Add
title={run.codePreview} so hovering reveals the full write-bounded preview.
@aryasaatvik
aryasaatvik force-pushed the feat/runs-table-content-fit branch from 897e665 to 8128880 Compare June 15, 2026 16:14
@aryasaatvik

Copy link
Copy Markdown
Owner Author

@greptile the branch was rebased to drop an unrelated commit and the P1 title fix landed in 8128880cf — please re-review the current head.

@aryasaatvik
aryasaatvik merged commit aa3c278 into dev Jun 15, 2026
8 checks passed
aryasaatvik added a commit that referenced this pull request Jun 23, 2026
…xt (#25)

## What

Rebuilds the runs list as a **real semantic `<table>` with content-fit
columns** (`table-layout: auto`), replacing the bespoke fixed-width
flexbox layout.

- **Content-fit**: each column sizes to the maximum width its rows need
— the sticky `<thead>` and `<tbody>` rows share one column model, so
columns size *and* stay aligned automatically (no more shared `COL_*`
fixed-width constants, no `min-w` scroll floor).
- **Alignment**: short / numeric / badge columns (status, duration,
tools, interaction, log) are **centered**; text columns (started,
trigger, actor, code) are left-aligned.
- **No blowout**: the two columns that can run long — `actor` (token id
/ email) and `code` — are width-capped + truncated (hover shows the full
value).
- The row `<Button>` becomes a `<tr role="button" tabIndex onClick
onKeyDown>` (click + keyboard, accessible). The live divider / loading /
empty / end-of-history states become `colSpan` rows.

## Why this shape (vs openstatus)

Studied `openstatus-data-table`: it renders a real shadcn `<table>` but
sets **fixed per-column `size`** values (CSS vars → inline widths) —
*not* content-fit. True "snap each column to its content" comes from a
real table's default `table-layout: auto`, which is what this uses.
Sticky-header border rendering uses `border-separate border-spacing-0`
(the same browser-bug workaround openstatus uses).

## Notes

- `<table>` carries a narrow `oxlint-disable react/forbid-elements`
(with a boundary reason) — the runs table needs its own infinite-scroll
container + sticky thead, which the shared `<Table>`'s built-in overflow
wrapper would conflict with. Same suppression the shared `Table`
component itself uses.
- Trade-off: content-fit means the table is its natural content width —
narrower than the pane → left-aligned with slack on the right; wider →
horizontal scroll.

## Verification

typecheck 0 · lint 0/0 · format clean · tests 19/19.
aryasaatvik added a commit that referenced this pull request Jun 23, 2026
…xt (#25)

## What

Rebuilds the runs list as a **real semantic `<table>` with content-fit
columns** (`table-layout: auto`), replacing the bespoke fixed-width
flexbox layout.

- **Content-fit**: each column sizes to the maximum width its rows need
— the sticky `<thead>` and `<tbody>` rows share one column model, so
columns size *and* stay aligned automatically (no more shared `COL_*`
fixed-width constants, no `min-w` scroll floor).
- **Alignment**: short / numeric / badge columns (status, duration,
tools, interaction, log) are **centered**; text columns (started,
trigger, actor, code) are left-aligned.
- **No blowout**: the two columns that can run long — `actor` (token id
/ email) and `code` — are width-capped + truncated (hover shows the full
value).
- The row `<Button>` becomes a `<tr role="button" tabIndex onClick
onKeyDown>` (click + keyboard, accessible). The live divider / loading /
empty / end-of-history states become `colSpan` rows.

## Why this shape (vs openstatus)

Studied `openstatus-data-table`: it renders a real shadcn `<table>` but
sets **fixed per-column `size`** values (CSS vars → inline widths) —
*not* content-fit. True "snap each column to its content" comes from a
real table's default `table-layout: auto`, which is what this uses.
Sticky-header border rendering uses `border-separate border-spacing-0`
(the same browser-bug workaround openstatus uses).

## Notes

- `<table>` carries a narrow `oxlint-disable react/forbid-elements`
(with a boundary reason) — the runs table needs its own infinite-scroll
container + sticky thead, which the shared `<Table>`'s built-in overflow
wrapper would conflict with. Same suppression the shared `Table`
component itself uses.
- Trade-off: content-fit means the table is its natural content width —
narrower than the pane → left-aligned with slack on the right; wider →
horizontal scroll.

## Verification

typecheck 0 · lint 0/0 · format clean · tests 19/19.
aryasaatvik added a commit that referenced this pull request Aug 18, 2026
…xt (#25)

## What

Rebuilds the runs list as a **real semantic `<table>` with content-fit
columns** (`table-layout: auto`), replacing the bespoke fixed-width
flexbox layout.

- **Content-fit**: each column sizes to the maximum width its rows need
— the sticky `<thead>` and `<tbody>` rows share one column model, so
columns size *and* stay aligned automatically (no more shared `COL_*`
fixed-width constants, no `min-w` scroll floor).
- **Alignment**: short / numeric / badge columns (status, duration,
tools, interaction, log) are **centered**; text columns (started,
trigger, actor, code) are left-aligned.
- **No blowout**: the two columns that can run long — `actor` (token id
/ email) and `code` — are width-capped + truncated (hover shows the full
value).
- The row `<Button>` becomes a `<tr role="button" tabIndex onClick
onKeyDown>` (click + keyboard, accessible). The live divider / loading /
empty / end-of-history states become `colSpan` rows.

## Why this shape (vs openstatus)

Studied `openstatus-data-table`: it renders a real shadcn `<table>` but
sets **fixed per-column `size`** values (CSS vars → inline widths) —
*not* content-fit. True "snap each column to its content" comes from a
real table's default `table-layout: auto`, which is what this uses.
Sticky-header border rendering uses `border-separate border-spacing-0`
(the same browser-bug workaround openstatus uses).

## Notes

- `<table>` carries a narrow `oxlint-disable react/forbid-elements`
(with a boundary reason) — the runs table needs its own infinite-scroll
container + sticky thead, which the shared `<Table>`'s built-in overflow
wrapper would conflict with. Same suppression the shared `Table`
component itself uses.
- Trade-off: content-fit means the table is its natural content width —
narrower than the pane → left-aligned with slack on the right; wider →
horizontal scroll.

## Verification

typecheck 0 · lint 0/0 · format clean · tests 19/19.
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.

1 participant