Skip to content

Midterms dashboard: race summary, seat medians, engagement block, insight cards - #5114

Draft
aseckin wants to merge 5 commits into
mainfrom
2026-midterms-v2
Draft

Midterms dashboard: race summary, seat medians, engagement block, insight cards#5114
aseckin wants to merge 5 commits into
mainfrom
2026-midterms-v2

Conversation

@aseckin

@aseckin aseckin commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Five improvements to the /midterms-2026 hub.

1. Governor-tab race summary

A top-line read on the races — "10 of 19 races lean Democrat" — sitting between the chamber tabs and the legend. The count and party name are party-colored and follow whichever side leads, so this flips to red on its own if forecasts move.

The tabs and legend used to be two independent corner overlays; they're now one absolutely-positioned header row with the summary between them. Because the row is absolute, a summary that wraps grows over the map instead of pushing it down — verified the map's svgTop is byte-identical across both tabs at 1024px and 1440px, including with a forced 3-line string.

Lean threshold is demWinPct >= 50, the same split state_tooltip.tsx already uses, so the summary can never disagree with a tooltip. Governor tab only.

2. Seat-distribution medians

Both charts now surface a median: a dot on the x-axis at the median bin carrying an always-on flyout, using the same VictoryTooltip shape as the hover tooltips. computeQuartilesFromCDF was already running every render and its result was being thrown away — the quartile lines that consumed it are gated behind !isDiscrete, and both questions became Discrete in e82e6a9.

  • Hides while a bin is hovered, so it never stacks with the hover tooltip.
  • Suppressed entirely when the median is exactly 0. The EVEN bin already owns that x-position, and labelling both there invites reading them as the same claim — they aren't. The EVEN bar's height is P(seat advantage = 0); the median is where the CDF crosses 50%. They coincide on Senate today only because the community's median forecast genuinely is a tied Senate. The callout returns on its own as soon as the median moves off zero.
  • Flyout inks key off the flyout's own background rather than the page theme — the light page's black flyout needs the swatches designed for dark surfaces, and vice versa.
  • Hover flyouts to 90% opacity, median to 80%, both +1px padding-top and ~2px more gap between lines.

3. "Today:" baseline

Chamber control's pre-midterm seat counts read Today: instead of Current: and drop their party colors for gray, so the static baseline reads as context rather than competing with the live forecast directly above it. Also fixes a latent dark-mode bug: those two spans hardcoded light-mode hexes with no dark variant.

4. Community Insight cards

Cards now carry the question they're about. Three zones: question-title strip → comment as the visual hero → author footer. Height is capped rather than fixed, so a short comment lets the card shrink; the title strip wraps to a second line only when it needs to.

Rewritten as a self-contained <Link> rather than wrapping ActivityCard, which renders username and subtitle together at the top and forces pr-12 for its corner arrow — both fight this layout, and a 340px card can't spare 48px of right padding once the comment is the hero. ActivityCard is untouched, so the Labor Hub is unaffected.

5. Engagement block

The Labor Hub's three cards (contact / share / newsletter) carried over with midterms copy and its own Mailjet list, slotted between Community Insights and the footer disclaimer. NewsletterSubscribeCard gained an optional copy prop defaulting to its current English strings, so the Labor Hub renders unchanged while Midterms gets translated copy.

All new strings are in en, es, cs, pt, zh, zh-TW, translated rather than English-filled, reusing the existing contactUs / copyLink / emailAddress keys. Seat counts use ICU plurals so +1 doesn't read "1 seats".


Worth a reviewer's attention

A Victory footgun. victory-voronoi-container's handleMouseMove calls onActivated(newPoints) and then onDeactivated(previousPoints) back-to-back in the same tick whenever the active bin changes. An unconditional onDeactivated handler therefore undoes every activation and the state never sticks. Guarded with a ref that lets only the genuine clears (out-of-bounds, pointer off the container) through. Caught this because the median visibly failed to hide on hover.

The median marker must stay in voronoiBlacklist. The container is voronoiDimension="x", so without it the marker becomes its own hover target and renders a second, wrong tooltip.

Verification

bun run lint (eslint + tsc) exits 0 with 165 warnings — the same count as before these changes, so no new warnings. bun run format clean.

