Skip to content

feat: show alternatives banner on unavailable book pages - #12910

Merged
mekarpeles merged 17 commits into
internetarchive:masterfrom
Sadashii:feat/12743/recommend-available-alternatives
Jul 16, 2026
Merged

feat: show alternatives banner on unavailable book pages#12910
mekarpeles merged 17 commits into
internetarchive:masterfrom
Sadashii:feat/12743/recommend-available-alternatives

Conversation

@Sadashii

@Sadashii Sadashii commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Closes #12743

This PR adds a warning banner recommending available alternative books on the Book Edition page when a book is in preview_only, checkedout, waitlist, or locate physical states.

Technical

  • Extracted availability state resolution logic to a new @public function get_lending_state in lending.py.
  • Registered the helper as a template global in code.py.
  • Added <ol-banner> inside the EditionNavBar macro, wrapped within a responsive div using .desktop-only / .mobile-only CSS utility classes to avoid layout conflicts.
  • Simplified view.html layout by delegating the banner rendering to the navbar component, and removed the unused data-workid carousel attribute.
  • Refactored get_pending_action_banner in mybooks.py and updated AGENTS.md to follow i18n best practices (combining localized segments into single formatted strings with placeholders).
  • Implemented unit tests covering all lending states in test_lending.py.
  • Added data-lending-state attribute to #contentBody and visibility/tracking hooks in lazy-carousel.js to instrument discovery of recommended books.

Analytics & Experiment Tracking

