<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>RawCull Tech Documentation on RawCull</title><link>https://techrawcull.netlify.app/docs/</link><description>Recent content in RawCull Tech Documentation on RawCull</description><generator>Hugo</generator><language>en</language><atom:link href="https://techrawcull.netlify.app/docs/index.xml" rel="self" type="application/rss+xml"/><item><title>Thumbnails and Scan Pipeline</title><link>https://techrawcull.netlify.app/docs/thumbnail/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/thumbnail/</guid><description>&lt;h1 id="thumbnails-and-scan-pipeline"&gt;Thumbnails and Scan Pipeline&lt;a class="td-heading-self-link" href="#thumbnails-and-scan-pipeline" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;RawCull separates catalog scanning, thumbnail preloading, UI-driven thumbnail requests, and full-size preview extraction. They share RawParserKit and disk-cache infrastructure, but they have deliberately different memory-admission rules.&lt;/p&gt;
&lt;p&gt;The key current invariant is: &lt;strong&gt;catalog preloading warms the 200 px grid cache and disk cache, but only UI demand may admit an image to the preview-size RAM cache&lt;/strong&gt;. This keeps scan order from displacing the images the user is actively viewing.&lt;/p&gt;</description></item><item><title>Memory Cache</title><link>https://techrawcull.netlify.app/docs/cache/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/cache/</guid><description>&lt;h1 id="cache-system"&gt;Cache System&lt;a class="td-heading-self-link" href="#cache-system" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;RawCull uses several caches because RAW decoding is expensive and because different UI surfaces need different image sizes. The normal thumbnail path is layered RAM -&amp;gt; disk -&amp;gt; RAW extraction. The zoom path has its own disk cache for larger embedded JPEGs. Burst analysis has a separate JSON cache for derived analysis data.&lt;/p&gt;
&lt;h2 id="source-map"&gt;Source Map&lt;a class="td-heading-self-link" href="#source-map" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Cache&lt;/th&gt;
					&lt;th&gt;Main files&lt;/th&gt;
					&lt;th&gt;Stores&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Preview memory cache&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Actors/SharedMemoryCache.swift&lt;/code&gt;, &lt;code&gt;Model/Cache/CachedThumbnail.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Larger &lt;code&gt;NSImage&lt;/code&gt; thumbnails for detail/loupe use&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Grid memory cache&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Actors/SharedMemoryCache.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Small grid thumbnails, downscaled before insertion&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Thumbnail disk cache&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Actors/DiskCacheManager.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;JPEG thumbnail files under the app cache directory&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Full-size JPEG disk cache&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Actors/FullSizeJPGDiskCache.swift&lt;/code&gt;, &lt;code&gt;Model/Handlers/ZoomPreviewHandler.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Larger embedded JPEG previews for zoom&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Burst analysis cache&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Actors/BurstAnalysisCache.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;JSON snapshots of embeddings, scores, groups, rankings, and review state&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Diagnostics&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Model/Cache/CacheDelegate.swift&lt;/code&gt;, &lt;code&gt;Model/Cache/CacheStatistics.swift&lt;/code&gt;, &lt;code&gt;Views/Diagnostics/MemoryDiagnosticsView.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Hit/miss/eviction/pressure counters&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="thumbnail-lookup"&gt;Thumbnail Lookup&lt;a class="td-heading-self-link" href="#thumbnail-lookup" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre class="mermaid"&gt;flowchart TD
 A[&amp;#34;Request thumbnail&amp;#34;] --&amp;gt; B{&amp;#34;Preview memory cache?&amp;#34;}
 B --&amp;gt;|&amp;#34;hit&amp;#34;| C[&amp;#34;Return NSImage&amp;#34;]
 B --&amp;gt;|&amp;#34;miss&amp;#34;| D{&amp;#34;Disk thumbnail cache?&amp;#34;}
 D --&amp;gt;|&amp;#34;hit&amp;#34;| E[&amp;#34;Decode JPEG and refill memory&amp;#34;]
 D --&amp;gt;|&amp;#34;miss&amp;#34;| F[&amp;#34;RawParserKit extractThumbnail&amp;#34;]
 F --&amp;gt; G[&amp;#34;Create NSImage&amp;#34;]
 G --&amp;gt; H[&amp;#34;Store preview memory&amp;#34;]
 G --&amp;gt; I[&amp;#34;Store grid memory copy&amp;#34;]
 G --&amp;gt; J[&amp;#34;Save disk JPEG&amp;#34;]&lt;/pre&gt;
&lt;p&gt;The same lookup idea is used by preload and on-demand requests. &lt;code&gt;ScanAndCreateThumbnails&lt;/code&gt; warms the cache after a catalog opens. &lt;code&gt;ThumbnailLoader&lt;/code&gt; and &lt;code&gt;RequestThumbnail&lt;/code&gt; resolve individual thumbnails while the user scrolls.&lt;/p&gt;</description></item><item><title>Memory Pressure</title><link>https://techrawcull.netlify.app/docs/memorypressure/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/memorypressure/</guid><description>&lt;h1 id="memory-pressure"&gt;Memory Pressure&lt;a class="td-heading-self-link" href="#memory-pressure" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;RawCull can hold many images in memory while scanning and culling. The memory-pressure code exists to keep the app responsive when macOS reports pressure and to make cache behavior visible during development.&lt;/p&gt;
&lt;h2 id="source-map"&gt;Source Map&lt;a class="td-heading-self-link" href="#source-map" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;File&lt;/th&gt;
					&lt;th&gt;Role&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Actors/SharedMemoryCache.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Owns cache limits, memory-pressure dispatch source, pressure counters, and cache clearing&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Model/ViewModels/MemoryViewModel.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Samples total system memory, used system memory, app memory, and pressure threshold for the UI&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Model/Cache/CacheConfig.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Contains &lt;code&gt;CacheRecommendationPolicy&lt;/code&gt; and cache-limit calculation&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Model/ViewModels/MemoryDiagnosticsViewModel.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Writes diagnostics samples for cache/memory review&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Views/Settings/MemoryTab.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Displays memory stats, cache limits, and pressure status&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Views/RawCullSidebarMainView/MemoryWarningLabelView.swift&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Shows the sidebar warning during pressure&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="runtime-flow"&gt;Runtime Flow&lt;a class="td-heading-self-link" href="#runtime-flow" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre class="mermaid"&gt;flowchart TD
 A[&amp;#34;SharedMemoryCache.ensureReady&amp;#34;] --&amp;gt; B[&amp;#34;startMemoryPressureMonitoring&amp;#34;]
 B --&amp;gt; C[&amp;#34;DispatchSourceMemoryPressure&amp;#34;]
 C --&amp;gt; D{&amp;#34;event&amp;#34;}
 D --&amp;gt;|&amp;#34;normal&amp;#34;| E[&amp;#34;refreshConfig from settings/adaptive policy&amp;#34;]
 D --&amp;gt;|&amp;#34;warning&amp;#34;| F[&amp;#34;shrink cache limits to 60 percent&amp;#34;]
 D --&amp;gt;|&amp;#34;critical&amp;#34;| G[&amp;#34;clear memory caches and set 50 MB floor&amp;#34;]
 F --&amp;gt; H[&amp;#34;Notify file handler warning&amp;#34;]
 G --&amp;gt; H
 E --&amp;gt; I[&amp;#34;Clear warning&amp;#34;]&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;SharedMemoryCache&lt;/code&gt; starts monitoring lazily during &lt;code&gt;ensureReady()&lt;/code&gt;. That is called before thumbnail cache use, so the memory-pressure handler is active before large thumbnail work begins.&lt;/p&gt;</description></item><item><title>Concurrency</title><link>https://techrawcull.netlify.app/docs/concurrency-revised/</link><pubDate>Sat, 01 Aug 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/concurrency-revised/</guid><description>&lt;h1 id="concurrency"&gt;Concurrency&lt;a class="td-heading-self-link" href="#concurrency" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This page documents concurrency in the &lt;code&gt;RawCullAIModels&lt;/code&gt; branch of RawCull and in the first-party Swift packages checked out beside it. The audit covers production source in the RawCull app, &lt;code&gt;DecodeEncodeGeneric&lt;/code&gt;, &lt;code&gt;ParseRsyncOutput&lt;/code&gt;, &lt;code&gt;PhotoAIKit&lt;/code&gt;, &lt;code&gt;PhotoAnalysisKit&lt;/code&gt;, &lt;code&gt;RawCullCore&lt;/code&gt;, &lt;code&gt;RawParserKit&lt;/code&gt;, &lt;code&gt;RsyncArguments&lt;/code&gt;, &lt;code&gt;RsyncProcessStreaming&lt;/code&gt;, and &lt;code&gt;RsyncAnalyse&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;RawCull uses concurrency to keep SwiftUI responsive while it scans folders, reads RAW metadata, extracts and caches previews, calculates sharpness, creates similarity artifacts, groups bursts, runs AI models, downloads model assets, and streams rsync output.&lt;/p&gt;</description></item><item><title>Focus Mask and Sharpness</title><link>https://techrawcull.netlify.app/docs/focusmask/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/focusmask/</guid><description>&lt;h1 id="focus-mask-and-sharpness"&gt;Focus Mask and Sharpness&lt;a class="td-heading-self-link" href="#focus-mask-and-sharpness" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;RawCull&amp;rsquo;s focus system does two related jobs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;produce a visible focus mask for image inspection,&lt;/li&gt;
&lt;li&gt;compute a scalar sharpness score for sorting, burst ranking, and saved scoring.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both jobs use the same core engine, but the UI overlay and the numeric score are not identical. The overlay tries to show useful focus evidence. The score tries to rank files fairly across a catalog or burst.&lt;/p&gt;</description></item><item><title>Detailed Sharpness Scoring</title><link>https://techrawcull.netlify.app/docs/detailsharpnessscoring/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/detailsharpnessscoring/</guid><description>&lt;h1 id="detailed-sharpness-scoring"&gt;Detailed Sharpness Scoring&lt;a class="td-heading-self-link" href="#detailed-sharpness-scoring" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This page explains, step by step, where RawCull computes sharpness scores, what code is involved, what the score means, and which factors can move the result up or down.&lt;/p&gt;
&lt;p&gt;The short version: RawCull does not ask &amp;ldquo;is the whole image contrasty?&amp;rdquo;. It asks &amp;ldquo;how much reliable edge detail exists in the useful part of the image, especially around the subject or the camera AF point?&amp;rdquo;. The score is then used for sharpness sorting, burst ranking, saved scoring results, and inspection badges.&lt;/p&gt;</description></item><item><title>Detailed Focus Mask Computation</title><link>https://techrawcull.netlify.app/docs/detailsfocusmask/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/detailsfocusmask/</guid><description>&lt;h1 id="detailed-focus-mask-computation"&gt;Detailed Focus Mask Computation&lt;a class="td-heading-self-link" href="#detailed-focus-mask-computation" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This page explains, step by step, how RawCull computes the visible focus mask: where the work starts in the UI, which model and engine methods run, how the bitmap overlay is produced, what the intermediate values mean, and which factors can change the final result.&lt;/p&gt;
&lt;p&gt;The short version: the focus mask is not a camera focus-point display. It is a computed overlay built from local edge energy. RawCull first finds useful regions to inspect, then computes a Laplacian edge-energy image, ranks local patches, thresholds the strongest evidence, colorizes it, clips it to the selected patch areas, and finally draws the result over the photo.&lt;/p&gt;</description></item><item><title>Burst Groups</title><link>https://techrawcull.netlify.app/docs/burstgroup/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/burstgroup/</guid><description>&lt;h1 id="burst-groups"&gt;Burst Groups&lt;a class="td-heading-self-link" href="#burst-groups" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Burst analysis turns a flat catalog into groups of adjacent, visually similar frames. It ranks each multi-frame group, presents review queues, and supports culling decisions such as keeping the best frame, keeping the top two, deferring a group, or setting a manual pick.&lt;/p&gt;
&lt;p&gt;The implementation separates orchestration, Vision feature-print indexing, pure grouping and ranking engines, cache persistence, and the review UI.&lt;/p&gt;
&lt;h2 id="source-map"&gt;Source Map&lt;a class="td-heading-self-link" href="#source-map" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Area&lt;/th&gt;
					&lt;th&gt;Main files&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Analysis orchestration and user actions&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;sourcecode/RawCull/RawCull/Model/ViewModels/RawCullViewModel+BurstGrouping.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Similarity indexing and grouping state&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;sourcecode/RawCull/RawCull/Model/ViewModels/SimilarityScoringModel.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Pure grouping and ranking&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;sourcecode/RawCullCore/Sources/RawCullCore/BurstGroupingEngine.swift&lt;/code&gt;, &lt;code&gt;BurstRankingEngine.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Shared models&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;sourcecode/RawCullCore/Sources/RawCullCore/BurstAnalysisModels.swift&lt;/code&gt;, app &lt;code&gt;BurstAnalysisModels.swift&lt;/code&gt;, &lt;code&gt;BurstReviewQueueModels.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Cache&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;sourcecode/RawCull/RawCull/Actors/BurstAnalysisCache.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Ratings and manual overrides&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;CullingModel.swift&lt;/code&gt;, &lt;code&gt;SavedFiles.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Burst home and review list&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;BurstGroupsHomeView.swift&lt;/code&gt;, &lt;code&gt;SimilarityGridSelectionView.swift&lt;/code&gt;, &lt;code&gt;CullingGridView.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Single-burst workspace and comparison&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;BurstCullingWorkspaceView.swift&lt;/code&gt;, &lt;code&gt;ComparisonGridView.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Tests&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;RawCullCore/Tests/RawCullCoreTests/BurstGroupingEngineTests.swift&lt;/code&gt;, &lt;code&gt;BurstRankingEngineTests.swift&lt;/code&gt;, app burst/culling tests&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="end-to-end-flow"&gt;End-to-End Flow&lt;a class="td-heading-self-link" href="#end-to-end-flow" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre class="mermaid"&gt;flowchart TD
 A[&amp;#34;Analyze Bursts&amp;#34;] --&amp;gt; B[&amp;#34;RawCullViewModel.analyzeBursts&amp;#34;]
 B --&amp;gt; C{&amp;#34;Valid BurstAnalysisCache?&amp;#34;}
 C --&amp;gt;|&amp;#34;yes&amp;#34;| D[&amp;#34;Remap cached IDs and apply snapshot&amp;#34;]
 C --&amp;gt;|&amp;#34;no&amp;#34;| E{&amp;#34;Sharpness scores missing?&amp;#34;}
 E --&amp;gt;|&amp;#34;yes&amp;#34;| F[&amp;#34;Calibrate and score target files&amp;#34;]
 E --&amp;gt;|&amp;#34;no&amp;#34;| G[&amp;#34;Reuse scores&amp;#34;]
 F --&amp;gt; H{&amp;#34;Similarity embeddings missing?&amp;#34;}
 G --&amp;gt; H
 H --&amp;gt;|&amp;#34;yes&amp;#34;| I[&amp;#34;Index Vision feature prints&amp;#34;]
 H --&amp;gt;|&amp;#34;no&amp;#34;| J[&amp;#34;Reuse embeddings&amp;#34;]
 I --&amp;gt; K[&amp;#34;Group adjacent frames&amp;#34;]
 J --&amp;gt; K
 K --&amp;gt; L[&amp;#34;Rank multi-frame groups&amp;#34;]
 L --&amp;gt; M[&amp;#34;Apply manual winner overrides&amp;#34;]
 M --&amp;gt; N[&amp;#34;Save cache and review-state snapshots&amp;#34;]
 N --&amp;gt; O[&amp;#34;Show home dashboard and review queues&amp;#34;]&lt;/pre&gt;
&lt;p&gt;Every awaited phase is protected by the analysis generation and selected catalog. A cancelled or superseded run cannot publish late results into a newer catalog.&lt;/p&gt;</description></item><item><title>File Read and Write Reference</title><link>https://techrawcull.netlify.app/docs/filereadandwrite/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/filereadandwrite/</guid><description>&lt;h1 id="file-read-and-write-reference"&gt;File Read and Write Reference&lt;a class="td-heading-self-link" href="#file-read-and-write-reference" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This page lists the main places RawCull reads and writes files. Use it before changing sandbox access, cache locations, persistence, or export behavior.&lt;/p&gt;
&lt;h2 id="file-map"&gt;File Map&lt;a class="td-heading-self-link" href="#file-map" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;File/folder&lt;/th&gt;
					&lt;th&gt;Access&lt;/th&gt;
					&lt;th&gt;Owner&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;User-selected catalog folder&lt;/td&gt;
					&lt;td&gt;Read&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;RawCullViewModel&lt;/code&gt;, &lt;code&gt;ScanFiles&lt;/code&gt;, &lt;code&gt;DiscoverFiles&lt;/code&gt;, parser package&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;RAW files (&lt;code&gt;.arw&lt;/code&gt;, &lt;code&gt;.nef&lt;/code&gt;)&lt;/td&gt;
					&lt;td&gt;Read&lt;/td&gt;
					&lt;td&gt;scan, thumbnails, focus parsing, zoom, export, diagnostics&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;focuspoints.json&lt;/code&gt; beside catalog&lt;/td&gt;
					&lt;td&gt;Read optional&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;ScanFiles&lt;/code&gt; fallback&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;App Support &lt;code&gt;savedfiles.json&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Read/write&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;CullingModel&lt;/code&gt;, &lt;code&gt;ReadSavedFilesJSON&lt;/code&gt;, &lt;code&gt;WriteSavedFilesJSON&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;App Support burst cache&lt;/td&gt;
					&lt;td&gt;Read/write/delete&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;BurstAnalysisCache&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Thumbnail cache directory&lt;/td&gt;
					&lt;td&gt;Read/write/delete&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;DiskCacheManager&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Full-size JPEG preview cache&lt;/td&gt;
					&lt;td&gt;Read/write/prune&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;FullSizeJPGDiskCache&lt;/code&gt;, &lt;code&gt;ZoomPreviewHandler&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Extracted &lt;code&gt;.jpg&lt;/code&gt; sidecars&lt;/td&gt;
					&lt;td&gt;Write&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;ExtractAndSaveJPGs&lt;/code&gt;, &lt;code&gt;SaveJPGImage&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;rsync include files / process output&lt;/td&gt;
					&lt;td&gt;Write/read process stream&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;ExecuteCopyFiles&lt;/code&gt;, &lt;code&gt;ArgumentsSynchronize&lt;/code&gt;, &lt;code&gt;PrepareOutputFromRsync&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Security-scoped bookmarks&lt;/td&gt;
					&lt;td&gt;Read/write UserDefaults&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;OpencatalogView&lt;/code&gt;, copy workflow&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Settings&lt;/td&gt;
					&lt;td&gt;Read/write UserDefaults&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;SettingsViewModel&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="catalog-reads"&gt;Catalog Reads&lt;a class="td-heading-self-link" href="#catalog-reads" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The active catalog comes from the sidebar folder selection. &lt;code&gt;RawCullViewModel.startCatalogLoad(for:)&lt;/code&gt; starts security-scoped access and then runs the scan.&lt;/p&gt;</description></item><item><title>Synchronous Code</title><link>https://techrawcull.netlify.app/docs/heavy/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/heavy/</guid><description>&lt;h1 id="synchronous-code"&gt;Synchronous Code&lt;a class="td-heading-self-link" href="#synchronous-code" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Most RawCull code uses async/await, actors, and task groups. Some Apple framework work is still fundamentally synchronous: ImageIO thumbnail decode, CoreImage RAW rendering, JPEG encoding, and direct binary MakerNote parsing. This page explains where that work lives and how the app keeps it from freezing the UI or starving Swift&amp;rsquo;s cooperative executor.&lt;/p&gt;
&lt;h2 id="source-map"&gt;Source Map&lt;a class="td-heading-self-link" href="#source-map" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Area&lt;/th&gt;
					&lt;th&gt;Files&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Cancellation-aware blocking bridge&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;RawParserKit/Sources/RawParserKit/CancellableImageIOWork.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Thumbnail extraction&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;SonyThumbnailExtractor.swift&lt;/code&gt;, &lt;code&gt;NikonThumbnailExtractor.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Embedded JPEG extraction&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;JPGSonyARWExtractor.swift&lt;/code&gt;, &lt;code&gt;JPGNikonNEFExtractor.swift&lt;/code&gt;, &lt;code&gt;SonyRAWJPEGCreator.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;MakerNote parsing&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;SonyMakerNoteParser.swift&lt;/code&gt;, &lt;code&gt;NikonMakerNoteParser.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;App callers&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;RequestThumbnail.swift&lt;/code&gt;, &lt;code&gt;ScanAndCreateThumbnails.swift&lt;/code&gt;, &lt;code&gt;ExtractAndSaveJPGs.swift&lt;/code&gt;, &lt;code&gt;ScanAndExtractJPGs.swift&lt;/code&gt;, &lt;code&gt;ZoomPreviewHandler.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Sharpness scoring&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;FocusMaskEngine+Scoring.swift&lt;/code&gt;, &lt;code&gt;FocusMaskEngine+MaskGeneration.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="why-blocking-work-matters"&gt;Why Blocking Work Matters&lt;a class="td-heading-self-link" href="#why-blocking-work-matters" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Swift&amp;rsquo;s cooperative thread pool expects async tasks to suspend instead of occupying threads for long periods. ImageIO and CoreImage calls often do not suspend; they block until decode/render work is done.&lt;/p&gt;</description></item><item><title>Repository Git Workflow</title><link>https://techrawcull.netlify.app/docs/pushpull/</link><pubDate>Sun, 19 Apr 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/pushpull/</guid><description>&lt;h1 id="repository-git-workflow-linear-history"&gt;Repository Git Workflow: Linear History&lt;a class="td-heading-self-link" href="#repository-git-workflow-linear-history" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This page documents the preferred repository workflow for RawCull documentation changes: branch-based development, signed commits when configured, rebasing onto &lt;code&gt;main&lt;/code&gt;, and fast-forward integration without merge commits.&lt;/p&gt;
&lt;h2 id="create-a-branch"&gt;Create a Branch&lt;a class="td-heading-self-link" href="#create-a-branch" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git checkout -b &amp;lt;new-branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git push --set-upstream origin &amp;lt;new-branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="daily-workflow"&gt;Daily Workflow&lt;a class="td-heading-self-link" href="#daily-workflow" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You always work on a dedicated branch. Commit often as you make progress.&lt;/p&gt;
&lt;h3 id="stage-changes"&gt;Stage changes&lt;a class="td-heading-self-link" href="#stage-changes" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="commit"&gt;Commit&lt;a class="td-heading-self-link" href="#commit" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you use the Claude CLI, it can generate a &lt;a href="https://www.conventionalcommits.org/"&gt;Conventional Commits&lt;/a&gt; message from the staged diff:&lt;/p&gt;</description></item><item><title>Sony/Nikon MakerNote Parser</title><link>https://techrawcull.netlify.app/docs/sonymakernoteparser/</link><pubDate>Wed, 25 Mar 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/sonymakernoteparser/</guid><description>&lt;h1 id="sonynikon-makernote-parser"&gt;Sony/Nikon MakerNote Parser&lt;a class="td-heading-self-link" href="#sonynikon-makernote-parser" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;RawCull reads autofocus points and embedded JPEG locations directly from RAW metadata. This is implemented in &lt;code&gt;RawParserKit&lt;/code&gt;, while the app consumes the parser through the vendor-neutral &lt;code&gt;RawFormat&lt;/code&gt; protocol.&lt;/p&gt;
&lt;h2 id="source-map"&gt;Source Map&lt;a class="td-heading-self-link" href="#source-map" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Area&lt;/th&gt;
					&lt;th&gt;Files&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Format protocol/registry&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;RawParserKit/Sources/RawParserKit/RawFormat.swift&lt;/code&gt;, &lt;code&gt;RawFormatRegistry.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Sony parser&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;SonyMakerNoteParser.swift&lt;/code&gt;, &lt;code&gt;SonyRawFormat.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Nikon parser&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;NikonMakerNoteParser.swift&lt;/code&gt;, &lt;code&gt;NikonRawFormat.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Thumbnail/JPEG extractors&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;SonyThumbnailExtractor.swift&lt;/code&gt;, &lt;code&gt;NikonThumbnailExtractor.swift&lt;/code&gt;, &lt;code&gt;JPGSonyARWExtractor.swift&lt;/code&gt;, &lt;code&gt;JPGNikonNEFExtractor.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Diagnostics&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;RawParserDiagnostics.swift&lt;/code&gt;, app &lt;code&gt;RawFileDiagnostics.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;App scan consumer&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Actors/ScanFiles.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Focus-point normalization&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;RawCullCore/Sources/RawCullCore/FocusPointParser.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Tests&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;RawParserKit/Tests/RawParserKitTests/&lt;/code&gt;, &lt;code&gt;RawCullCore/Tests/RawCullCoreTests/FocusPointParserTests.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="vendor-neutral-contract"&gt;Vendor-Neutral Contract&lt;a class="td-heading-self-link" href="#vendor-neutral-contract" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;App code usually does not call &lt;code&gt;SonyMakerNoteParser&lt;/code&gt; or &lt;code&gt;NikonMakerNoteParser&lt;/code&gt; directly. It resolves a format first:&lt;/p&gt;</description></item><item><title>Security-Scoped URLs</title><link>https://techrawcull.netlify.app/docs/security/</link><pubDate>Thu, 05 Feb 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/security/</guid><description>&lt;h1 id="security-scoped-urls"&gt;Security-Scoped URLs&lt;a class="td-heading-self-link" href="#security-scoped-urls" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;RawCull is a sandboxed macOS app. Any access outside the app container must come from user consent, usually a file/folder picker. RawCull uses two security-scope patterns:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;active catalog access for browsing/culling,&lt;/li&gt;
&lt;li&gt;persistent bookmarks for the rsync copy source and destination.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="source-map"&gt;Source Map&lt;a class="td-heading-self-link" href="#source-map" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Area&lt;/th&gt;
					&lt;th&gt;Files&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Active catalog scope&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;RawCullViewModel.swift&lt;/code&gt;, &lt;code&gt;RawCullViewModel+Catalog.swift&lt;/code&gt;, &lt;code&gt;RawCullApp.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Catalog scan scope&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Actors/ScanFiles.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Copy-folder bookmarks&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Views/CopyFiles/OpencatalogView.swift&lt;/code&gt;, &lt;code&gt;SourceAndDestinationSection.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;rsync runtime scope&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Model/ParametersRsync/ExecuteCopyFiles.swift&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;File writes needing scope&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;ExtractAndSaveJPGs.swift&lt;/code&gt;, &lt;code&gt;SaveJPGImage.swift&lt;/code&gt;, rsync workflow&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="api-basics"&gt;API Basics&lt;a class="td-heading-self-link" href="#api-basics" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The core calls are:&lt;/p&gt;</description></item></channel></rss>