Skip to content

Re-vendor the browser bundle: resize-aware summary clamp, and no stranded skeleton - #49

Merged
jeremyandrews merged 1 commit into
mainfrom
p03/summary-clamp-resize
Aug 2, 2026
Merged

Re-vendor the browser bundle: resize-aware summary clamp, and no stranded skeleton#49
jeremyandrews merged 1 commit into
mainfrom
p03/summary-clamp-resize

Conversation

@jeremyandrews

Copy link
Copy Markdown
Member

Re-vendors src/scolta/assets/js/scolta.js from canonical to pick up tag1consulting/scolta-php#269, two follow-ups to the AI-summary layout reservation. scolta.css is unchanged; that fix was JS only.

1. The clamp decision was frozen at resolve-time width

Whether to clamp the summary and offer Show more is a measurement (scrollHeight against clientHeight), and it ran only on resolve and on a toggle click. There was no resize listener in the bundle at all, so the decision was fixed at the width the summary happened to land in.

Rotate a phone to portrait, or shrink a responsive column, and a summary that fitted reflows to more lines and overflows the reserved height. The text is still clipped, but with no clamped class there is no fade and the control stays hidden, so a sighted reader saw text cut off at the box edge with no on-screen way to open it. The full text is in the DOM throughout, so find-in-page and assistive tech were never affected; the visible affordance was. Widening had the mirror problem, leaving a pointless control on a summary that now fits.

A feature-detected ResizeObserver on the summary text region now recomputes it: exactly one live at a time, disconnected when the slot is released and when the user expands the summary (not fighting that choice), re-established on collapse, and never installed when the AI summary is off.

The recompute moves nothing. It toggles a mask class and the control's hidden flag inside the fixed-height overflow: hidden panel, and the reserved height derives from a line count and a font size rather than the width, so the box measures the same at every width: 297.03px at 1280px wide and at 420px wide, control hidden at the first and visible at the second.

2. A pre-request throw stranded the loading skeleton

The search flow's expansion chain ends with an un-awaited summarizeResults(...), which is correct in itself, but nothing was chained onto the promise it returns. That function handles its own fetch failures; the work before the fetch does not, and buildLLMContext() dereferences r.data.meta unguarded. Because the function is async, such a throw becomes a rejected promise rather than a synchronous error, so the chain's own trailing .catch never saw it. With the slot reserved from the frame the results paint, the consequence was a skeleton shimmering permanently with no way back. The call now carries its own .catch, guarded on the search version so an abandoned cycle cannot collapse a newer search's slot.

Python side

Nothing changed. Both fixes are browser-side behaviour with no config key, no schema, no serialized setting, no indexer involvement and no index rebuild. Django and Wagtail inherit this through their scolta dependency and need no change of their own.

No new tests were mirrored into tests/js/. That suite is a 14-file subset rather than a strict mirror of scolta-php's 26, and it carries none of the summary, SAYT or render-seam suites; the new coverage lives with the code it tests, upstream.

Verification

  • python scripts/vendor_assets.py
  • cmp src/scolta/assets/js/scolta.js ../scolta-php/assets/js/scolta.js — identical
  • cmp src/scolta/assets/css/scolta.css ../scolta-php/assets/css/scolta.css — identical (unchanged this round)
  • python scripts/validate-dist.py — passed (wheel 770,590 bytes; sdist 4,343,094 bytes)
  • pytest — 808 passed
  • tests/js Jest subset — 283 passed, 1 skipped, across 14 suites

Vendored from scolta-php main at 07fddae, canonical scolta.js SHA-256 240d3b9c….

…nded skeleton

Picks up scolta-php#269, two follow-ups to the layout reservation.

The clamp decision is a measurement, and it ran only on resolve and on a
toggle click, so it froze at the width the summary landed in. Narrow the
column afterwards and a summary that fitted reflows past the reserved
height: still clipped, but with no fade and no control, so a sighted
reader saw text cut off with no way to open it. A feature-detected
ResizeObserver on the text region now recomputes it, one live at a time,
disconnected on release and on expand, re-established on collapse, and
never installed when the summary is off. The box measures the same at
every width, so the reveal moves nothing.

Separately, the un-awaited summarizeResults() call had nothing chained
onto it, so a throw before its fetch became a rejected promise the
expansion chain's catch never saw, stranding the reserved skeleton. It
now carries its own version-guarded catch.

No Python-side change. Copied byte-identically with
scripts/vendor_assets.py, verified with cmp.
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Summary

Re-vendors the browser bundle scolta.js from scolta-php to fix two AI summary UI bugs: a ResizeObserver-based clamp recalculation so the "Show more" control stays correct across viewport width changes, and a chained .catch on the previously un-awaited summarizeResults() call so a failure there no longer strands the loading skeleton indefinitely.

Type: bugfix
Effort: 2/5 — small, self-contained JS change plus changelog entry, byte-identical vendoring with no build/schema impact

Walkthrough

File Change Summary
src/scolta/assets/js/scolta.js Modified Adds ResizeObserver-driven summary clamp recompute and a guarded .catch on the un-awaited summarizeResults() call
CHANGELOG.md Modified Documents the two fixes and the re-vendoring from scolta-php

Related Issues & PRs


AI Review Summary — generated by ai-pr-review

@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.

AI Review: Approved

No findings above the confidence threshold. The changes look good.

🔵 Out-of-diff analyzer findings (2) — pre-existing issues on unchanged lines, capped to Low
  • 🔵 [Low] [F1] [semgrep] javascript.lang.security.audit.detect-non-literal-regexp.detect-non-literal-regexp: RegExp() called with a query function argument, this might allow an attacker to cause a Regular Expression Denial-of-Service (ReDoS) within your application as RegExP blocks the main thread. For this reason, it is recommended to use hardcoded regexes instead. If your regex is run on user-controlled input, consider performing input validation or use a regex checking/sanitization library such as https://www.npmjs.com/package/recheck to verify that the regex does not appear vulnerable to ReDoS. (at src/scolta/assets/js/scolta.js:1025 (line not in diff))
  • 🔵 [Low] [F2] [semgrep] javascript.lang.security.audit.detect-non-literal-regexp.detect-non-literal-regexp: RegExp() called with a query function argument, this might allow an attacker to cause a Regular Expression Denial-of-Service (ReDoS) within your application as RegExP blocks the main thread. For this reason, it is recommended to use hardcoded regexes instead. If your regex is run on user-controlled input, consider performing input validation or use a regex checking/sanitization library such as https://www.npmjs.com/package/recheck to verify that the regex does not appear vulnerable to ReDoS. (at src/scolta/assets/js/scolta.js:1044 (line not in diff))
Token usage by agent
Agent Model Input Output Cache Write Cache Read Total Est. Cost
code-reviewer Sonnet 5 1 18 / 16384 20522 1195 21736 $0.0775
silent-failure-hunter Sonnet 5 1 693 / 16384 20488 0 21182 $0.0872
judge-pass Sonnet 5 1646 119 0 0 1765 $0.0067
Total 1648 830 41010 1195 44683 $0.1714
Context enrichment (context) 8195
Language profiles (profile) 857

AI Review — generated by ai-pr-review

@jeremyandrews
jeremyandrews merged commit c5128e6 into main Aug 2, 2026
8 checks passed
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