Added tracking for the discovery of the recommended books carousel under the category OpenRelatedBooks to compare natural discovery vs banner-driven discovery:

  • OpenRelatedBooks|BannerClick: Tracked when a user clicks the link on the warning banner to jump to the recommendations.
  • OpenRelatedBooks|FromBanner: Tracked when the related books carousel lazy-loads/is discovered after the user clicked the banner link (URL hash is #related-work-carousel).
  • OpenRelatedBooks|ScrolledDownAvailable: Tracked when the related books carousel lazy-loads/is discovered by scrolling down normally when the book is available.
  • OpenRelatedBooks|ScrolledDownUnavailable: Tracked when the related books carousel lazy-loads/is discovered by scrolling down normally when the book is unavailable (in one of the 4 warning states).

Testing

  1. Run python unit tests:
    uv run --with-requirements requirements_test.txt pytest openlibrary/tests/core/test_lending.py
  2. Run template compilation tests:
    uv run --with-requirements requirements_test.txt pytest openlibrary/tests/test_templates.py
  3. Verify pre-commit checks run successfully:
    pre-commit run --files openlibrary/core/lending.py openlibrary/plugins/openlibrary/code.py openlibrary/templates/type/edition/view.html openlibrary/tests/core/test_lending.py openlibrary/macros/EditionNavBar.html openlibrary/plugins/openlibrary/js/lazy-carousel.js

Screenshot

  1. Added banner clicking on which smooth-scrolls the user down to the carousel section, banner will show above the navbar on both mobile and desktop - style changes to the banner will be made via feat(components): add ol-toast and ol-banner web componentsΒ #12868
image image
  1. Moved the carousel section inside the right-side segment for better looks and behaviour
image
  1. (Partially related) Switched the patron intent banner to use ol-banner
image

Stakeholders

@mekarpeles

Copilot AI review requested due to automatic review settings June 12, 2026 09:50
@github-actions github-actions Bot added the Priority: 2 Important, as time permits. [managed] label Jun 12, 2026
@Sadashii
Sadashii force-pushed the feat/12743/recommend-available-alternatives branch from 7fe996d to d74ca35 Compare June 12, 2026 09:51

Copilot AI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR improves in-page navigation and availability messaging on edition pages by enabling smooth scrolling, adjusting anchor offsets, and surfacing lending-state-driven banners/sections.

Changes:

  • Add get_lending_state to resolve user-facing lending/availability state (with new tests) and expose it to templates.
  • Update edition templates/macros to pass lending state into EditionNavBar and reposition the Related Books carousel as a tab section target.
  • Improve scrolling UX (smooth scroll + updated scroll-padding-top) and adjust carousel layout CSS; update pending-action banner rendering & i18n strings.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
static/css/page-book.css Adjusts scroll padding offsets for anchor navigation across breakpoints.
static/css/components/work.css Tweaks carousel container margin for related books layout.
static/css/base/common.css Enables smooth scrolling with reduced-motion fallback.
openlibrary/core/lending.py Adds get_lending_state helper for templates and other call sites.
openlibrary/tests/core/test_lending.py Adds test coverage for get_lending_state.
openlibrary/plugins/openlibrary/code.py Exposes get_lending_state as a template global.
openlibrary/templates/type/edition/view.html Computes lending state once and wires it into EditionNavBar; relocates Related Books section.
openlibrary/macros/EditionNavBar.html Adds conditional availability banner linking to related books.
openlibrary/templates/account/view.html Switches pending-action banner to <ol-banner> output.
openlibrary/plugins/upstream/mybooks.py Refactors translated pending-action message to use placeholders around an HTML link.
openlibrary/i18n/messages.pot Updates extracted strings and source references for i18n.
AGENTS.md Documents i18n guidance for link placeholders inside a single translatable string.

Comment thread openlibrary/core/lending.py Outdated
Comment thread openlibrary/core/lending.py Outdated
Comment thread openlibrary/tests/core/test_lending.py Outdated
Comment thread static/css/components/work.css Outdated
Comment thread openlibrary/i18n/messages.pot
Comment thread static/css/page-book.css
@Sadashii
Sadashii force-pushed the feat/12743/recommend-available-alternatives branch 2 times, most recently from c6c6a3f to d74ca35 Compare June 12, 2026 09:56
@Sadashii

Copy link
Copy Markdown
Collaborator Author

Note to self, a follow up PR on ol banner has to be merged first with some additions to be made here, do not merge this before that.

@github-actions github-actions Bot added the Needs: Response Issues which require feedback from lead label Jun 13, 2026

@lokesh lokesh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@Sadashii

Copy link
Copy Markdown
Collaborator Author

@RayBB need your inputs on the extracted get_lending_state function (it is slightly drifted) and about the urgency of migrating LoanStatus macro to utilize this.

Drift:
One behavioral divergence already exists: the new function adds an early elif is_waiting: return
Β "waitlist" before the partner/open/printdisabled checks. The original only surfaces waitlist
Β state inside the is_lendable branch. It's covered by test_get_lending_state_waiting_loan, so it
Β looks int

lokesh
lokesh previously requested changes Jun 18, 2026

@lokesh lokesh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@Sadashii
Can you check on the following issues:

1 ) Persistence working correctly when X clicked in banner
Two distinct dismissal paths existed and only one survives the swap:

  • Click-through (clicking "Continue"): the inline script at account/view.html:31-33 clears the pending_action cookie. βœ… Still works β€” .pending-action-link is inside the server-rendered message HTML,
    captured into ol-banner's light-DOM _content, so the selector still matches.
  • Explicit dismiss (X button): OLD site/banner with cookie_duration_days=1 β†’ legacy dismiss β†’ /hide_banner sets pending_action=1 β†’ server stops rendering for a day. NEW ol-banner fires
    ol-banner-dismiss with empty id β†’ listener returns early β†’ no cookie written β†’ the original pending_action JSON cookie is untouched β†’ banner re-renders on the next page load. ❌ Regression.

2 ) Analytics double-fire on the related-books carousel
The ping is emitted per-carousel, but the experiment wants per-section discovery. Two carousels under one #related-work-carousel anchor β†’ two pings.

Impact β€” worse than a flat 2Γ—: Both placeholders live adjacent in .related-books with rootMargin: '200px', so they intersect near-simultaneously and almost always both fire. But the multiplier is
inconsistent:

  • work with subjects + authors β†’ 2 pings
  • work with only one β†’ 1 ping

So FromBanner / ScrolledDownAvailable / ScrolledDownUnavailable are inflated by a variable factor that correlates with the book's metadata richness. That corrupts not just absolute counts but the
banner-vs-natural ratio the experiment exists to measure (richer books may skew toward one cohort).

