Features found in source
All promised features found 98 / 100
31.5 of 32 checked features (98%); 0 couldn't be verified; partial features count half.
Leaf · Version 01a1c524-8990-7e18-ac12-42284f15d3df
Checked: 2026-11-10T15:45:15.591Z · Report generation 2
Status: complete · Analysis: live
Source check ✓ 2026-11-10T11:07:59.334Z
All promised features found 98 / 100
31.5 of 32 checked features (98%); 0 couldn't be verified; partial features count half.
Good, small improvements suggested 82 / 100
3 security issue(s) found; serious issues reduce this score most. No scan guarantees safety.
Some setup steps missing 57 / 100
Present: README, run entry point, App specification or AGENTS. Missing: environment example, known install command. Static check in .; code was not executed. Checks README, environment example, install and run scripts, app specification or AGENTS, confirmed How to run it (confirmed) and run manifest (absent). The app was not executed.
Not checked
Not checked
Dependency and license evidence has not been checked.
These files are too big or are one very long line, so the AI review skipped them. The rest of the report is complete. If a file matters to you, look at it yourself.
The AI read 36 of 54 source files.
App specification source review: 31.5 of 32 features found in source (98%)
Found: 31; Partly: 1; Missing: 0; Couldn't verify: 0
Files checked: Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/EPUBImporter.swift, Sources/Leaf/DemoBook.swift
EPUBImporter.load parses title/author/chapters and rejects encrypted EPUBs; ReaderModel.load displays imported book.
Files from your spec: Sources/Leaf/ReaderModel.swift#openBook, Sources/ReaderCore/EPUBImporter.swift#load
Evidence: Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/EPUBImporter.swiftFiles checked: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/PageText.swift
Footer prev/next buttons call turnPage; turnPage computes destination page and repaginates.
Files from your spec: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift#turnPage
Evidence: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swiftFiles checked: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/ListeningExperience.swift
Contents sidebar buttons call selectChapter, which sets chapterIndex and repaginates.
Files from your spec: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift#selectChapter
Evidence: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swiftFiles checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/PageText.swift
Font size slider triggers repaginate; repaginate anchors on readingOffset/session offset.
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift#repaginate
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swiftFiles checked: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/BookLibrary.swift
Library list buttons call openLibraryBook, which resolves URL and loads with saved position.
Files from your spec: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swift#openLibraryBook
Evidence: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swiftFiles checked: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/BookLibrary.swift, Sources/Leaf/DemoBook.swift
Search field filters library by title or author case-insensitively.
Files from your spec: Sources/Leaf/BookLibraryView.swift
Evidence: Sources/Leaf/BookLibraryView.swiftFiles checked: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/BookLibrary.swift
Remove button calls removeLibraryBook, which only drops the library entry; EPUB and bookmark stores untouched
Files from your spec: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swift#removeLibraryBook
Evidence: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swiftFiles checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingPosition.swift, Sources/Leaf/BookLibrary.swift
applyRestoredPosition restores chapter, text and speech offsets; ReadingPosition and LibraryBook persist them
Files from your spec: Sources/Leaf/ReaderModel.swift#applyRestoredPosition, Sources/Leaf/ReadingPosition.swift, Sources/Leaf/BookLibrary.swift
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingPosition.swift, Sources/Leaf/BookLibrary.swiftFiles checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReaderAnnotations.swift, Sources/Leaf/BookLibrary.swift
bookmarkSelectedText appends a TextBookmark from the selection and saves it via TextBookmarkStore
Files from your spec: Sources/Leaf/ReaderModel.swift#bookmarkSelectedText, Sources/Leaf/ReaderAnnotations.swift
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReaderAnnotations.swiftFiles checked: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingPosition.swift
Sidebar bookmark button calls openBookmark, which jumps to the chapter and page containing the passage
Files from your spec: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift#openBookmark
Evidence: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swiftFiles checked: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingPosition.swift
Sidebar xmark and in-page remove button both call removeBookmark, which deletes and saves
Files from your spec: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift#removeBookmark
Evidence: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swiftFiles checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/DemoBook.swift
speak uses AVSpeechSynthesizer with a chosen installed voice; settings list Mac voices
Files from your spec: Sources/Leaf/ReaderModel.swift#speak, Sources/Leaf/ReadingSettingsView.swift
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingSettingsView.swiftFiles checked: Sources/Leaf/SpeechSettings.swift, Sources/Leaf/CloudNarrator.swift, Sources/Leaf/LocalKokoroWorker.swift
Local provider config, CloudNarrator local branch, and Kokoro worker generate found in source
Files from your spec: Sources/Leaf/SpeechSettings.swift#configuration, Sources/Leaf/CloudNarrator.swift#prepare, Sources/Leaf/LocalKokoroWorker.swift#generate
Evidence: Sources/Leaf/SpeechSettings.swift, Sources/Leaf/CloudNarrator.swift, Sources/Leaf/LocalKokoroWorker.swiftFiles checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift, Sources/ReaderCore/CloudSpeech.swift
speak uses cloud config; prepare calls client.speech; OpenRouter speech POST found in source
Files from your spec: Sources/Leaf/ReaderModel.swift#speak, Sources/Leaf/CloudNarrator.swift#prepare, Sources/ReaderCore/CloudSpeech.swift#speech
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift, Sources/ReaderCore/CloudSpeech.swiftFiles checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift, Sources/Leaf/ReaderView.swift
toggleReading pause/resume and CloudNarrator pause/resume found in source
Files from your spec: Sources/Leaf/ReaderModel.swift#toggleReading, Sources/Leaf/CloudNarrator.swift
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swiftFiles checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift
Pace picker, updateRate, and cloud setRate found in source
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift#updateRate
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swiftFiles checked: Sources/Leaf/LeafApp.swift, Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/SentenceNavigation.swift
Arrow commands, navigateReading, and SentenceNavigation.destination found in source
Files from your spec: Sources/Leaf/LeafApp.swift, Sources/Leaf/ReaderModel.swift#navigateReading, Sources/ReaderCore/SentenceNavigation.swift#destination
Evidence: Sources/Leaf/LeafApp.swift, Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/SentenceNavigation.swiftFiles checked: Sources/Leaf/PageText.swift, Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/CloudSpeech.swift
PageText mouseUp onSeek and ReaderModel.seek found in source
Files from your spec: Sources/Leaf/PageText.swift, Sources/Leaf/ReaderModel.swift#seek
Evidence: Sources/Leaf/PageText.swift, Sources/Leaf/ReaderModel.swiftFiles checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift, Sources/Leaf/PageText.swift
Word/sentence highlight found in source: CloudNarrator tick emits ranges, ReaderModel sets highlight, PageText paints it.
Files from your spec: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift#tick, Sources/Leaf/PageText.swift
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift, Sources/Leaf/PageText.swiftFiles checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReaderView.swift, Sources/Leaf/ListeningExperience.swift
toggleListeningView, fullscreen sync, and ListeningExperience overlay found in source.
Files from your spec: Sources/Leaf/ReaderModel.swift#toggleListeningView, Sources/Leaf/ReaderView.swift#syncListeningFullscreen, Sources/Leaf/ListeningExperience.swift
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReaderView.swift, Sources/Leaf/ListeningExperience.swiftFiles checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingSettingsView.swift, Sources/ReaderCore/CloudSpeech.swift
previewCloudVoice uses fixed sample text; settings buttons for local and cloud preview found in source.
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift#previewCloudVoice
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingSettingsView.swiftFiles checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift, Sources/Leaf/LocalKokoroWorker.swift
performInstall downloads runtime/model with stage text shown in settings; progress is text-only, no numeric progress.
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift#performInstall
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swiftFiles checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift, Sources/Leaf/LocalCommand.swift
cancelInstall cancels task; LocalCommand terminates subprocess and throws CancellationError, shown in source.
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift#cancelInstall
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift, Sources/Leaf/LocalCommand.swiftFiles checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift, Sources/Leaf/LocalKokoroWorker.swift
install() and Repair/check setup button re-run performInstall, found in source.
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift#install
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swiftFiles checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift, Sources/Leaf/LocalKokoroWorker.swift
Remove download button calls local.remove(), which deletes the Kokoro root directory and clears state.
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift#remove
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swiftFiles checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift, Sources/Leaf/ReaderModel.swift
Save key button calls saveKey(), which stores the key via Keychain SecItemAdd/Update.
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift#saveKey
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swiftFiles checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift, Sources/Leaf/ReaderModel.swift
Remove key button calls removeKey(), which deletes the Keychain item via SecItemDelete.
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift#removeKey
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swiftFiles checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift, Sources/ReaderCore/CloudSpeech.swift
Refresh models button awaits refreshModels(), which fetches the speech model catalog from OpenRouter.
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift#refreshModels
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift, Sources/ReaderCore/CloudSpeech.swiftFiles checked: Sources/Leaf/CloudNarrator.swift, Sources/ReaderCore/CloudSpeech.swift, Sources/Leaf/ReadingSettingsView.swift
prepare() requests timings when transcribed mode; on failure falls back to estimated timings with a warning.
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/CloudNarrator.swift#prepare, Sources/ReaderCore/CloudSpeech.swift#timings
Evidence: Sources/Leaf/CloudNarrator.swift, Sources/ReaderCore/CloudSpeech.swiftFiles checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/DictionaryLookup.swift, Sources/Leaf/DictionaryLookupView.swift
lookUpSelectedText uses LocalDictionary DCSCopyTextDefinition and shows result in DictionaryLookupView.
Files from your spec: Sources/Leaf/ReaderModel.swift#lookUpSelectedText, Sources/Leaf/DictionaryLookup.swift#definition, Sources/Leaf/DictionaryLookupView.swift
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/DictionaryLookup.swift, Sources/Leaf/DictionaryLookupView.swiftFiles checked: Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/CloudSpeech.swift, Sources/Leaf/ReadingSettingsView.swift
Local dictionary first, then OpenRouter dictionaryDefinition with saved key; found in source
Files from your spec: Sources/Leaf/ReaderModel.swift#lookUpSelectedText, Sources/ReaderCore/CloudSpeech.swift#dictionaryDefinition
Evidence: Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/CloudSpeech.swiftFiles checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/PlaybackActivity.swift, Sources/Leaf/ReaderView.swift
setActive disables idle system and display sleep; called for speaking or preview; found in source
Files from your spec: Sources/Leaf/ReaderModel.swift#updatePlaybackActivity, Sources/Leaf/PlaybackActivity.swift#setActive
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/PlaybackActivity.swiftActual run verification: Not tested. Source review does not prove the app works. Dependency warnings permit publishing; harmful content and unresolved rights or license blockers prevent it. Checks do not guarantee safety.
Medium — could affect users in some situations; plan a fix.
A crafted EPUB could cause unzip to read entries outside the intended archive path or trigger unexpected unzip behavior, potentially exposing local files or crashing the importer.
Fix hint: Validate the resolved path stays within the archive root and reject absolute or traversal paths before invoking unzip; consider a native ZIP reader instead of shelling out.
open · Review finding
ai-security · Sources/ReaderCore/EPUBImporter.swiftMedium — could affect users in some situations; plan a fix.
Users who run Kokoro setup download and execute a large Python dependency tree from the network; a compromised package or index could run arbitrary code on the user's Mac.
Fix hint: Pin and verify the requirements file hash or use a lockfile with hashes; document the trust boundary and consider sandboxing the worker process.
open · Review finding
ai-security · Sources/Leaf/Resources/LocalKokoro.requirements.txtLow — limited impact; fix during routine maintenance.
If OpenRouter is slow or unreachable, the settings UI can remain in a loading state and the user cannot easily recover.
Fix hint: Add a timeout and cancellation to the model refresh request and surface failures without blocking the settings UI.
open · Review finding
ai-security · Sources/Leaf/SpeechSettings.swiftLow — an improvement hint for the developer, with no confirmed user harm.
Fix hint: Review the platforms in your listing Basics. This information does not affect the score or publication.
open · Review finding
platform-check ·Improvement hints only; excluded from buyer headline scores.
Code cleanliness: 77 / 100 (AI maintainability review). Architecture: 79 / 100 (AI structure review). Test presence: 0 test files (presence signal only). Coverage: Not checked.
Source coverage: Sampled. Build not run by CookThisApp.