Manually verified against the dev server in both light and dark mode: median inks invert correctly against the flyout, hover gating survives moving between bins and restores on exit, Senate keeps EVEN with no median while House keeps its callout, all 14 insight cards land within the height cap with zero text clipping, and the summary wraps without shifting the map.

Out of scope

Noticed while testing: the page scrolls ~74px horizontally at 1024px width. The cause is the global site footer (gap-[122px] sm:gap-[218px]), outside <main> and unrelated to this work — worth a separate fix.

Summary by CodeRabbit

  • New Features

    • Added interactive House, Senate, and overall Congress control forecast timelines.
    • Added race-lean summaries for Senate and governor races.
    • Added median seat indicators to seat distribution charts.
    • Added engagement options, including contact, sharing, link copying, and newsletter signup.
    • Improved map layouts and insight cards for clearer election information.
  • Enhancements

    • Added localized messaging for timeline availability, forecasts, subscriptions, and engagement actions across supported languages.
    • Updated chamber status labels to display “Today.”

…ight cards

Five changes to the /midterms-2026 hub:

- Governor tab gets a top-line summary ("10 of 19 races lean Democrat"),
  rendered in a new absolutely-positioned header row shared with the chamber
  tabs and legend so a wrapping summary never pushes the map down. Lean split
  is >= 50%, matching the state tooltip so the two can't disagree.
- Both seat-distribution charts gain a median callout: a dot on the x-axis at
  the median bin carrying an always-on VictoryTooltip. It hides while a bin is
  hovered so it never stacks with the hover tooltip, and is suppressed entirely
  when the median is exactly 0 (the EVEN bin already owns that spot). Hover
  flyouts go to 90% opacity, the median's to 80%, both +1px padding-top and
  ~2px more gap between lines.
- Chamber control's pre-midterm baseline reads "Today:" instead of "Current:"
  and drops its party colors for gray, so it reads as context rather than
  competing with the live forecast above it. Also fixes an existing dark-mode
  bug: those two spans hardcoded light-mode hexes with no dark variant.
- Community Insight cards carry their question title in a header strip, with
  the comment as the visual hero and the author as a footer. Height is capped
  rather than fixed, so short comments let the card shrink.
- Adds the Labor Hub's three engagement cards (contact / share / newsletter)
  with midterms copy and its own Mailjet list. NewsletterSubscribeCard gained
  an optional `copy` prop defaulting to its current English, so the Labor Hub
  renders unchanged.

Median-marker note: Victory's voronoi handleMouseMove calls onActivated(new)
then onDeactivated(previous) back-to-back in the same tick, so an unconditional
onDeactivated handler silently undoes every activation. Guarded with a ref.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4f2484e1-f7db-4437-8745-a8e8454118fb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The midterms hub adds forecast timelines, race-lean and seat-median summaries, updated map and insight cards, localized messages, and contact, sharing, and newsletter engagement controls.

Changes

Midterms forecasting and summaries