Fix: track once per section. Simplest is a module-scoped guard:
// top of module
let relatedBooksTracked = false;
// in the block
if (!relatedBooksTracked && (config.key === 'related-subjects-carousel' || config.key === 'related-authors-carousel')) {
relatedBooksTracked = true;
...
}

** 3 ) Broken dismiss-tracking selector **

Root cause: class renamed across the component swap. querySelector('.page-banner--dismissable-close') now returns null, the if (dismiss) guard is false, and the PreserveIntent|Dismiss attribute is
never attached. Dismiss analytics silently lost.

Fix: stop trying to decorate the button. Listen for the component's own event (it bubbles, composed: true) and send the ping directly:
container.addEventListener('ol-banner-dismiss', function() {
if (window.archive_analytics && window.archive_analytics.ol_send_event_ping) {
window.archive_analytics.ol_send_event_ping({ category: 'PreserveIntent', action: 'Dismiss' });
}
});

@RayBB

RayBB commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

@Sadashii I'm not super familiar with get_lending_state, is there something particular you wanted to know from me about it?

In terms of the loanstatus macro. If you mean converting it to Jinja, we certainly could but I wouldn't let you block on it. I think it's moderately difficult compared to the other templates and we haven't converted anything like that yet.

Let me know how I can be helpful!

@Sadashii

Sadashii commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

@lokesh good catches, resolved.
@RayBB I was just wondering if a seperate effort should be made about switching LoanStatus macro to this handler (with the appropriate additons) to help the jinja migration efforts, i.e. if these changes are in your present timeline.

@Sadashii
Sadashii requested a review from lokesh July 2, 2026 16:07
@lokesh

lokesh commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Thanks for addressing the previous comments.

Here are some additional items that came up in the automatic review that need a look over:

  1. openlibrary/plugins/openlibrary/js/lazy-carousel.js:8 β€” ordinary nav-bar "Related Books" clicks are misattributed as FromBanner. The click listener matches a[href="#related-work-carousel"], but in EditionNavBar.html both the warning banner and the regular nav-menu item use that same href. Clicking the plain nav item (never touching the banner) sets bannerClicked=true, so the subsequent carousel load fires OpenRelatedBooks|FromBanner instead of ScrolledDown*. This corrupts the exact banner-vs-natural experiment the PR exists to measure. Match the banner specifically (e.g. data-ol-link-track="OpenRelatedBooks|BannerClick" or an id) instead of the shared href.

  2. static/css/components/work.css:313 β€” .lazy-carousel { margin: 0 !important } is unscoped and leaks site-wide. The rule pairs .related-books .carousel-container (correctly scoped) with a bare .lazy-carousel, which is the class emitted by RawQueryCarousel.html:33 for every QueryCarousel β€” home page, subject pages, author pages, "You might also like". So this zeroes carousel margins everywhere, and !important blocks downstream overrides. Scope it: .related-books .lazy-carousel.

  3. openlibrary/core/lending.py (elif is_waiting: return "waitlist") β€” the waitlist branch is hoisted above printdisabled/open/borrowable, diverging from LoanStatus.html. In the source macro, waitlist state only surfaces inside the is_lendable block, below the partner/open/printdisabled/borrowable checks. A print-disabled user (or a user whose book is readable/borrowable) who also holds a not-yet-their-turn waiting_loan is now classified "waitlist" β†’ wrongly shown the "unavailable" banner, while the read button rendered by LoanStatus.html on the same page still shows readable/borrow. The two views disagree.

@RayBB

RayBB commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

@Sadashii I see what you're asking. Since basically all of openlibrary/macros/EditionNavBar.html is being touched, and it doesn't make any network calls I would love if you converted it to Jinja! That would be a great step in the right direction and good to get more people understanding it.

#13077 has the most recent example of how we did it (you might need to merge master or rebase).

Summary is you should be able to:

  1. Create EditionNavBar.html.jinja
  2. Call: $:render_jinja_template("EditionNavBar.html.jinja", XYZ)

Don't spent too much time on it if you get stuck but it would be very easy for AI to convert that particular macro.

What do you think?

@Sadashii

Sadashii commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

