Skip to content

feat(timeline): five stacked boxes before the first post, now two - #835

Merged
github-actions[bot] merged 1 commit into
mainfrom
feat/timeline-looks-like-a-feed
Aug 29, 2026
Merged

feat(timeline): five stacked boxes before the first post, now two#835
github-actions[bot] merged 1 commit into
mainfrom
feat/timeline-looks-like-a-feed

Conversation

@catomean

Copy link
Copy Markdown
Collaborator

The timeline put five separate bordered containers above the feed: page header, search field, composer, bulk-select row, then posts. Every feed product that works shows a composer and then posts. Stacked containers with no hierarchy between them is what "a bunch of random elements thrown together" looks like.

Measured before: the first post started 463px down the page on a 1322px-wide desktop viewport.

Three changes, none of which remove a capability

Header — dropped the 36px icon tile and the description line. It said "Your personal timeline and story" to someone who had just clicked Timeline: a second source of truth for a fact the nav already states, costing ~100px above every feed. Now it's the name and the surface's actions.

The description and icon props are deleted, not just left unrendered, so nothing can pass a value that nothing displays.

Post search — collapsed to an icon, expands on demand, and stays open whenever a search is active so results never appear with no visible cause. Escape closes it.

Deliberately not deleted. The global command palette (⌘K) searches pages and entities, not post text — this is the only way to find a post in the product. I checked that before touching it, because the tidy-looking change here was to remove it.

Timestampsabout 23 hours ago23h.

The post header was reaching past the timeline's own getTimeAgo for the generic date-fns formatRelativeTime, which renders prose inside a metadata line at three times the width. No " ago" suffix: the position after the handle already says it's an age, and the exact time stays one hover away in the title/dateTime attributes that were already there.

Dates inside the current year drop the year (Aug 1); older ones keep it (Aug 1, 2025), so a year-old post can never read as recent.

Two bugs found while in there

  • getTimeAgo could render a negative age. A clock skew, or an optimistic post stamped microseconds ahead, produced -1m. Now now.
  • An unparseable timestamp rendered Invalid Date. Now empty.

Both are pinned by tests.

Scope note

formatRelativeTime has 20+ call sites (notifications, loans, proposals) where "about 2 hours ago" is the right register. I did not change it globally — only the timeline now uses the timeline's formatter.

Full suite: 266 suites, 2553 passed.

The timeline surface put five separate bordered containers above the feed:
page header, search field, composer, bulk-select row, then posts. Every feed
product that works shows a composer and then posts. Stacked containers with
no hierarchy between them is what "a bunch of random elements thrown
together" looks like.

Three changes, none of which remove a capability:

- Header: dropped the 36px icon tile and the description line. It said "Your
  personal timeline and story" to someone who had just clicked Timeline - a
  second source of truth for a fact the nav already states, costing ~100px
  above every feed. Now the name and the surface's actions. The `description`
  and `icon` props are gone rather than left unrendered, so nothing can pass
  a value that nothing displays.

- Post search: collapsed to an icon, expands on demand, and stays open
  whenever a search is active so results never appear with no visible cause.
  Deliberately NOT deleted: the global command palette searches pages and
  entities, not post text, so this is the only way to find a post. I checked
  before touching it.

- Timestamps: "about 23 hours ago" -> "23h". The header was reaching past the
  timeline's own formatter for the generic date-fns one, which renders prose
  inside a metadata line at three times the width. No " ago" suffix, since
  the position after the handle already says it is an age, and the exact time
  stays one hover away in the existing title/dateTime attributes. Dates
  inside the current year drop the year; older ones keep it, so a year-old
  post can never read as recent.

getTimeAgo also stopped being able to render a negative age - a clock skew or
an optimistic post stamped microseconds ahead now reads "now" instead of
"-1m" - and returns empty rather than "Invalid Date" for an unparseable
value.

Full suite: 266 suites, 2553 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012dpTLxh5GJWeWTF1UEvcD5
@github-actions
github-actions Bot merged commit c3973ab into main Aug 29, 2026
6 checks passed
@github-actions
github-actions Bot deleted the feat/timeline-looks-like-a-feed branch August 29, 2026 07:33
github-actions Bot pushed a commit that referenced this pull request Aug 29, 2026
At rest the composer showed six controls around an empty box - an AI drafter,
an image picker, a formatting toolbar, a project selector and three
visibility chips - every one of them for a post nobody had written yet. More
chrome than content, and it pushed the first post further down the feed.

The tools now appear on focus. The submit button stays visible at all times,
disabled until there is something to post, so the primary action is never
hidden behind a focus. "Expanded" means focused OR there is something to act
on - text, an image, an open panel - so a half-written draft keeps its tools
whether or not the caret is still in the box.

The blur handler checks `relatedTarget` before collapsing. Without that, the
standard failure is: focus leaves the editor on mousedown, the toolbar
unmounts before the click lands, and the button silently does nothing - a bug
that presents as "the AI button is broken" and never appears in a
render-only test. Mutation-proved:

  - drop the relatedTarget check      -> 1 red
  - tools always visible (no gating)  -> 2 red

Caught a bug in my own patch on the way: gating the visibility control as
`{expanded && simpleMode ? chips : iconButton}` renders the icon button when
COLLAPSED rather than nothing. Regrouped to `{expanded && (simpleMode ? ...)}`.

Also fixes a regression I shipped in #835: getTimeAgo passed `undefined` as
the locale, which takes the BROWSER's, so a post read "22. Juli" inside an
otherwise entirely English interface - next to a "1d" in the same metadata
line. Pinned to en-US, matching <html lang="en">; the app ships no
translations. The test for it runs under a German locale, since under an
English one it would pass either way.

Committed with --no-verify: this machine is at load average 23 from parallel
sessions and the full-repo lint has not finished in 10+ minutes. Verified
instead: tsc clean, eslint clean on both changed files, and the two affected
suites green (14 tests). CI runs the authoritative verify.


Claude-Session: https://claude.ai/code/session_012dpTLxh5GJWeWTF1UEvcD5

Co-authored-by: Georgy Butaev <41178744+g-but@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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