Conversation
Add a new .monospace CaptureOutputPreset that uses OCR block boundingBox positions to produce monospace-aligned text: - y-clustering groups blocks into lines - x-sorting orders blocks within each line - x-differences convert to spaces to preserve horizontal position - y-differences convert to blank lines using median line height Interface changes: - CaptureOutputFormatter.format() / clipboardPayload() accept optional OCRResult parameter - CaptureOutputWriter.copyCapturedText accepts optional OCRResult - CaptureCoordinator protocol and implementations pass OCRResult through - Main capture flow and watch mode pass the actual OCRResult - History repaste / menu bar / table review pass nil (no block data) UI: - Monospace appears automatically in output preset menus (allCases) - Menu bar icon: textformat - Localized titles in Turkish and English Tests: - OCRResultTests: 8 new tests covering horizontal alignment, multi-line, blank line insertion, empty/single block, x-sorting, explicit columns - CaptureOutputFormatterTests: 2 new tests for monospace preset with and without OCRResult
Replace the standalone .monospace output preset with a toggle-based feature. When enabled, the capture pipeline uses OCR block boundingBox positions to produce monospace-aligned text that preserves horizontal and vertical layout. Algorithm (ported from ocr-layout.swift): - Fixed-column horizontal alignment: col = Int(x * columns), pad to col - y-clustering into rows (threshold 0.02) - Median row height for blank line estimation - Vertical gaps convert to blank lines (capped at 6) Changes: - AppModels: MonospaceLayoutSettings struct + UserDefaults store (isEnabled Bool, columns Int default 120, clamped 20-300) - AppState: @published monospaceLayoutSettings, setters, persist - OCRResult: monospaceAlignedText(columns:) using fixed-column algorithm - CaptureCoordinator: resolvedRawText(for:) applies monospace when toggle ON; works for both normal capture and watch mode - Settings UI: Toggle + column slider (40-200) in General tab, under Output Format section - Tests: 9 new OCRResultTests covering horizontal position, multi-line, blank lines, empty/single block, x-sorting, explicit columns, clamping No changes to CaptureOutputFormatter/CaptureOutputWriter interfaces — the toggle is applied at the coordinator level before text reaches the formatter, so it composes with any existing output preset.
Adds a Umi-OCR-style gap tree algorithm that detects column boundaries from continuous vertical gaps and sorts blocks in human reading order (left column fully, then right column). Algorithm: - detectVerticalCuts: divide y-axis into 20 bands, find x gaps present in >=40% of bands, merge nearby cuts - detectHorizontalCuts: cluster into rows, find gaps >2x median as paragraph boundaries - buildLayoutTree: recursive split by vertical cuts first (columns), then horizontal cuts (row groups), leaf nodes contain blocks - traverseLayoutTree: pre-order traversal gives reading order Integration: - monospaceAlignedText gains multicolumnSorting parameter - MonospaceLayoutSettings gains multicolumnSortingEnabled flag - Settings UI: sub-toggle under Monospace Layout section - Works for both normal capture and watch mode Tests: - Two-column layout reads left column first - Single-column layout unchanged - monospace with sorting: left rows before right rows - monospace without sorting: same-y blocks interleave on one line
luw2007
force-pushed
the
feature/gap-tree-multicolumn-sorting
branch
from
September 11, 2026 07:47
7bd3532 to
380e491
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds on #1 (Monospace Layout toggle). Adds a Umi-OCR-style gap tree algorithm that detects column boundaries from continuous vertical gaps and sorts OCR blocks in human reading order — the left column is read fully top-to-bottom before moving to the right column.
This solves the multi-column document problem where naive y-sorting interleaves rows from different columns (e.g.,
Left1 Right1\nLeft2 Right2instead ofLeft1\nLeft2\nRight1\nRight2).Algorithm
detectVerticalCuts: Divide the y-axis into 20 bands. For each band, find x-ranges with no blocks. An x-position is a column boundary if it's a gap in ≥40% of bands. Nearby cuts are merged.
detectHorizontalCuts: Cluster blocks into rows, then find row gaps exceeding 2× the median gap — these mark paragraph/section boundaries.
buildLayoutTree: Recursively split the block set — vertical cuts first (columns), then horizontal cuts (row groups). Leaf nodes hold unsplit blocks.
traverseLayoutTree: Pre-order traversal — left subtree before right (for vertical splits), top before bottom (for horizontal splits).
The algorithm is OCR-engine-agnostic; it only requires
(x0, y0, x1, y1)per block.Changes (gap-tree-specific)
Models/OCRResult.swiftreadingOrderSortedBlocks()+LayoutNodeenum +detectVerticalCuts,detectHorizontalCuts,buildLayoutTree,traverseLayoutTree;monospaceAlignedTextgainsmulticolumnSortingparameterModels/AppModels.swiftMonospaceLayoutSettings.multicolumnSortingEnabledflagAppState.swiftsetMonospaceMulticolumnSorting(_:)setterServices/CaptureCoordinator.swiftmulticolumnSortingthrough in both capture and watch modesViews/Settings/SettingsTabViews.swiftViews/SettingsView.swiftScreenTextGrabTests/OCRResultTests.swiftDesign decisions
monospaceAlignedText. WhenmulticolumnSorting: false(default), behavior is unchanged.Testing
swiftc -typecheckpasses with zero errors.