@lokesh

  1. Applied
  2. Reverted per our conversations that moving the banner to the sidebar squishes it more.
  3. Fixed the precedence to follow LoanStatus as exactly, a seperate PR post-this should also refactor LoanStatus macro to utilize get_lending_state to have a single source of truth.

@RayBB

Sounds good, will update this and LoanStatus macro to jinja in a seperate PR. Had Antigravity take a look and should be a clean change.

@lokesh

lokesh commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

One question and one comment.

  1. What does dismissing the banner do? Does it come back if I reload the page?
  2. I don't think it should block the PR, but I want to just note that the top of page banner might not be the best UI pattern for this scenario. It screams "you came here to read this, it's unavailable, let me rescue you". But people land on this page with diverse intents. And seeing a banner, first thing, before they get a chance to orient themselves, and a banner that has negative framing "this book is unavailable" gives a bad first impression.

In the future we might want to consider a lighter contextual treatment. For example, an empty-state near the Read/Borrow CTA "Not available to read on OL - see similar available books"


One more thing: I just noticed that the "You might also like" carousel only includes books that are available to read. Which is surprising, though not necessarily a bad thing. But we should at minimum update the heading to something like "Available books like this one"

@mekarpeles

Copy link
Copy Markdown
Member
  1. If we dismiss banner and refresh, should the banner persist? (Lokesh had +1'd)
  2. Fixing the banner flexing span structure

Comment thread openlibrary/core/lending.py
Comment thread openlibrary/macros/EditionNavBar.html
Comment thread openlibrary/plugins/openlibrary/code.py Outdated
Sadashii and others added 6 commits July 16, 2026 14:42
…hive#12743)

Recommend alternative books via a warning banner when a book edition is
unavailable (preview_only, checked out, waitlist, or locate physical states).

To avoid code duplication and keep template markup clean, the availability
state resolution is extracted to Python, and the responsive banner logic
is encapsulated inside the EditionNavBar component.

- Add `get_lending_state` helper to lending.py and register it as template global.
- Update EditionNavBar.html to conditionally render the banner wrapped in
  responsive divs, avoiding display-override conflicts.
- Delegate banner logic in view.html to EditionNavBar and clean up unused
  data-workid carousel attribute.
- Refactor get_pending_action_banner in mybooks.py and update AGENTS.md to
  follow i18n best practices (unified strings with format placeholders).
- Add unit tests for get_lending_state to test_lending.py.
Sadashii and others added 10 commits July 16, 2026 14:44
…dules

Concurrently building 'js', 'css', and 'components' targets in parallel via concurrently causes a race condition because each target lists node_modules as a prerequisite in the Makefile, which executes npm ci when the directory is out of date.

On fresh checkout on GitHub Actions, package-lock.json and package.json timestamps are newer than the restored node_modules cache, leading all parallel processes to concurrently run npm ci (which deletes and reinstalls node_modules), causing arbitrary MODULE_NOT_FOUND errors.

Touching node_modules right after cache restore/install updates its modification timestamp to the current time, making it newer than package-lock.json. This tells make that the directory is already up-to-date, preventing concurrent npm ci execution.
…rk/db calls

- Refactor get_lending_state in lending.py to prioritize cheap checks and run db queries lazily.
- Compute lending state once at the top-level book page and pass it down to EditionNavBar, databarWork, and edition-sort.
- Refactor LoanStatus macro to accept pre-computed lending_state, skipping get_lending_state and lazy loading loans/waitlists only when needed.
- Remove unused variables (my_turn_to_borrow) and cleanup dead code.
- Update RelatedWorksCarousel header to 'Available books like this one' to match search query filtering.
…rebase conflict resolution

A manual conflict resolution while rebasing onto master reordered
dict entries in setup_template_globals(), which caused a later
commit's removal of the get_lending_state template global (superseded
by precomputed lending_state) to silently not reapply. Also includes
ruff import-order autofixes surfaced by the rebase.
@mekarpeles
mekarpeles force-pushed the feat/12743/recommend-available-alternatives branch from 79439e7 to 0c878c4 Compare July 16, 2026 20:48
@openlibrary-bot

