Search changes when the words live inside an image. A filename cannot describe a screenshot. A scanned PDF may contain no text layer. A browser page may hide its useful text inside HTML.
Syzygy builds a local search index for those cases. The indexer does not replace your history. It reads history events. It extracts searchable fields.
It writes a durable search document. The original item remains the source of truth.
A search result has two timelines
The history timeline starts at capture. The search timeline starts when an index job finishes. The two moments can differ. The list can show a new item first.
The search index can fill its body fields later. OCR can take longer than a title update. Large files can wait for a worker. The UI should expose that difference.
It should not report a missing result as data loss. It should not block capture while OCR runs.
The durable job path
Each index job carries an item identity. It carries a content fingerprint. It records a job status. It records retry information.
A worker claims a bounded batch. The worker reads the content it can access. It extracts text and metadata. It builds a search document.
It upserts or deletes the document. It marks the job finished. If extraction fails, it records the error. The job can retry.
A stale claim can return to the queue after a restart. The queue prevents one crash from losing an indexing request.
What the search document contains
The document identifies the original item. It stores the peer or origin context. It stores the sequence value. It stores the content kind.
It stores metadata tokens. It stores the file name. It stores body text when extraction succeeds. It stores OCR text when recognition succeeds.
It stores tags. It stores the source application. It stores date fields for filtering. The document is an index projection.
It is not a second blob store. Opening a result returns to history. The history path checks local materialization. The user sees the current content state.
Text extraction starts with safe bytes
Plain text goes directly to extraction. Markdown goes directly to extraction. Source code keeps its line structure. UTF-8 BOM is removed.
UTF-16 little-endian BOM is handled. UTF-16 big-endian BOM is handled. Invalid UTF-8 stops at the last valid boundary. The extractor avoids producing broken replacement text.
The index receives content that a search query can compare.
HTML extraction removes transport noise
HTML can contain a useful article. It can contain scripts. It can contain styles. It can contain templates.
It can contain comments. It can contain hidden metadata. The extractor ignores non-content nodes. It keeps visible text.
It keeps block boundaries. It keeps meaningful line breaks. It keeps source whitespace inside code-preserving containers. It can use image alt text as a fallback clue.
The search document receives readable text. The original HTML remains available for the preview surface.
PDF uses text before OCR
A text-native PDF already has a usable text layer. Syzygy extracts that text first. The indexer avoids OCR when extraction succeeds. The result arrives with less CPU work.
A scanned PDF can have an empty text layer. The content service renders a bounded number of pages. The current default covers the first two pages. The PDF size limit is 32 MiB.
OCR turns the rendered pages into text. The search document stores the extracted text. The PDF binary remains unchanged.
Image OCR handles screenshots
Images can arrive from a screenshot tool. They can arrive from a browser copy. They can arrive from a synced device. The OCR provider can be PaddleOCR.
The OCR provider can be the Tesseract CLI. Large images can be resized before recognition. The result maps bounding boxes back to original coordinates. The language hint selects provider settings.
The output can group text by language. The OCR result is stored as structured JSON. The search index uses its text fields. The preview can use its coordinates.
OCR cache keys protect correctness
An OCR result belongs to an input image. It also belongs to a provider. It belongs to a language setting. It belongs to a pixel limit.
It belongs to an engine key. It belongs to a coordinate version. Those values form the cache key. Changing the provider creates a new result.
Changing the language creates a new result. Changing the resize limit creates a new result. Changing coordinate semantics creates a new result. The cache applies a TTL.
The cache applies a maximum entry count. Expired entries leave the source item unchanged.
File indexing follows explicit limits
FilesV2 can contain several files. The indexer can index file names. It can index file extensions. It can index readable file bodies.
It can index PDF content. It can index image OCR text. Glob filters decide which files enter extraction. Byte limits protect the worker.
Concurrency limits protect the desktop. The collection remains addressable by its member identity. Two members can share a basename. The search document keeps a stable occurrence or content reference.
Advanced search narrows the work
Search can filter by case sensitivity. It can filter by file extension. It can filter by maximum bytes. It can filter by peer.
It can filter by source application. It can filter by start date. It can filter by end date. The query has a result limit.
The scan has a work limit. The request has a concurrency limit. The limits protect interactive use. The result still points to history.
Tags and full text work together
Tags provide a human category. OCR provides words from an image. File names provide a coarse filter. The source app provides context.
The date range provides a time boundary. Combine two of these signals for a focused
search. Search research inside a project tag. Search an error code inside screenshots
from a support app.
Search a PDF term inside a date range. The index does not need a separate database query service.
What happens when a job fails
The job records an error. The item remains in history. Metadata can still be searchable. The worker can retry the body extraction.
An OCR provider can be unavailable. The indexer can keep the file name. The indexer can fill OCR text later. A malformed document should not crash the batch.
A stale worker claim should not block the queue. A deleted item should produce an index delete. An older upsert should not revive a deletion.
Search diagnostics for a missing result
Confirm the item exists in history. Confirm the item is not a draft-like local state. Check whether an index job exists. Check the fingerprint.
Check the job status. Check the extraction error. Check the OCR provider status. Check the query filters.
Check the date range. Check the language of the query. Check whether the result was deleted. Search should report an index delay separately from a missing item.
Search diagnostics for stale text
Compare the item content hash. Compare the index fingerprint. Check for a pending upsert. Check for a failed replacement job.
Check whether the UI cached an older result. Open the item from history. The history content remains the final reference.
A real workflow: meeting screenshot
Capture a screenshot of a meeting slide. The history item appears immediately. The index job enters the queue. The worker runs OCR.
The result stores detected text. The item receives a project tag. Search the project tag. Add one phrase from the slide.
Open the matching item. Copy the original image. The OCR text helps you find the image. It does not replace the image.
A real workflow: scanned invoice PDF
Copy the PDF into history. The PDF appears before OCR completes. The extractor checks for embedded text. The text layer is empty.
The service renders the first pages. OCR extracts the invoice number. The indexer stores the number. Search finds the PDF by that number.
Open the result. Use the PDF preview for the original layout. Use TextWorkbench for a text snapshot if needed.
A real workflow: code search
Copy a command from a terminal. The content plan marks source text. The indexer stores the command. Search the option name later.
Filter by the source application. Open the source preview. Copy the exact command. The search result does not normalize away important punctuation.
The history item remains the source.
Questions people ask
Does search need an internet connection?
The local index runs on the device. Provider-backed OCR can have its own configuration. The history and metadata search remain local.
Why did a new item not appear in search?
The index job may still be pending. Large files may wait for a worker. Check the job state before copying the item again.
Can the index search image pixels?
The index searches OCR text and metadata. It does not compare pixels as an image similarity service.
How many PDF pages use OCR?
The current default renders at most the first two pages. The PDF has a 32 MiB limit.
Can I search a PDF with an existing text layer?
Yes. The extractor reads embedded text before OCR.
Does OCR modify the screenshot?
No. OCR adds a structured result and index fields.
Can a plugin open the index directory?
Plugins should use the search API. The index directory remains an internal implementation detail.
What happens after permanent deletion?
The search document is deleted. Synchronization receives deletion evidence.
Can tags filter OCR results?
Yes. Tags and full text are separate fields that can be combined in a query.
Does a failed OCR job delete the item?
No. The item remains in history. The job records the failure.
A verification checklist
Capture a text item. Search its exact phrase. Capture a screenshot. Wait for OCR.
Search a visible word. Copy a text-native PDF. Search its embedded phrase. Copy a scanned PDF.
Search an OCR phrase. Apply a tag. Combine the tag and phrase. Delete the item forever.
Confirm its search result disappears. Restart the desktop. Confirm pending jobs resume.
Search results should show their evidence
The same phrase can appear in a file name, a tag, a PDF text layer, or OCR output. A useful result tells you which source matched. That label explains why one result opens immediately while another still waits for a content block or an OCR job.
Opening a result returns to the original history item and keeps its context. A PDF result can show the page, an OCR result can show the image region, and a code result can show the source application. The search snippet narrows the choice; it does not replace checking the source.
Multilingual OCR needs the same honesty. Store the language setting and the recognition time with the derived result. Changing the language creates a new result instead of silently changing the old one. Users can then decide whether to rerun recognition or keep the existing snapshot.
query
├─ match: file name, tag, text layer, or OCR
├─ state: ready, pending, or failed
├─ context: page, region, member, or source app
└─ action: open the original item and verify
A compact support report
When a result is missing, record the operation time, item kind, job state, fingerprint state, provider status, query filters, and whether the item is local. That is enough to separate a delayed job from a malformed file or a stale view.
Do not attach the screenshot text, a PDF body, or a customer file to prove the problem. A short report such as “scanned PDF, page one rendered, OCR job failed after the size check” gives a support engineer a reproducible branch without copying the work itself.
The index is an entrance to history. It earns trust when every result carries a source, a state, and a path back to the original content.
Continue reading
Read persistent Tags and Trash for category and deletion state. Read TextWorkbench and translation for turning extracted text into a new snapshot. Read P2P sync for how headers and content reach another device. Local search works when the index tells the truth about its own delay.