Layer / File(s) Summary
Forecast timeline rendering
front_end/src/app/(main)/midterms-2026/helpers/build_control_timelines.ts, front_end/src/app/(main)/midterms-2026/components/balance_of_power_timelines.tsx, front_end/src/components/charts/group_chart.tsx
Control forecast histories now produce interactive House, Senate, and Congress timelines with legends and unavailable states.
Election summaries and chart callouts
front_end/src/app/(main)/midterms-2026/components/race_lean_summary.tsx, front_end/src/app/(main)/midterms-2026/components/seat_distribution_chart.tsx, front_end/src/app/(main)/midterms-2026/components/geographic_map.tsx, front_end/messages/*.json
The hub displays race-lean summaries, median seat labels, updated chamber labels, and localized timeline messages.
Election layout integration
front_end/src/app/(main)/midterms-2026/sections/elections_map_section.tsx, front_end/src/app/(main)/midterms-2026/components/responsive_map.tsx
The election section uses balance-of-power timelines and places governor race summaries in desktop and mobile map layouts.
Community insight cards
front_end/src/app/(main)/midterms-2026/components/insight_card.tsx
Insight cards now use custom linked markup with question context, author metadata, clamped content, and longer comment excerpts.

Engagement and newsletter

Layer / File(s) Summary
Engagement and newsletter actions
front_end/src/app/(main)/midterms-2026/sections/engagement_section.tsx, front_end/src/app/(main)/midterms-2026/page.tsx, front_end/src/app/(main)/actions.ts
The midterms page adds partner, contact, sharing, and newsletter actions using the midterms newsletter list.
Localized newsletter copy
front_end/src/app/(main)/labor-hub/components/newsletter_subscribe_card.tsx
NewsletterSubscribeCard accepts partial localized copy and uses it for labels, placeholders, confirmations, and toast messages.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MidtermsPage
  participant ElectionsMapSection
  participant BalanceOfPowerTimelines
  participant buildControlTimeline
  participant GroupChart
  MidtermsPage->>ElectionsMapSection: render election section
  ElectionsMapSection->>BalanceOfPowerTimelines: provide congressOutcome
  BalanceOfPowerTimelines->>buildControlTimeline: build forecast timelines
  buildControlTimeline-->>BalanceOfPowerTimelines: return chart series
  BalanceOfPowerTimelines->>GroupChart: render interactive timelines
Loading

Possibly related PRs

Suggested reviewers: ncarazon

Poem

I’m a rabbit with charts in my den,
Forecast lines hop forward again.
Seat medians glow,
Race leans now show,
And newsletters gather new friends.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main dashboard changes, including race summaries, seat medians, engagement content, and redesigned insight cards.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 2026-midterms-v2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

🚀 Preview Environment

Your preview environment is ready!

Resource Details
🌐 Preview URL https://metaculus-pr-5114-2026-midterms-v2-preview.mtcl.cc
📦 Docker Image ghcr.io/metaculus/metaculus:2026-midterms-v2-14889a9
🗄️ PostgreSQL NeonDB branch preview/pr-5114-2026-midterms-v2
Redis Fly Redis mtc-redis-pr-5114-2026-midterms-v2

Details

  • Commit: c356242d52ee11bfb725502214ffa2b46c4901ae
  • Branch: 2026-midterms-v2
  • Fly App: metaculus-pr-5114-2026-midterms-v2

ℹ️ Preview Environment Info

Isolation:

  • PostgreSQL and Redis are fully isolated from production
  • Each PR gets its own database branch and Redis instance
  • Changes pushed to this PR will trigger a new deployment

Limitations:

  • Background workers and cron jobs are not deployed in preview environments
  • If you need to test background jobs, use Heroku staging environments

Cleanup:

  • This preview will be automatically destroyed when the PR is closed

The hub's right panel showed only point-in-time numbers, so a reader couldn't
tell whether "Democrats 86.3% to hold the House" was a sharp move or had been
flat for months. The panel now shows three community-prediction timelines —
House, Senate, and the four-way congressional outcome — covering ~18 months of
history (333 aggregation points back to Jan 2025).

No new fetching: getPostsWithCP already sets include_cp_history, so the history
for #34484 was arriving and being discarded.

- helpers/build_control_timelines.ts turns that history into ChoiceItem series,
  summing options per series. House = DD+RD / RR+DR and Senate = DD+DR / RR+RD,
  the same sums chamber_control_card.tsx uses, so each line's last point lands
  exactly on the percentage the cards showed (verified: 86.3/13.7, 37.3/62.7).
  Deliberately not built on generateChoiceItemsFromMultipleChoiceForecast, which
  routes through getAllOptionsHistory — that returns only the last entry of
  question.options when options_history is absent and would silently collapse a
  four-option question to one series.
- Rendered with the shared GroupChart: individual lines, a dot pinned to each
  endpoint that tracks the cursor on hover, and no axes. Values live in the
  legend instead, showing the current figure at rest and the hovered point's
  figure while the cursor is in the plot.
- GroupChart gains additive hideYAxis / hideXAxis props (both default false, so
  no existing call site changes). Its axis tick styles are set inline on the
  VictoryAxis and can't be reached via extraTheme, hence props rather than a
  theme override. hideXAxis makes permanent what the component already did while
  the cursor was active; pair it with showCursorLabel so the hovered date is the
  only x value ever shown.
- The four congressional outcomes use MULTIPLE_CHOICE_COLOR_SCALE; two blues and
  two reds were indistinguishable as four lines.
- Layout: one rule divides the map from the panel and runs the full card height,
  and the map column now carries the same padding as the panel so it's inset
  equally on all sides rather than butting against the rule.

The snapshot cards (chamber_control_card, congress_outcome_card) are no longer
rendered but are left in place unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@aseckin
aseckin deployed to testing_env August 12, 2026 06:57 — with GitHub Actions Active
@aseckin
aseckin deployed to testing_env August 12, 2026 06:57 — with GitHub Actions Active
blue-300 read heavier than the rest of the section's chrome now that it's the
only structural line in the card.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@aseckin
aseckin deployed to testing_env August 12, 2026 09:43 — with GitHub Actions Active
@aseckin
aseckin deployed to testing_env August 12, 2026 09:43 — with GitHub Actions Active

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@front_end/messages/en.json`:
- Around line 2394-2395: Update the plural wording in
midtermsHubSeatAdvantageTooltip and midtermsHubSeatAdvantageOverTooltip so
counts above one render “seats advantage” instead of “seat advantage,” while
preserving the existing count interpolation and greater-than prefix.

In
`@front_end/src/app/`(main)/midterms-2026/components/balance_of_power_timelines.tsx:
- Around line 214-215: Update the percentage rendering in the balance-of-power
timeline component to use useLocale() and Intl.NumberFormat with style "percent"
and one minimum and maximum fraction digit. Preserve the existing em-dash for
null values, and format non-null values through the locale-aware formatter so
locales such as Spanish use the correct decimal and spacing conventions.

In `@front_end/src/app/`(main)/midterms-2026/components/insight_card.tsx:
- Around line 17-19: Update the excerpt generation in InsightCard, including the
related logic at the alternate excerpt usage, so the EXCERPT_CHARS limit is
applied at the last whitespace boundary at or before the limit rather than
slicing through a word. Preserve the existing line-clamp-6 rendering and
fallback behavior.
- Around line 33-36: Update the Link in insight_card to provide a localized
screen-reader-only cue announcing that it opens in a new tab, using
useTranslations() rather than hardcoded English; preserve the existing
target="_blank" behavior and ensure the cue is included in the link’s accessible
name.

In `@front_end/src/app/`(main)/midterms-2026/helpers/build_control_timelines.ts:
- Around line 70-73: Update the timeline-building flow around the timestamp loop
and findCoveringForecast so forecast coverage is computed once using a sorted,
monotonic cursor over history rather than calling history.find per timestamp and
series. Reuse the resulting timestamp-to-forecast mapping for sumCenters and
forecaster counts across all series, preserving existing aggregation values and
fallback behavior.

In `@front_end/src/app/`(main)/midterms-2026/sections/engagement_section.tsx:
- Line 58: Extend useCopyUrl to accept caller-provided localized success and
failure messages, displaying both through the existing toast/status feedback
path instead of relying on its hardcoded English success text and logging-only
failure handling. In engagement_section.tsx, update the useCopyUrl call to pass
the appropriate translated success and failure strings from the available i18n
source.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ad9ec80-9eac-4267-81d2-8806164bc92c

📥 Commits

Reviewing files that changed from the base of the PR and between 5a2a941 and 3cabf0d.

📒 Files selected for processing (21)
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
  • front_end/src/app/(main)/actions.ts
  • front_end/src/app/(main)/labor-hub/components/newsletter_subscribe_card.tsx
  • front_end/src/app/(main)/midterms-2026/components/balance_of_power_timelines.tsx
  • front_end/src/app/(main)/midterms-2026/components/chamber_control_card.tsx
  • front_end/src/app/(main)/midterms-2026/components/geographic_map.tsx
  • front_end/src/app/(main)/midterms-2026/components/insight_card.tsx
  • front_end/src/app/(main)/midterms-2026/components/race_lean_summary.tsx
  • front_end/src/app/(main)/midterms-2026/components/responsive_map.tsx
  • front_end/src/app/(main)/midterms-2026/components/seat_distribution_chart.tsx
  • front_end/src/app/(main)/midterms-2026/helpers/build_control_timelines.ts
  • front_end/src/app/(main)/midterms-2026/helpers/post_utils.ts
  • front_end/src/app/(main)/midterms-2026/page.tsx
  • front_end/src/app/(main)/midterms-2026/sections/elections_map_section.tsx
  • front_end/src/app/(main)/midterms-2026/sections/engagement_section.tsx
  • front_end/src/components/charts/group_chart.tsx

Comment thread front_end/messages/en.json
Comment thread front_end/src/app/(main)/midterms-2026/components/insight_card.tsx
Comment on lines +33 to +36
<Link
href={`/questions/${sourcePost.id}/#comment-${comment.id}`}
target="_blank"
rel="noreferrer"

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Announce the new-tab behavior accessibly.

target="_blank" opens the question in a new tab, but the link has no accessible text that announces this behavior. If opening a new tab is intentional, add a localized screen-reader-only cue. Otherwise, remove target="_blank".

Based on learnings, use useTranslations() for the cue instead of hardcoding English in TSX.

Suggested fix
     <Link
       href={`/questions/${sourcePost.id}/#comment-${comment.id}`}
       target="_blank"
       rel="noreferrer"
     >
+      <span className="sr-only">{t("midtermsHubOpensInNewTab")}</span>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Link
href={`/questions/${sourcePost.id}/#comment-${comment.id}`}
target="_blank"
rel="noreferrer"
<Link
href={`/questions/${sourcePost.id}/#comment-${comment.id}`}
target="_blank"
rel="noreferrer"
>
<span className="sr-only">{t("midtermsHubOpensInNewTab")}</span>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@front_end/src/app/`(main)/midterms-2026/components/insight_card.tsx around
lines 33 - 36, Update the Link in insight_card to provide a localized
screen-reader-only cue announcing that it opens in a new tab, using
useTranslations() rather than hardcoded English; preserve the existing
target="_blank" behavior and ensure the cue is included in the link’s accessible
name.

Source: Learnings

Comment on lines +70 to +73
for (const timestamp of timestamps) {
const entry = findCoveringForecast(history, timestamp);
aggregationValues.push(sumCenters(entry, optionIndices));
aggregationForecasterCounts.push(entry?.forecaster_count ?? 0);

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.

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Remove the repeated history scans.

Line 71 performs a linear history.find() for every timestamp and every series. The three timelines create eight series, so this becomes O(series × N²). Long aggregation histories can block rendering in the sidebar.

Build the timestamp-to-forecast coverage once with a sorted monotonic cursor. Reuse that result for each series.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@front_end/src/app/`(main)/midterms-2026/helpers/build_control_timelines.ts
around lines 70 - 73, Update the timeline-building flow around the timestamp
loop and findCoveringForecast so forecast coverage is computed once using a
sorted, monotonic cursor over history rather than calling history.find per
timestamp and series. Reuse the resulting timestamp-to-forecast mapping for
sumCenters and forecaster counts across all series, preserving existing
aggregation values and fallback behavior.


function ShareCard() {
const t = useTranslations();
const copyUrl = useCopyUrl({ includeHash: false });

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Provide localized clipboard status feedback.

useCopyUrl() emits an English success toast and only logs clipboard failures. This leaves the new localized share card with an untranslated success message and no failure message.

Extend the hook with caller-provided status messages, then pass translated success and failure text at Line 58.

Based on learnings: prefer i18n strings for UI text.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@front_end/src/app/`(main)/midterms-2026/sections/engagement_section.tsx at
line 58, Extend useCopyUrl to accept caller-provided localized success and
failure messages, displaying both through the existing toast/status feedback
path instead of relying on its hardcoded English success text and logging-only
failure handling. In engagement_section.tsx, update the useCopyUrl call to pass
the appropriate translated success and failure strings from the available i18n
source.

Source: Learnings

Atakan Seckin and others added 2 commits August 12, 2026 12:13
…lized copy toast

- Insight cards navigate in the current tab instead of opening a new one. Their
  corner glyph changes from the open-in-new-window arrow to a plain right arrow,
  which is what the link now actually does.

- build_control_timelines resolves each timestamp's covering forecast window once
  with a monotonic cursor and shares the result across every series, replacing a
  history.find scan run per timestamp per series. Both sequences ascend, so this
  is a single pass rather than series x timestamps x history. Verified
  behavior-identical against the previous implementation: 3 charts x 7 sample
  indices x 8 series, plus timestamps, labels and forecaster counts — zero
  mismatches, zero nulls across 333 points.

  It assumes non-overlapping aggregation windows, which is how CP history is
  built; the tradeoff is noted in the helper's comment.

- useCopyUrl accepts optional localized success/failure messages. It previously
  hardcoded an English success toast and dropped failures into console.error, so
  on the fully-translated midterms hub "Copy Link" showed English, and a denied
  clipboard write left the button looking broken. Both defaults are unchanged, so
  the other three callers behave exactly as before.

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