Copy link
Copy Markdown
Collaborator

πŸ‘‹ This branch was rebased onto current master to resolve merge conflicts. Here's what was going on, in case it helps for next time:

Why the conflict happened

Your branch had drifted 17 commits behind master. The only file those commits and this PR both touched was openlibrary/i18n/messages.pot β€” it's machine-generated (regenerated from source strings via scripts/i18n-messages extract), and master had regenerated large chunks of it independently. Any two branches that both touch .pot around the same time will collide, even with no real logic conflict.

There was also a small package.json/package-lock.json conflict from an incidental @sentry/browser version bump (10.60.0 β†’ 10.63.0) picked up by an npm install alongside an unrelated JS fix β€” unrelated to the feature, so master's pinned version won.

One real (non-generated) conflict: openlibrary/plugins/openlibrary/code.py β€” both your branch and master added new entries to the same Template.globals dict at the same spot. Simple additive conflict, resolved by keeping both.

Tips to avoid this proactively:

  1. Rebase onto master periodically, especially for long-running branches (this one was open ~5 weeks). The longer a branch lives, the more likely messages.pot and package-lock.json drift.
  2. Regenerate messages.pot after rebasing, don't hand-merge it: python ./scripts/i18n-messages extract (or let the generate-pot pre-commit hook do it β€” needs Docker, since it depends on infogami being importable). Whichever side's regeneration is more recent is generally right; there's nothing to "merge" semantically in a generated catalog.
  3. Don't let dependency lockfile bumps ride along in unrelated commits. If npm install changes package-lock.json entries you didn't intend to touch, revert those hunks before committing β€” git diff package-lock.json is worth a glance before git add.
  4. After rebasing, running pre-commit run --files $(git diff master..HEAD --name-only) locally catches lint/format issues before pushing β€” ruff check would have caught the small self-inflicted issue this rebase introduced (an orphaned get_lending_state reference from a conflict-resolution ordering mismatch, already fixed).

No action needed on your end β€” the branch is up to date with master and mergeable. Thanks for the PR! πŸ™Œ

