Skip to content

feat(search): surface PDF highlight bodies in Global Search (#864) - #873

Merged
otomatty merged 3 commits into
developfrom
claude/fix-issue-864-rsNZe
May 16, 2026
Merged

feat(search): surface PDF highlight bodies in Global Search (#864)#873
otomatty merged 3 commits into
developfrom
claude/fix-issue-864-rsNZe

Conversation

@otomatty

@otomatty otomatty commented May 16, 2026

Copy link
Copy Markdown
Owner

Extend /api/search to also probe pdf_highlights.text with owner-scoping
and a defensive sources.kind='pdf_local' check, then plumb the new
discriminated rows through the client merge logic so the global search
dropdown and /search page render highlight hits and deep-link to either
the derived Zedi page or /sources/:sourceId/pdf#page=N.

  • Server: add highlight query gated by PDF_HIGHLIGHT_SEARCH_DISABLED env
    kill switch; tag every result row with kind ("page" / "pdf_highlight").
  • Client: turn SearchSharedResponse into a discriminated union, route
    highlight rows via resolveSearchResultUrl, and score them below page
    matches to preserve "title > derived page > highlight body" ordering.
  • Tests: add unit coverage for owner-scoping, kind="pdf_local" filter,
    feature-flag kill switch, URL composition, and the merged result shape.

Summary by CodeRabbit

  • New Features

    • Search now returns combined page and PDF-highlight results (each tagged by kind); highlights show a PDF badge/icon and deep-linking to PDF or a derived page.
    • Navigation updated to route directly from any result kind.
  • Bug Fixes

    • API no longer exposes full content text in page responses; merged results enforce hard limits.
    • PDF-highlight search can be disabled via an environment flag.
  • Tests

    • Expanded coverage for PDF highlights, scope/access behavior, deduplication, merging/allocation, and limit enforcement.

Review Change Stack

Extend /api/search to also probe pdf_highlights.text with owner-scoping
and a defensive sources.kind='pdf_local' check, then plumb the new
discriminated rows through the client merge logic so the global search
dropdown and /search page render highlight hits and deep-link to either
the derived Zedi page or /sources/:sourceId/pdf#page=N.

- Server: add highlight query gated by PDF_HIGHLIGHT_SEARCH_DISABLED env
  kill switch; tag every result row with kind ("page" / "pdf_highlight").
- Client: turn SearchSharedResponse into a discriminated union, route
  highlight rows via resolveSearchResultUrl, and score them below page
  matches to preserve "title > derived page > highlight body" ordering.
- Tests: add unit coverage for owner-scoping, kind="pdf_local" filter,
  feature-flag kill switch, URL composition, and the merged result shape.
@coderabbitai

coderabbitai Bot commented May 16, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c9f03530-667c-4074-9332-78d0a502a073

📥 Commits

Reviewing files that changed from the base of the PR and between b8d67cf and 44df644.

📒 Files selected for processing (2)
  • server/api/src/__tests__/routes/search.test.ts
  • server/api/src/routes/search.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/api/src/routes/search.ts
  • server/api/src/tests/routes/search.test.ts

📝 Walkthrough

Walkthrough

Adds PDF-highlight results end-to-end: new API union rows, server-side highlight query (owner + sources.kind='pdf_local') with kill-switch, reserved-budget merge with pages, client discriminated-union model and dedup changes, kind-aware routing, UI updates, and tests validating SQL, payload shapes, and merged limits.

Changes

PDF Highlight Search Integration

Layer / File(s) Summary
API contract and result types
src/lib/api/types.ts
Adds SearchPageResultRow and SearchPdfHighlightResultRow, unifies as SearchResultRow, and updates SearchSharedResponse.results to SearchResultRow[].
Backend route: page + highlight merge
server/api/src/routes/search.ts
Implements pageRows flow, runPdfHighlightSearch() with owner and sources.kind='pdf_local', omits content_text from page SELECT, tags rows with kind, merges results using a reserved highlight quota and clips to limit, and adds kill-switch PDF_HIGHLIGHT_SEARCH_DISABLED.
Backend tests: two-query model, scope & highlight coverage
server/api/src/__tests__/routes/search.test.ts
Mocks two sequential DB calls (pages then highlights), clears env in beforeEach, asserts scope-specific SQL chains and joins, verifies highlight filtering and kill-switch skipping, checks content_text non-leak, and validates merged-limit/reservation behavior.
Global search model & dedup (hook)
src/hooks/useGlobalSearch.ts
Introduces discriminated union GlobalSearchResultItem (page
Global search hook tests: dedup & highlight
src/hooks/useGlobalSearch.test.ts
Refactors tests to typed SearchResultRow[], adds pdf_highlight factories and tests for inclusion, ordering, non-deduping, formatting, and snippet behavior.
Global search context: kind-aware routing
src/contexts/GlobalSearchContext.tsx, src/contexts/GlobalSearchContext.test.ts
handleSelect now accepts full item; adds resolveSearchResultUrl that prefers derivedPageId for highlights or deep-links to /sources/:sourceId/pdf#page=:pdfPage; tests validate routing for both kinds.
Header search UI: selection & keys
src/components/layout/Header/HeaderSearchBar.tsx, src/components/layout/Header/HeaderSearchDropdownContent.tsx
Header passes full GlobalSearchResultItem to selection handlers, getResultKey produces stable kind-aware keys, dropdown renders kind-based icons and a “PDF” badge for highlights, and Enter/select flows updated.
Search result card: discriminated types & badges
src/components/search/SearchResultCard.tsx
Replaces single card item type with page/highlight union; derives isPdf/isShared/hasSourceUrl from kind to choose icons and show “PDF” badge for highlights.
Search results page: merge, sort, and routing
src/pages/SearchResults.tsx
Deduplicates shared pages, builds shared highlights separately, merges personal + pages + highlights, sorts by score, delegates click routing to resolveSearchResultUrl, and adds stable keys for highlights.

Sequence Diagram(s)

sequenceDiagram
  participant SearchUI as Search UI
  participant Endpoint as GET /api/search
  participant PageQuery as Page Query
  participant HighlightQuery as Highlight Query
  participant Client as useGlobalSearch

  SearchUI->>Endpoint: q=term&scope=own
  Endpoint->>PageQuery: Query pages by scope
  PageQuery-->>Endpoint: [{ kind: "page", ... }]
  alt PDF_HIGHLIGHT_SEARCH_DISABLED not set
    Endpoint->>HighlightQuery: Query pdf_highlights (owner_id, sources.kind='pdf_local')
    HighlightQuery-->>Endpoint: [{ kind: "pdf_highlight", ... }]
  end
  Endpoint-->>Client: { results: SearchResultRow[] }
  Client->>Client: buildGlobalSearchResults()
  Client->>Client: dedupSharedRowsAgainstPersonal() (page-only)
  Client->>Client: Merge personal + pages + highlights, sort & slice
  Client-->>SearchUI: GlobalSearchResultItem[]
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • otomatty/zedi#831: Related prior changes to /api/search scope/default-note handling that this PR builds upon.
  • otomatty/zedi#739: Related refactor of global search dedup/build logic; this PR extends that work with pdf_highlight kinds.
  • otomatty/zedi#722: Prior /api/search changes affecting scope/SQL that overlap with this PR’s server-side adjustments.

Poem

🐰 I hopped through rows both page and PDF,
Kind tags guiding every little step,
Highlights kept, pages trimmed just right,
Merged and routed into sight,
Hooray — search now leaps with extra pep!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(search): surface PDF highlight bodies in Global Search (#864)' accurately reflects the main change: enabling PDF highlight text to appear in the global search feature.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fix-issue-864-rsNZe

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

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request integrates PDF highlights into the global search functionality by introducing a discriminated union for search results and adding backend support for querying the pdf_highlights table with a configurable kill switch. The frontend is updated to display these highlights with specific icons and deep-linking capabilities. Feedback suggests centralizing the formatting logic for PDF highlights to avoid duplication between the search hook and the results page.

Comment on lines +136 to +162
const sharedHighlights: PdfHighlightSearchResultItem[] = dedupedShared
.filter((r): r is SearchPdfHighlightResultRow => r.kind === "pdf_highlight")
.map((r) => {
const snippet = extractSmartSnippet(r.text, keywords, 200);
const highlightedSnippet = highlightKeywords(snippet, keywords);
const file =
(r.source_display_name?.trim() || r.source_title?.trim() || null) ??
t("common.pdfHighlightFallbackName", { defaultValue: "PDF" });
return {
kind: "pdf_highlight",
highlightId: r.highlight_id,
sourceId: r.source_id,
pdfPage: r.pdf_page,
derivedPageId: r.derived_page_id,
title: t("common.pdfHighlightResultTitle", {
defaultValue: "{{file}} (p.{{page}})",
file,
page: r.pdf_page,
}),
snippet,
highlightedSnippet,
matchType: "content" as MatchType,
thumbnailUrl: undefined,
updatedAt: new Date(r.updated_at).getTime(),
score: PDF_HIGHLIGHT_BASE_SCORE,
};
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The logic for formatting PDF highlight results is duplicated here from src/hooks/useGlobalSearch.ts. This includes i18n keys, default values, and snippet extraction. It is recommended to centralize this logic, for example by making buildPdfHighlightItem more flexible or creating a shared formatting helper, to ensure consistency and ease of maintenance.

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/layout/Header/HeaderSearchDropdownContent.tsx (1)

11-27: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add bilingual TSDoc for the exported props interface.

HeaderSearchDropdownContentProps is part of the exported contract but currently has an empty doc block. Please add explicit JP/EN TSDoc describing key props (searchResults, onSelectItem, keyboard-indexing props).

Proposed doc patch
 /**
- *
+ * Header search dropdown rendering contract.
+ *
+ * ヘッダー検索ドロップダウンの描画契約。
  */
 export interface HeaderSearchDropdownContentProps {

As per coding guidelines, **/*.{ts,tsx}: “Add TSDoc / JSDoc comments to exported functions, types, and interfaces” and **/*.{ts,tsx,js,md}: “Include both Japanese and English comments/documentation in code and documentation files”.

🤖 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 `@src/components/layout/Header/HeaderSearchDropdownContent.tsx` around lines 11
- 27, Add bilingual (JP/EN) TSDoc for the exported interface
HeaderSearchDropdownContentProps: describe the purpose of the interface and
document key props such as searchResults (array of GlobalSearchResultItem
returned by the search), onSelectItem (callback invoked when a result is
chosen), keyboard-indexing props like activeIndex, getOptionId and
setActiveIndex (how indexing/IDs are used for keyboard navigation), and other
important props (hasContent, showEmpty, showResults, query, hasQuery, listRef,
footerRef, closeDropdown, handleSearchSubmit, itemCount). Keep each prop comment
concise and provide both Japanese and English sentences for the interface and
each property, following existing TSDoc style in the repo.
🧹 Nitpick comments (2)
src/lib/api/types.ts (1)

242-242: ⚡ Quick win

Add a dedicated TSDoc block for SearchResultRow.

SearchResultRow is exported but currently undocumented as its own symbol, which weakens contract discoverability for downstream users.

As per coding guidelines **/*.{ts,tsx}: Add TSDoc / JSDoc comments to all exported functions, types, and interfaces.

🤖 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 `@src/lib/api/types.ts` at line 242, Add a TSDoc block above the exported type
SearchResultRow describing that it is a union of SearchPageResultRow and
SearchPdfHighlightResultRow and explaining when each variant is returned (e.g.,
page-level results vs PDF highlight results), including any important fields or
usage notes; ensure the comment references the related types SearchPageResultRow
and SearchPdfHighlightResultRow and follows the project's TSDoc/JSDoc style for
exported types.
server/api/src/__tests__/routes/search.test.ts (1)

214-241: ⚡ Quick win

Strengthen response-shape assertions for contract fields.

These tests currently pass even if page rows include unintended fields or miss required page fields. Please assert the required page shape (e.g., owner_id, thumbnail_url, source_url) and explicitly assert content_text is absent.

Also applies to: 417-458

🤖 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 `@server/api/src/__tests__/routes/search.test.ts` around lines 214 - 241,
Update the test "response page rows are tagged with kind='page' and include
note_id" (the it block) to more strictly assert the page response shape: after
parsing body.results (from the createSearchApp + authHeaders flow) assert that
the result object contains required page fields owner_id, thumbnail_url, and
source_url, and explicitly assert that content_text is absent (e.g.,
result.content_text is undefined or result does not have the key). Apply the
same stronger assertions to the other similar test block referenced (the test
around lines 417-458) so both tests validate presence of required page fields
and absence of content_text.
🤖 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 `@server/api/src/routes/search.ts`:
- Around line 160-169: Replace the spreading of raw SQL rows in taggedPageRows
with an explicit mapping that builds each object only from the contract fields
defined by SearchPageResultRow (do not include pc.content_text or any other raw
columns); in other words, change the taggedPageRows mapping over pageRows to
pick and assign only the allowed properties (e.g., the exact keys declared in
SearchPageResultRow) and then add kind: "page" and the appropriate type
assertion to SearchPageResultRow, then merge with highlightRows and return via
c.json as before (leave runPdfHighlightSearch and the final c.json call
unchanged).

In `@src/components/search/SearchResultCard.tsx`:
- Around line 27-40: Add concise bilingual (Japanese and English) TSDoc comments
for the two newly exported interfaces: SearchResultCardPageItem and
SearchResultCardPdfHighlightItem; for each interface include a short summary in
EN/JP describing its purpose and list of fields (e.g., pageId, noteId, sourceUrl
for SearchResultCardPageItem and highlightId, sourceId, pdfPage, derivedPageId
for SearchResultCardPdfHighlightItem) so the public contract is explicit and
follows the repository's TSDoc/JSDoc guidelines.

In `@src/hooks/useGlobalSearch.test.ts`:
- Line 415: The section comment "Issue `#864`: PDF ハイライト統合" in the
useGlobalSearch.test file is Japanese-only; update that comment to include an
English counterpart (e.g., "Issue `#864`: PDF ハイライト統合 / PDF highlight integration"
or similar) so comments comply with the bilingual guideline; locate the Japanese
string "PDF ハイライト統合" in the test file and replace or append the English
translation adjacent to it.

---

Outside diff comments:
In `@src/components/layout/Header/HeaderSearchDropdownContent.tsx`:
- Around line 11-27: Add bilingual (JP/EN) TSDoc for the exported interface
HeaderSearchDropdownContentProps: describe the purpose of the interface and
document key props such as searchResults (array of GlobalSearchResultItem
returned by the search), onSelectItem (callback invoked when a result is
chosen), keyboard-indexing props like activeIndex, getOptionId and
setActiveIndex (how indexing/IDs are used for keyboard navigation), and other
important props (hasContent, showEmpty, showResults, query, hasQuery, listRef,
footerRef, closeDropdown, handleSearchSubmit, itemCount). Keep each prop comment
concise and provide both Japanese and English sentences for the interface and
each property, following existing TSDoc style in the repo.

---

Nitpick comments:
In `@server/api/src/__tests__/routes/search.test.ts`:
- Around line 214-241: Update the test "response page rows are tagged with
kind='page' and include note_id" (the it block) to more strictly assert the page
response shape: after parsing body.results (from the createSearchApp +
authHeaders flow) assert that the result object contains required page fields
owner_id, thumbnail_url, and source_url, and explicitly assert that content_text
is absent (e.g., result.content_text is undefined or result does not have the
key). Apply the same stronger assertions to the other similar test block
referenced (the test around lines 417-458) so both tests validate presence of
required page fields and absence of content_text.

In `@src/lib/api/types.ts`:
- Line 242: Add a TSDoc block above the exported type SearchResultRow describing
that it is a union of SearchPageResultRow and SearchPdfHighlightResultRow and
explaining when each variant is returned (e.g., page-level results vs PDF
highlight results), including any important fields or usage notes; ensure the
comment references the related types SearchPageResultRow and
SearchPdfHighlightResultRow and follows the project's TSDoc/JSDoc style for
exported types.
🪄 Autofix (Beta)

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

Run ID: c24a6dbf-09d1-406f-808a-8ec35f081bac

📥 Commits

Reviewing files that changed from the base of the PR and between 5f99095 and 640d40d.

📒 Files selected for processing (11)
  • server/api/src/__tests__/routes/search.test.ts
  • server/api/src/routes/search.ts
  • src/components/layout/Header/HeaderSearchBar.tsx
  • src/components/layout/Header/HeaderSearchDropdownContent.tsx
  • src/components/search/SearchResultCard.tsx
  • src/contexts/GlobalSearchContext.test.ts
  • src/contexts/GlobalSearchContext.tsx
  • src/hooks/useGlobalSearch.test.ts
  • src/hooks/useGlobalSearch.ts
  • src/lib/api/types.ts
  • src/pages/SearchResults.tsx

Comment thread server/api/src/routes/search.ts Outdated
Comment thread src/components/search/SearchResultCard.tsx
Comment thread src/hooks/useGlobalSearch.test.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 640d40d989

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/api/src/routes/search.ts Outdated
Comment on lines +167 to +169
const highlightRows = await runPdfHighlightSearch(db, userId, pattern, limit);

return c.json({ results: [...taggedPageRows, ...highlightRows] });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Enforce a single response limit after merging result kinds

The new flow applies LIMIT ${limit} to the page query and again to the highlight query, then concatenates both arrays without a final cap, so /api/search can now return up to 2 * limit rows (e.g., 40 when limit is 20). This changes the endpoint’s effective limit semantics and can increase payload size/latency for callers that rely on limit as a hard bound; apply a final sort/slice (or split the limit budget) before returning results.

Useful? React with 👍 / 👎.

…ation

- Drop `pc.content_text` from the page-query SELECT and whitelist response
  fields explicitly so the API never echoes full page bodies back to the
  client (PR #873 review: CodeRabbit).
- Cap the merged page+highlight result list at `limit` after concatenation;
  each branch had its own LIMIT, so a naïve concat could return up to
  2*limit rows (PR #873 review: codex).
- Extract `formatPdfHighlightDisplay` so the header dropdown and
  /search page share one i18n / snippet helper instead of duplicating
  it (PR #873 review: Gemini). Translator function is injected so the
  helper works with both i18next's global `t` and react-i18next's hook.
- Add bilingual TSDoc to `SearchResultCardPageItem` /
  `SearchResultCardPdfHighlightItem` (PR #873 review: CodeRabbit).
- Add English text to the section comment in
  `useGlobalSearch.test.ts` (PR #873 review: CodeRabbit).
- Add tests covering the whitelist, the final limit cap, and the
  shared display helper's fallback / length-override paths.

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

Actionable comments posted: 1

🤖 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 `@server/api/src/routes/search.ts`:
- Around line 200-211: The current concat-and-slice (const merged =
[...taggedPageRows, ...highlightRows].slice(0, limit)) prematurely enforces the
global limit and can drop all highlightRows; instead stop clipping here so
cross-kind ranking can decide the final top N—remove the .slice(0, limit) and
return or propagate both taggedPageRows and highlightRows (or return the full
merged array) to the layer that performs final ranking; alternatively if you
must enforce a hard cap here, implement a reserved-budget strategy (e.g.,
allocate per-kind quotas from limit and slice each of taggedPageRows and
highlightRows accordingly) and document the reservation logic. Ensure
references: taggedPageRows, highlightRows, merged, runPdfHighlightSearch, and
limit are updated consistently.
🪄 Autofix (Beta)

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

Run ID: 80d0bb40-f5c2-41e4-b545-654ea6998c27

📥 Commits

Reviewing files that changed from the base of the PR and between 640d40d and b8d67cf.

📒 Files selected for processing (6)
  • server/api/src/__tests__/routes/search.test.ts
  • server/api/src/routes/search.ts
  • src/components/search/SearchResultCard.tsx
  • src/hooks/useGlobalSearch.test.ts
  • src/hooks/useGlobalSearch.ts
  • src/pages/SearchResults.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/hooks/useGlobalSearch.test.ts
  • src/components/search/SearchResultCard.tsx
  • src/hooks/useGlobalSearch.ts

Comment thread server/api/src/routes/search.ts Outdated
… review)

The previous `.slice(0, limit)` honored Codex's `limit` hard cap but let
page rows starve the highlight branch entirely whenever there were
`limit` or more page hits (CodeRabbit follow-up review on PR #873).

Switch to a reserved-budget merge that satisfies both reviewers:
- Highlights get up to `ceil(limit / 4)` reserved slots so the new
  feature is never silently dropped when pages dominate.
- Pages get the remainder, and unused highlight slots spill back so
  pages can still fill the response when there are no highlights.
- Total is always <= `limit` (codex's hard cap preserved).

Adds three tests: highlight reservation under page saturation, page
spill-back when highlights are empty, and full-highlight fill when
pages are empty.
@otomatty otomatty self-assigned this May 16, 2026
@otomatty
otomatty merged commit be80392 into develop May 16, 2026
17 checks passed
@otomatty
otomatty deleted the claude/fix-issue-864-rsNZe branch May 16, 2026 03:54
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.

2 participants