A modern QuickLook extension for macOS that allows you to preview plain text files without file extensions.
QLStephenSwift is a complete rewrite of the legacy QLStephen project using Swift and the latest macOS QuickLook framework. It provides QuickLook previews for text files that don't have file extensions, such as:
- README
- Makefile
- CHANGELOG
- LICENSE
- Shell scripts without extensions
- Configuration files
- And many more...
- ✅ Pure Swift implementation using modern QuickLook Extension framework
- ✅ Automatic text/binary file detection
- ✅ Comprehensive encoding support:
- BOM detection (UTF-8, UTF-16, UTF-32 BE/LE)
- ISO-2022-JP escape sequence detection
- Strict UTF-8 validation (RFC 3629 compliant)
- ICU statistical analysis for legacy encodings
- CJK encodings (Japanese, Korean, Chinese)
- Western encodings (Windows-1252, MacRoman)
- ✅ Intelligent encoding detection with priority-based fallback
- ✅ Configurable maximum file size limit
- ✅ Line number display (optional, configurable separator)
- ✅ RTF rendering with customizable fonts, colors, and tab widths
- ✅ Line ending preservation (LF, CR, CRLF automatically detected and maintained)
- ✅ macOS 15+ compatible (no external process dependencies)
- ✅ Excludes binary files and
.DS_Store
- macOS 15.0 or later
- Xcode 16.0 or later (for building)
- Download the latest release from Releases
- Unzip and copy
QLStephenSwift.appto/Applicationsfolder - Launch the application once to register the QuickLook extension
- Clone and build:
git clone https://github.com/MyCometG3/QLStephenSwift.git cd QLStephenSwift open QLStephenSwift/QLStephenSwift.xcodeproj - Build and run the project (⌘R)
-
Enable the extension in System Settings:
- System Settings → Privacy & Security → Extensions → Quick Look
- Enable "QLStephenSwift Extension"
-
Reset QuickLook cache and restart Finder:
qlmanage -r && qlmanage -r cache killall Finder
Configure the maximum text file size for preview (default: 100KB, range: 100KB-10MB):
defaults write group.com.mycometg3.qlstephenswift maxFileSize 204800 # 200KBValid range: 102400-10485760 bytes (100KB-10MB)
This setting controls preview truncation for text files only. See File Size Limits for details on how this interacts with binary detection.
New features for enhanced text preview:
- Line Numbers: Display line numbers with configurable separator (space, colon, pipe, tab)
- RTF Rendering: Rich text output with customizable fonts, colors, and tab widths
These features can be enabled/disabled via the application UI. For detailed configuration options and advanced settings, see FEATURES.md.
Settings are automatically migrated from the original QLStephen on first launch. For manual migration:
OLD_SIZE=$(defaults read com.whomwah.quicklookstephen maxFileSize 2>/dev/null)
[ -n "$OLD_SIZE" ] && defaults write com.mycometg3.qlstephenswift maxFileSize -int $OLD_SIZESimply select any text file without an extension in Finder and press the Space bar to preview it with QuickLook.
public.data- Generic data filespublic.content- Content filespublic.unix-executable- Unix executable files (displays shell scripts with shebangs)
This extension intentionally does not declare support for public.plain-text or public.text UTIs. Here's why:
macOS Quick Look Precedence Rules:
- System Quick Look generators (located in
/System/Library/QuickLook/) take precedence over third-party extensions for common UTIs likepublic.plain-textandpublic.text - When both system and third-party extensions support the same UTI, the system plugin wins for core/native file types
- Third-party extensions are only used when the system doesn't have a strong handler for that UTI
Our Strategy:
- By using generic UTIs (
public.data,public.content), this extension can preview files that the system doesn't handle well - This is particularly effective for files without extensions (like
README,Makefile,LICENSE) - the core use case for this extension - If we declared
public.plain-text, the system's text handler would take over for.txtfiles and most recognized text files, defeating the purpose
Result:
- Files without extensions → handled by this extension (via
public.data) - Files with
.txtor recognized text extensions → handled by system Quick Look - This division of labor ensures the extension fills the gap where the system falls short
For more details on Quick Look UTI precedence, see Apple's Quick Look documentation.
Adaptive reading strategy based on file size:
- Files ≤5MB: Entire file loaded for encoding detection and complete text decoding
- Files >5MB: First 8KB sampled to minimize memory usage
Binary classification rules (applied to sampled data):
- Immediate rejection: Any null byte (0x00) → classified as binary
- Statistical analysis: Control characters (excluding TAB/LF/CR/FF/ESC) > 30% → classified as binary
- ESC (0x1B) is allowed for ISO-2022-JP escape sequences
The preview system uses two independent size limits that serve different purposes:
Binary Detection (Analysis Phase)
- Files ≤5MB: Entire file loaded for accurate encoding detection
- Files >5MB: First 8KB sampled to minimize memory usage
- This is a hardcoded limit in
FileAnalyzer.swiftfor the analysis phase
Preview Display (Rendering Phase)
- Controlled by "Max Text File Size" setting in app UI (default: 100KB, max: 10MB)
- Text files exceeding this limit are truncated in preview
- Does not affect binary detection—truncation occurs after text validation
- Configurable via UI or
defaults writecommand
These limits are sequential and independent:
- First, binary detection runs (using 5MB threshold for sampling strategy)
- If file passes as text, preview truncation applies (using user-configured limit)
This means setting "Max Text File Size" above 5MB is valid—binary detection will still use sampling for files >5MB, but the full text content (up to the configured limit) will be displayed in preview.
The formatter automatically detects and preserves the original line ending style:
- LF (
\n) - Unix/Linux/macOS - CRLF (
\r\n) - Windows - CR (
\r) - Classic Mac OS
Detection uses single-pass iteration for efficiency, and the original style is maintained throughout formatted output.
Multi-stage detection with priority-based fallback to minimize false positives:
-
BOM Detection (highest priority)
- UTF-8, UTF-16 BE/LE, UTF-32 BE/LE
-
ISO-2022-JP Escape Sequence Detection
- Detects ISO-2022-JP by checking for escape sequences (ESC $ B, ESC ( B, etc.)
- Must be checked before UTF-8 validation (ISO-2022-JP uses only ASCII bytes)
-
Strict UTF-8 Validation (RFC 3629 compliant)
- Validates byte sequence structure
- Rejects overlong encodings and invalid code points
-
ICU Statistical Detection
- Uses Foundation's
NSString.stringEncoding(for:)with no encoding suggestions - Empty suggestions allow ICU to use full statistical analysis without bias
- Provides heuristic-based detection for legacy encodings
- Uses Foundation's
-
Priority-based Fallback (in order of strictness and regional relevance)
- Japanese: ISO-2022-JP (safety net), EUC-JP, Shift-JIS
- Korean: EUC-KR
- Chinese: GB18030, Big5, GB2312
- Western: Windows-1252, MacRoman
- UTF-16/32 BE/LE without BOM (rare, last resort)
-
Lossy UTF-8 (final fallback)
- Replaces invalid sequences with U+FFFD replacement characters
The original QLStephen uses legacy QuickLook Generator plugins with Objective-C and external dependencies (file command, libmagic). These aren't available in modern macOS sandbox environments.
QLStephenSwift modernizes the approach:
- ✅ Pure Swift implementation with modern QuickLook Extension framework
- ✅ No external dependencies (compatible with macOS 15+ sandbox)
- ✅ Enhanced encoding detection (CJK languages, strict UTF-8 validation)
- ✅ App Extension architecture for better security and reliability
- Verify extension is enabled: System Settings → Privacy & Security → Extensions → Quick Look
- Reset QuickLook:
qlmanage -r && qlmanage -r cache - Restart Finder:
killall Finder - Check which extension handles files:
qlmanage -m | grep public.data
QLStephenSwift uses the modern App Extension architecture (.appex), not legacy Quick Look Generators (.qlgenerator). To verify and manage the extension, use the pluginkit command:
List all Quick Look Preview extensions:
pluginkit -m -p com.apple.quicklook.previewFind QLStephenSwift extension specifically:
pluginkit -m -i com.mycometg3.qlstephenswift.qlstephenswiftpreviewAlternatively, filter by protocol:
pluginkit -m -p com.apple.quicklook.preview | grep -i qlstephenForce re-register the extension (if not appearing):
pluginkit -a /Applications/QLStephenSwift.app/Contents/PlugIns/QLStephenSwiftPreview.appexNote: The qlmanage command is useful for testing previews and clearing caches, but pluginkit is the proper tool for managing App Extension-based Quick Look plugins. Legacy .qlgenerator plugins are deprecated in modern macOS versions.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
- Original QLStephen by Duncan Robertson
- Inspired by the need for a modern, Swift-based QuickLook solution
- Implementation assisted by GitHub Copilot with Claude Sonnet 4.5
QLStephenSwift
- MyCometG3
Original QLStephen
- Duncan Robertson
- And many contributors