β€” PAM (Open Library's Project AI Manager)

@openlibrary-bot openlibrary-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Follow-up review after the rebase (all prior review threads are marked resolved β€” I independently re-verified each against the current code):

  • βœ… get_book_provider(doc).short_name fix in lending.py β€” confirmed, calls the function and reads the attribute off the returned provider, not off the function itself.
  • βœ… waiting_loan now drives is_waiting β†’ "waitlist" β€” confirmed used, not dead.
  • βœ… test_get_lending_state_partner mocks short_name, matching the corrected implementation.
  • βœ… No !important remains anywhere in this PR's CSS diff.
  • βœ… lazy-carousel.js matches data-ol-link-track="OpenRelatedBooks|BannerClick" specifically (fixes nav-bar-click misattribution) and guards on a module-scoped relatedBooksTracked flag (fixes the double-fire).
  • βœ… account/view.html's ol-banner-dismiss handler unconditionally clears the pending_action cookie β€” the old "empty id β†’ early return, cookie never cleared" regression is gone.
  • βœ… banner-analytics.js listens for ol-banner-dismiss directly (e.detail.dismissId || e.target.id) instead of the renamed .page-banner--dismissable-close selector, and is wired up via initOlBannerDismissals() in js/index.js gated on ol-banner[dismissible].
  • βœ… Single get_lending_state() call per book page, threaded down through EditionNavBar/databarWork/editions_datatable β€” not recomputed per row (editions_datatable only passes it for the highlighted/current edition row, correctly leaving other rows to resolve their own state).
  • βœ… get_lending_state uses @public, no manual Template.globals registration needed.

One unrelated discovery while reading the surrounding code β€” flagging separately on LoanStatus.html, not blocking this PR.

$if not waiting_loan:
$ book_provider = get_book_provider(doc)
$else:
$ book_provider = get_book_provider.ia

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Pre-existing bug, not introduced by this PR (this line is unchanged context in the diff β€” same on master): get_book_provider.ia reads an .ia attribute off the function object get_book_provider, not off a provider instance. That attribute doesn't exist β€” there's no get_book_provider.ia = ... anywhere in book_providers.py. The module-level singleton you likely want is ia_provider (openlibrary/book_providers.py:752), or call get_book_provider_by_name("ia").

This branch ($else: under $if not waiting_loan:) is reachable whenever lending_state == 'waitlist' and the user has a real active waiting_loan β€” at that point this line raises AttributeError: 'function' object has no attribute 'ia'.

Flagging here because this PR's get_lending_state refactor sits right next to it, and @Sadashii's comment above mentions a follow-up PR to migrate LoanStatus onto get_lending_state as the single source of truth β€” worth fixing as part of that follow-up rather than in this PR.

@mekarpeles
mekarpeles merged commit e600862 into internetarchive:master Jul 16, 2026
5 checks passed
mekarpeles added a commit to Sadashii/openlibrary that referenced this pull request Jul 16, 2026
The CheckWorldcat rename's pot entries drifted from master (which
merged internetarchive#12910 in the meantime); regenerated from the final rebased
tree so the two are consistent.
mekarpeles added a commit to Sadashii/openlibrary that referenced this pull request Jul 16, 2026
….pot after rebasing onto master

internetarchive#12910 merged in the meantime and split the old single is_lendable
branch into separate borrowable/waitlist/checkedout branches, each
duplicating the $if secondary_action: BookPreview(...) line. Applied
this PR's BookPreview -> PreviewSearchInside swap to all three,
matching the single occurrence it replaced pre-split. Regenerated
messages.pot from the final rebased tree rather than hand-merging.
Sadashii pushed a commit to Sadashii/openlibrary that referenced this pull request Jul 17, 2026
The CheckWorldcat rename's pot entries drifted from master (which
merged internetarchive#12910 in the meantime); regenerated from the final rebased
tree so the two are consistent.
Sadashii pushed a commit to Sadashii/openlibrary that referenced this pull request Jul 17, 2026
….pot after rebasing onto master

internetarchive#12910 merged in the meantime and split the old single is_lendable
branch into separate borrowable/waitlist/checkedout branches, each
duplicating the $if secondary_action: BookPreview(...) line. Applied
this PR's BookPreview -> PreviewSearchInside swap to all three,
matching the single occurrence it replaced pre-split. Regenerated
messages.pot from the final rebased tree rather than hand-merging.
mekarpeles added a commit that referenced this pull request Jul 17, 2026
* feat: Migrate Book Preview & Search Inside to ol-dialog

- Migrates the Book Preview and Search Inside modals from legacy jQuery Colorbox to the new <ol-dialog> Lit component.
- Extracted the shared book preview dialog markup to a new macros/BookPreviewFloater.html template to prevent markup duplication.
- Updated templates (BookPreview.html, PreviewSearchInside.html, and home/index.html) to call BookPreviewFloater.
- Made the book preview iframe size dynamic and responsive using CSS calc() in buttonCta.css to eliminate vertical scrollbars inside the modal.
- Improved accessibility by adding an iframe title, form search role/aria-label, and dynamically updating the search trigger's aria-expanded attributes in JS.
- Cleaned up dialog.js by extracting redundant show/hide form actions into a shared collapseSearchForm helper.
- Standardized templates to use the correct ('...') i18n syntax.
- Regenerated openlibrary/i18n/messages.pot to compile the new 'See more about this book on Archive.org' localization string.

* Improve accessibility of preview and search inside elements

* fix(rebase): resolve LoanStatus.html conflict and regenerate messages.pot after rebasing onto master

#12910 merged in the meantime and split the old single is_lendable
branch into separate borrowable/waitlist/checkedout branches, each
duplicating the $if secondary_action: BookPreview(...) line. Applied
this PR's BookPreview -> PreviewSearchInside swap to all three,
matching the single occurrence it replaced pre-split. Regenerated
messages.pot from the final rebased tree rather than hand-merging.

---------

Co-authored-by: Michael E. Karpeles (Mek) <michael.karpeles@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs: Response Issues which require feedback from lead Priority: 2 Important, as time permits. [managed]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Book Page: Recommend Available Alternatives

6 participants