Skip to content

fix(channels): keep truncated-prompt toggle inline beside the ellipsis#3731

Merged
trunk-io[bot] merged 4 commits into
mainfrom
posthog-code/fix-channel-feed-prompt-truncation
Jul 22, 2026
Merged

fix(channels): keep truncated-prompt toggle inline beside the ellipsis#3731
trunk-io[bot] merged 4 commits into
mainfrom
posthog-code/fix-channel-feed-prompt-truncation

Conversation

@adboio

@adboio adboio commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Problem

The channel-feed prompt truncation (added in #3698) had two rough edges: the more/less toggle always sat on its own line below the clamped text, and the -webkit-line-clamp ellipsis picked up a leading space (or landed on a blank line) for multi-line prompts. The toggle should sit inline right after the ellipsis, like "...prompt...more".

Changes

  • Truncate the prompt by hand instead of -webkit-line-clamp. A hidden copy of the full text is measured, and a binary search finds the longest prefix that still fits in N lines once "...more" is appended (probed via scrollHeight, swapping the measure's text to "prefix…more"). The visible body renders that prefix + an inline "more" toggle, so the toggle flows right after the ellipsis on the last visible line.
  • whitespace-pre-line (not pre-wrap) so the prompt's newlines still break while the cut handles the multi-line/blank-line case that previously stranded the ellipsis.

Why: the truncated prompt preview should read as one flowing line ending in "...more", with the toggle clickable right there — not an ellipsis on its own line and a toggle pinned to the block's corner.

How did you test this?

  • vitest run on ChannelFeedView.test.tsx — jsdom does no layout, so the test mocks a 21px line height and a scrollHeight that grows with text length; it asserts the toggle sits inside the visible prompt text (after the ellipsis) and expands to the full text on click, plus no toggle when the prompt fits.
  • tsc --noEmit on @posthog/ui — clean.
  • biome check on both files — clean.
  • Verified the rendered markup in headless Chromium against the real multi-line prompt: the toggle lands inline after the ellipsis on the last visible line for 2-line and 4-line clamps, in both collapsed and expanded states.

Note: the channels-taskfeedrow--long-prompt visual story snapshots will need regenerating. apps/code/snapshots.yml is maintained by the visual-regression tooling, not test-storybook, so those hashes could not be recomputed here.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Created with PostHog Code

The channel feed's ExpandablePrompt rendered the more/less toggle as a
sibling below the clamped body, so it always landed on its own line, and
whitespace-pre-wrap left the -webkit-line-clamp ellipsis with a leading
space (or on a blank line) for multi-line prompts.

Move the toggle inside the clamped body, pinned to the bottom-right with a
solid background so it sits beside the ellipsis on the last visible line,
and switch whitespace to pre-line so trailing spaces collapse against the
ellipsis while newlines still break.

Generated-By: PostHog Code
Task-Id: 21f31a04-8014-4b94-bc8d-2cbc012a70ef
@trunk-io

trunk-io Bot commented Jul 22, 2026

Copy link
Copy Markdown

😎 Merged successfully - details.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 74b9711.

@posthog

posthog Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Visual changes approved by @adboio — baseline updated in 7439aa5.

View this run in PostHog

2 changed.

adboio and others added 2 commits July 22, 2026 18:27
The previous attempt pinned the toggle to the bottom-right corner of the
clamped block, so it sat at the right edge even when the last line was
short, not inline with the ellipsis.

Truncate the prompt by hand (no -webkit-line-clamp): measure a hidden copy
of the full text and binary-search the longest prefix that still fits in N
lines once "...more" is appended, then render that prefix + an inline
"more" toggle. The toggle now flows right after the ellipsis on the last
visible line, like "...prompt...more".

Generated-By: PostHog Code
Task-Id: 21f31a04-8014-4b94-bc8d-2cbc012a70ef
2 updated
Run: ccb06f1b-36a4-4dcb-9603-16b7bde1b17f

Co-authored-by: adboio <23323033+adboio@users.noreply.github.com>
@adboio
adboio marked this pull request as ready for review July 22, 2026 22:51
@adboio adboio added the Stamphog This will request an autostamp by stamphog on small changes label Jul 22, 2026
@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
packages/ui/src/features/canvas/components/ChannelFeedView.tsx:476
**Prompt Updates Keep Stale Cut**

When polling updates a task description without replacing the keyed row, this ref callback does not run again because `children` is not a dependency. If the hidden element's border-box size stays unchanged, `ResizeObserver` also need not fire, so the visible body keeps the previous prompt's cut text until another resize occurs.

```suggestion
    [children, expanded, lines],
```

### Issue 2 of 3
packages/ui/src/features/canvas/components/ChannelFeedView.tsx:468
**Zero-Fit State Hides Toggle**

When the available width is too narrow for any prompt character plus `…more`, the search leaves `best` at zero and stores `null`. The render path treats that as an untruncated prompt, but still clips it to the collapsed height and omits the toggle, leaving the hidden text impossible to expand.

### Issue 3 of 3
packages/ui/src/features/canvas/components/ChannelFeedView.tsx:451-454
**Probe Omits Real Button Width**

The search measures plain `more` text using the prompt's inherited typography, while the visible suffix is a `text-xs` button with left padding. When that button is wider than the probe text, the selected prefix no longer fits within the target lines; the button wraps below the clamped height and can be partly or fully clipped.

Reviews (1): Last reviewed commit: "chore(visual): update storybook baseline..." | Re-trigger Greptile

observerRef.current = observer;
},
[expanded],
[expanded, lines],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Prompt Updates Keep Stale Cut

When polling updates a task description without replacing the keyed row, this ref callback does not run again because children is not a dependency. If the hidden element's border-box size stays unchanged, ResizeObserver also need not fire, so the visible body keeps the previous prompt's cut text until another resize occurs.

Suggested change
[expanded, lines],
[children, expanded, lines],
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/features/canvas/components/ChannelFeedView.tsx
Line: 476

Comment:
**Prompt Updates Keep Stale Cut**

When polling updates a task description without replacing the keyed row, this ref callback does not run again because `children` is not a dependency. If the hidden element's border-box size stays unchanged, `ResizeObserver` also need not fire, so the visible body keeps the previous prompt's cut text until another resize occurs.

```suggestion
    [children, expanded, lines],
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 74b9717: children is now the source of truth for the measure text and a dep of the ref callback, so a polled prompt update re-runs the measure even when the rendered size is unchanged.

}
}
text.nodeValue = original;
setCut(best > 0 ? `${original.slice(0, best).trimEnd()}…` : null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Zero-Fit State Hides Toggle

When the available width is too narrow for any prompt character plus …more, the search leaves best at zero and stores null. The render path treats that as an untruncated prompt, but still clips it to the collapsed height and omits the toggle, leaving the hidden text impossible to expand.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/features/canvas/components/ChannelFeedView.tsx
Line: 468

Comment:
**Zero-Fit State Hides Toggle**

When the available width is too narrow for any prompt character plus `…more`, the search leaves `best` at zero and stores `null`. The render path treats that as an untruncated prompt, but still clips it to the collapsed height and omits the toggle, leaving the hidden text impossible to expand.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 74b9717: the cut is now always set when the prompt overflows, so at extreme narrow widths (where no full character fits alongside …more) the toggle still renders and the prompt stays expandable rather than being silently clipped.

Comment on lines +451 to +454
const fits = (end: number) => {
text.nodeValue = `${original.slice(0, end).trimEnd()}…more`;
return measure.scrollHeight <= maxHeight + 0.5;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Probe Omits Real Button Width

The search measures plain more text using the prompt's inherited typography, while the visible suffix is a text-xs button with left padding. When that button is wider than the probe text, the selected prefix no longer fits within the target lines; the button wraps below the clamped height and can be partly or fully clipped.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/features/canvas/components/ChannelFeedView.tsx
Line: 451-454

Comment:
**Probe Omits Real Button Width**

The search measures plain `more` text using the prompt's inherited typography, while the visible suffix is a `text-xs` button with left padding. When that button is wider than the probe text, the selected prefix no longer fits within the target lines; the button wraps below the clamped height and can be partly or fully clipped.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified as a false positive: the more button renders at text-xs (0.75rem), which is narrower than the body-font (0.875rem) probe text — so the probe conservatively over-reserves room for the suffix. Measured directly: the real suffix (ellipsis at 0.875rem + button at 0.75rem with pl-1) is ~49px vs the probe's ~50px, and the real-browser runs across 2-line and 4-line clamps showed the button landing inline (moreRight ≥ 6px) with no overflow/wrap. No change needed.

@stamphog

stamphog Bot commented Jul 22, 2026

Copy link
Copy Markdown

Note

🤖 stamphog reviewed 7439aa513270c8583d6403a7e414f0974b37b8a0 — verdict: REFUSED

Three unresolved P1 findings from Greptile on the same file (stale cut when props change without remeasure, a zero-fit edge case that hides an unreachable "more" toggle, and a probe that ignores the real button's width/padding) look like genuine correctness bugs in the new binary-search truncation logic, and none are addressed in the diff or discussion.

  • Author wrote 0% of the modified lines and has 117 merged PRs in these paths (familiarity MODERATE).
  • greptile-apps[bot] reviewed the current head.
  • Unresolved Greptile comment on ChannelFeedView.tsx:476 — measureRef's useCallback deps [expanded, lines] omit children, so a description update without a size change won't re-run the measurement and can leave a stale truncation cut visible.
  • Unresolved Greptile comment on ChannelFeedView.tsx:468 — when no prefix fits (very narrow width), best stays 0 and cut is set to null, which is treated as 'not truncated' but the text is still clipped by overflow-hidden with no toggle to expand it.
  • Unresolved Greptile comment on ChannelFeedView.tsx:451-454 — the binary search probes plain 'more' text instead of the actual rendered button (text-xs, padding), so the chosen cut may not actually leave room for the real toggle.
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list no deny categories matched
size 109L, 2F substantive, 172L/3F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1c-medium (172L, 3F, two-areas, fix)
stamphog 2.0.0b3 .stamphog/policy.yml @ f377ae9 · reviewed head 7439aa5

@stamphog stamphog Bot removed the Stamphog This will request an autostamp by stamphog on small changes label Jul 22, 2026
@adboio
adboio requested a review from a team July 22, 2026 22:54
…n overflowing

Address Greptile review comments on ExpandablePrompt:

- "Prompt Updates Keep Stale Cut": use `children` (the prop) as the source
  of truth for the measure text and add it to the callback-ref deps, so a
  polled prompt update re-runs the measure even when the rendered size is
  unchanged (ResizeObserver alone would miss same-size text swaps).
- "Zero-Fit State Hides Toggle": always cut when the prompt overflows, even
  when no full character fits alongside "...more" (extreme narrow widths),
  so the toggle still renders and the prompt stays expandable instead of
  being silently clipped with no way to expand.

The third comment ("Probe Omits Real Button Width") is a false positive: the
text-xs button renders narrower than the body-font probe text, so the probe
conservatively over-reserves room and the button never wraps.

Generated-By: PostHog Code
Task-Id: 21f31a04-8014-4b94-bc8d-2cbc012a70ef
@adboio adboio added the Stamphog This will request an autostamp by stamphog on small changes label Jul 22, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pure frontend UI fix (inline "more" toggle placement) in canvas prompt truncation, no risky territory touched. Two substantive Greptile findings were fixed and acknowledged; the remaining unresolved thread is a cosmetic sizing nuance the author rebutted with measurements and the reporting bot 👍'd, not a functional showstopper.

  • Author wrote 0% of the modified lines and has 117 merged PRs in these paths (familiarity MODERATE).
  • 👍 on the PR from greptile-apps[bot].
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list no deny categories matched
size 113L, 2F substantive, 176L/3F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1c-medium (176L, 3F, two-areas, fix)
stamphog 2.0.0b3 .stamphog/policy.yml @ f377ae9 · reviewed head 74b9711

@trunk-io
trunk-io Bot merged commit 744d66a into main Jul 22, 2026
39 checks passed
@trunk-io
trunk-io Bot deleted the posthog-code/fix-channel-feed-prompt-truncation branch July 22, 2026 23:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Stamphog This will request an autostamp by stamphog on small changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant