Uh oh!
There was an error while loading. Please reload this page.
fix(plugin-gantt): one container-driven predicate for a row's dates, and a task list sized from the container - #7420
Merged
Conversation
…and a task list sized from the container (objectui#7204, objectui#7224)
Two gates decided whether a gantt row's dates were painted and they read two
different widths: the Start/End columns were gated on the container-derived
task-list width, the date sublabel under the title on the component's own
`@media (min-width: 640px) { .gantt-sm-hidden { display: none } }` rule, which
reads the viewport. Between a 640px and a 1023px container both were shut and
the row showed no dates at all; the same hole opened at any width once the
splitter was dragged under the threshold.
The sublabel now renders on exactly the complement of the columns, both from
the same container width, and the media rule is gone. The two date cells lose
their `hidden sm:block` viewport test and render on the same predicate as the
header captions they sit under.
The task-list default is sized from the container instead of capped at 320px:
from 1024 up it takes 3/8 of the container clamped to [320, 560], which at 1440
leaves the title 287px with the columns still painted (a 40-character title
measures 262px in the row's font). The Start/End threshold moves from an
estimated 280 to a derived 412 = 32 row padding + 160 columns + 28
open-details slot + 32 title furniture + 160 minimum title, each term traced to
the markup that spends it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyCContributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-project-manager
marked this pull request as ready for review
September 3, 2026 00:40
Uh oh!
There was an error while loading. Please reload this page.
os-project-manager
deleted the
claude/issue-7204-gantt-tasklist-width
branch
September 3, 2026 00:56
This was referenced Sep 4, 2026
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 freeto 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.
Fixes#7204
Fixes#7224
The ruling this implements
Maintainer, 2026-09-02, replying to decision batch #5 where these two cards were items 2 and 3, presented as one ruling with recommendation Y. Verbatim reply: 「同意」 (recorded on #7204, comment 5507936333). Option C (an authorable width key) is not taken — no new key. Option X (predicate + threshold, without part 2) was the recorded fallback, not the plan.
All three parts are in this PR. The
gantt-block passthrough defect (an inventedgantt.taskListWidthvalidates and is silently ignored) is, per the ruling, not repaired here.Part 1 — one predicate
Before, two gates decided whether a row's dates were painted and they read two different widths:
showStartEndColumns(taskListWidth), container-derived — and Tailwindhidden sm:block, viewport@media (min-width: 640px) { .gantt-sm-hidden { display: none } }, viewportTwo gates on two different widths are not complements. Between a 640px and a 1023px container both were shut: the row's dates were in the DOM twice and painted zero times. The same hole opened at any width once the splitter was dragged under the threshold.
Now the sublabel renders on exactly
!showSEColumns, the media rule and the class it hid are gone, and the two date cells lose theirhidden sm:blockso the columns are painted on the same single predicate as the header captions above them. A row always carries its dates one way or the other.Two smaller things fall out of that, unasked for but not separable: the header captions and the data cells now agree (before, a viewport under 640px with a task list over the threshold printed
Start/Endcaptions over cells that were not painted), and the cells'hiddenattribute plus inlinedisplay: noneare replaced by conditional rendering, which removes an inline style.Part 2 — the default, sized from the container
From a 1024px container up the pane takes 3/8 of the container, clamped to [320, 560], rounded to a whole pixel. Below 1024 the two stepped defaults (140 / 220) are unchanged.
sm:text-smfont (measured in Chromium, below), so the top of the ruling's 25-to-40 character band is legible at 1440 and wider with the columns still painted. 3/8 is the smallest clean fraction that does it: 515/1440 = 0.3576 is the bare requirement.depth * 14indent (262 + 14 = 276). Past that, more pane width buys no legibility the ruling asked for and the timeline — the view's primary content — pays for it. The ceiling binds from a 1494px container up.Resulting pane widths and title space, all three desktop widths clearing 412:
Before this change all three were pane 320 / title 67.
Part 3 — the threshold is the derived 412
START_END_COLUMNS_MIN_W = TASK_LIST_ROW_PADDING_W + START_END_COLUMNS_W + OPEN_DETAILS_SLOT_W + TITLE_FURNITURE_W + TASK_LIST_MIN_W, each term named in the source and traced to the markup that spends it:px-2 sm:px-4— 16px a side from 640px upw-16 gantt-sm-w20, and the injected rule sets.gantt-sm-w20 { width: 80px }from 640px upw-6(24px) with itsmarginLeft: 4w-3collapse spacer pulled back 4px (8),gap-2(8), thew-2colour dot (8),gap-2(8)TASK_LIST_MIN_W, the pane's own drag floor, reused32 + 160 + 28 + 32 + 160 = 412. Not the card's estimated 440, which is derived from nothing.
The derivation was checked against the real thing rather than asserted. For a flat leaf row at a 640px-or-wider viewport with
onTaskClicklive, the title's space iswhich reproduces #7204's reported numbers exactly, in Chromium: at pane 320 that is 67px for a flat leaf and 53px at depth 1 (67 − 14) — the card measured 53. A summary row's
w-4toggle costs 4 more, giving 63, which is what the harness read for the summary row. No term is off by a pixel.Measurements — real Chromium, and why that matters here
@mediarules irrespective ofwindow.innerWidth, so while the old rule was livegetComputedStyle(sublabel).displayreadnoneat 500, 800 and 1440 alike — including widths where a real browser painted the sublabel. Taken at face value that reads "the sublabel is never visible", which is false and sends the fix somewhere else. Every viewport-dependent number in this PR comes from Chromium 1194 (/opt/pw-browsers/chromium, preinstalled — noplaywright install), driving the real component through a Vite dev server with the repo's own Tailwind build and the component's own injected style element, at real viewports.#7224's table, before and after (
sub= date sublabel painted,cols= both Start/End cells painted; container equals viewport):Before: 2 rows where sublabel and columns are both off — 640 and 800, the #7224 hole. After: 0. At every row exactly one of the two is painted.
The same run, with the container decoupled from the viewport — the case the two gates could never agree on:
Zero violations of the complement, and the crossover is pinned to the pixel: a 1097px container gives pane 411 and the sublabel; 1098 gives pane 412 and the columns.
Title width, measured (flat leaf, columns painted): name cell 259 / 319 / 339 at 1280 / 1440 / 1920, which is exactly
pane − 1 − 32 − 160 − 28; minus the 32px of furniture that gives 227 / 287 / 307. A 25-character title measures 154px and a 40-character title 262px in that font, so 1440 and 1920 paint a 40-character name in full and 1280 paints about 35 characters of it. Before: 67px at all three.Verification
origin/main'sGanttView.tsxover the committed fix. The mutation was proved on disk by blob hash (8e7b4d8b…=origin/mainblob, ≠ theHEADblobe0cca573…) plus two anchored controls (gantt-sm-hiddenback,START_END_COLUMNS_MIN_Wgone); the run came back 14 failed / 14 with the plugin-gantt: between 640px and 1023px the task-list rows carry NO dates at all — the Start/End columns are gated off and the date sublabel is display:none from 640px up #7224 complement red at 500 / 639 / 640 / 800 and every width pin red at 1024–1920. The restore was proved by state —git diff HEADempty and blob equality — not by an exit code, under atrap ... EXIT INT TERMwith absolute paths.packages/plugin-ganttsuite after the fix: 59 files, 471 tests, all passing (the 14 new ones included).type-checkfor the package: exit 0, after building its dependency closure first (a staledist/*.d.tslies in both directions).lintfor the package: exit 0, 0 errors.GanttView.tsxcarries 19 warnings before and 19 after, identical rule multiset — measured the same way, by linting the restored pre-fix blob. The new test file adds 0.--format json), it is the only package the diff touches (the other changed path is a.changeset/*.md, which eslint does not lint), and the config enables no type-aware linting (noparserOptions.project, noprojectService, norecommendedTypeChecked), so this diff cannot move a verdict on a file it does not contain.check:control-bytes,check:i18n-keys,check:i18n-drift,check:i18n-dead-keys,check:vi-mock-specifiers,check:vi-mock-inherit,check:readme-exports,check:shell-escape-residue,check:sdui-registration-pins,check:element-data-source-declaration.b5f659e72.Bytes — measured, not inferred
check:eager-closureis green: framework 511.5 KB / 511.7 KB ceiling (headroom 0.2 KB), vendor-objectstack 926.1 / 944.3, ui-components 387.3 / 389.6, aggregate 3178.3 KB / 3191.4 KB. No ceiling was touched.Byte-neutrality was measured rather than argued from "plugin-gantt should be lazy". The console was built on this branch and again with
GanttView.tsxrestored toorigin/main(both builds exited 0 — a failed ablation build reads a stale artifact and reports a stripped input as free):eagerGzipByteseagerRawBytesEvery one of the 48 eager chunks is identical in gzip bytes; no chunk appears on one side only. The zero is a reading, not a stale artifact: the entry chunk hash differs between the two builds (
index-DUp49asL.jsvsindex-XRCMG0Ou.js), so the two builds genuinely produced different bundles and the eager closure still weighed the same. No gantt chunk appears in the eager set at all.Deviations and notes, flagged rather than folded in
border-r. The pane's width is set inline underbox-sizing: border-box, so the true break-even is 413. 412 is implemented as ruled; the effect is that at exactly pane 412 the title gets 159px rather than the 160 the term names. One pixel, stated rather than silently corrected.0.375 × 1024 = 384 < 412), and it is a net gain — the title goes from 67px to 291px and the dates stay visible — but it is a change plugin-gantt: the task list is capped at 320px and its Start/End columns take 160px of that, so task names get 53px and truncate to ~7 characters while 864px of chart sits empty #7204's table never visits, so it is called out. The crossover is pinned in the tests at 1097/1098..gantt-sm-w20(80px) and the open-details slot (hidden sm:block) only apply from 640px up, so below that the columns cost 128 and the slot 0 — the threshold is conservative there, never permissive. Left alone: making those unconditional is a narrow-layout change nobody asked for.depth * 14is unbounded, so no single default keeps a deeply nested row legible. Stated as a known limit of container-based sizing, per the dispatch — not chased here. The tests and the source comment both say so.Test surface
packages/plugin-gantt/src/GanttView.tasklistDates-7204.test.tsx, 14 pins. Every assertion is about the DOM, never about computed style, and the file says why: the predicate under test is a render decision, which jsdom answers exactly, whereas a computed-style assertion there would confirm the wrong answer in the reassuring direction. The pixel readings that go with it were taken in Chromium and live in this PR. The suite also pins that the component's own stylesheet contains nogantt-sm-hiddenand that no surviving media block hides anything, so re-adding a viewport gate on a row's dates has to fail here whatever it gets called next time.🤖 Generated with Claude Code
https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
Generated by Claude Code