arrow_back
BACK TO DOWNLOAD
FULL RELEASE ARCHIVE
Changelog History
Review the complete release logs, security audit patches, and optimizations across all builds of docurip.
v0.5.X Releases ▼
v0.5.2
2026-07-04Added
- HTML export format: Export crawled documentation as styled HTML files (individual or merged). Uses pulldown-cmark to convert Markdown to HTML with embedded CSS styling. Added
HtmlFilesandMergedHtmlvariants toExportFormat. - Virtualized ResultTree: Tree view in ResultBrowser now uses react-window for efficient rendering of large result sets. Only visible nodes are rendered, improving performance with thousands of pages.
- Lazy-loaded MarkdownPreview: MarkdownPreview component is now code-split and loaded on demand, reducing initial bundle size by ~31KB.
v0.5.1
2026-07-01Added
- Advanced Markdown cleaning pipeline: Comprehensive pre- and post-processing for cleaner output:
- Pre-processing: strips
<script>and<style>tags with content, removes empty<a href="#"></a>links before HTML-to-Markdown conversion - Extended boilerplate detection: filters cookie banners ("we use cookies"), newsletter signups ("Subscribe to our newsletter"), and copy-code buttons in addition to existing UI elements
- Post-processing: collapses excessive blank lines (3+ → 2), removes empty link syntax
[text](), and strips broken image references![alt]()
- Pre-processing: strips
v0.5.0
2026-06-28Added
- PDF/EPUB import: Import PDF and EPUB files into Markdown with automatic image extraction. New Import view with drag & drop file picker. Backend modules
importer/pdf.rsandimporter/epub.rshandle extraction viapdf_extractandepubcrates. - JSON export format: Export crawled documentation as structured JSON files (individual or merged). Each entry includes
title,url,content, andmetafields. AddedJsonFilesandMergedJsonvariants toExportFormat. - Text cleaner for imports: Configurable pipeline that strips headers, footers, page numbers, footnotes, and boilerplate from imported PDFs/EPUBs. Uses cross-page frequency analysis for header/footer detection, sequential number detection for page numbers, and zone-restricted pattern matching. Toggle in Import UI, enabled by default.
- Tauri native drag & drop: Import view uses Tauri's native file drop event instead of HTML5 drag & drop for more reliable file handling.
- Auto content extraction in crawler: When no CSS selectors are configured, the crawler now tries common content selectors (
main,article,[role="main"],#content,.content, etc.) before falling back to the full HTML body. Prevents nav, sidebar, and footer content from polluting the Markdown output. - Markdown deduplication: Post-processing step in
HtmlToMarkdownremoves duplicate text blocks (>80 chars) that appear multiple times in converted output, eliminating repeated content from pages with duplicated DOM structures.
Fixed
- JSON export title extraction: Now detects both ATX-style (
# Heading) and setext-style (Heading\n===) Markdown headings. Previously only ATX headings were detected, causing filenames likegetting-startedto appear as the title instead of the actual heading text. - Clean text toggle not working: Fixed click area (onClick was only on the small track, not the label) and stale closure in drag & drop handler (useEffect captured initial state; now uses useRef to always read current value).
- TextCleaner ineffective on real PDFs: Pre-trims excessive blank lines from
pdf_extractoutput, uses non-blank-line-aware zone indexing, expanded detection zones to 5 lines, added sequential page number detection across pages, and caps effective zone at half page height to avoid false positives on short pages. - UI boilerplate in crawled output: Strips "Copy page", "Open markdown", "Edit page" text that leaks from documentation site UI buttons into the Markdown/JSON output.
- TOC navigation polluting output: Detects and removes anchor-link table-of-contents sections (lists of
[Section](#anchor)links), including full-path variants like/docs/page#section. - Trailing heading stubs: Removes repeated heading-only blocks (no content between them) that appear at the end of pages from sidebar/mobile navigation elements. Handles both ATX (
## Heading) and setext (Heading\n----------) formats, plus short fragments like "💡Tip" interspersed between stubs.
v0.4.X Releases ▼
v0.4.2
2026-06-28Added
- "Active Crawl" navigation item: New sidebar entry appears when a crawl is running, allowing access to crawl controls (pause/cancel) from any view. Dashboard remains accessible during active crawls.
- Default settings update:
default_page_limitincreased from 50 to 1000,request_delayreduced from 1000ms to 750ms (25% faster).
Fixed
activeJobIdstracking bug: Fixed stale "RUNNING" badge issue —activeJobIdsnow only updates onjobStatusChangedevents. Jobs are correctly removed from active set when status iscompleted,failed, orcancelled. Previously, all non-status events incorrectly added the jobId to the active set.NewCrawlViewstate loss: Active crawl state is now persisted insessionStorageand restored on mount — switching between Dashboard and Active Crawl no longer loses the Live Monitor or controls. Paused jobs are now correctly restored (previously onlyrunning/queuedwere handled).- Duplicate sidebar entries: "New Crawl" and "Active Crawl" are now mutually exclusive — only one appears at a time based on whether a crawl is running.
- LiveConsole close button removed: Removed the "X" close button from the LiveConsole header — only "Clear" and "Minimize" remain, as closing the console while a crawl is active was confusing and rarely useful.
v0.4.1
2026-06-28Added
- Queue backpressure:
MAX_QUEUE_SIZE = 50_000limit with warning event when queue reaches capacity — prevents unbounded memory growth during aggressive crawls. - Vitest test suite: 8 passing frontend unit tests covering
useToasts(push, dismiss, auto-dismiss, error persistence) anduseCrawlEvents(event handling, active job tracking, 500-event cap). Includesvitest.config.ts,src/test/setup.ts, and test files for both hooks.
Fixed
- Phosphor icon
titleprop removed:titleattribute is not supported by@phosphor-icons/react— removed from all icon instances inLiveConsole.tsxto fix TypeScript build error.
v0.4.0
2026-06-28Added
- Full-text search in ResultBrowser: backend-powered content search via
search_job_results— reads.mdfiles from disk with relevance scoring and preview snippets. Triggered at 3+ characters with 300ms debounce. - ErrorKind icons in LiveConsole: error events now display visual indicators for error type — red disk icon for
Disk, orange cloud forNetwork, file-x forParse, stop sign forRobotsBlocked, and generic warning forUnknown.
Changed
job.resultskeeps only metadata in RAM:PageMetanow stores URL, title, HTTP status, and link count — Markdown content no longer sits on the heap. Eliminates O(n·content) RAM growth during large crawls.- Persist throttling:
persist_jobis no longer invoked after every page; it runs at most every 50 pages or 10 seconds, whichever comes first. - Introduced
JobStatus::Cancelled: cancelled jobs now report statusCancelledinstead ofFailed. ErrorKindfor typed error classification:CrawlEvent::Errornow carrieskind: ErrorKind(Network/Disk/Unknown), laying the groundwork for differentiated error display in the frontend.
Fixed
- Blocking
tokio::select!in crawl loop:select! { _ = persist_interval.tick() => true, else => false }blocked each loop iteration for up to 10 seconds because_ = futurealways matches andelsenever fires. Replaced with a non-blockingInstant::elapsed()check — resolves a ~7× throughput regression.
v0.3.X Releases ▼
v0.3.4
2026-06-27Added
- MIME-Type validation for asset downloads:
HttpFetcher::fetch_byteschecksContent-Typeagainst an allow-list (images, fonts, CSS, JS, JSON, PDF, audio/video, octet-stream) and rejectstext/html/application/xhtml+xmlso that error pages or login redirects served at asset URLs no longer get persisted as broken images/stylesheets. - SSRF check on the start URL:
validate_crawl_inputnow runscrawler::ssrf::is_private_targeton the submitted URL whenssrf_protectionis enabled, returning an actionable error before the crawl is even spawned. Previously SSRF was only enforced on follow-up links during the crawl. useUpdater.errorrendered in the update banner: the update banner now shows the captured error message and switches the action button label from "Install & Restart" to "Retry" when a previous install attempt failed.
Changed
- User-Agent unified to
Docurip/0.3.3inHttpFetcher(fetcher/http.rs) and the defaultAppSettings.user_agent(settings/config.rs); previously both still advertisedDocurip/0.3.1. - Dashboard stats polling throttled: stats refresh every 3 s while at least one crawl is active, otherwise every 4th tick (~12 s). Job list and recent exports continue to poll every 3 s. Keeps the live-stats UX from v0.3.2 during crawls while reducing idle backend load.
- NewCrawl logs migrated to
useRef: log entries are appended into a ref-backed array (mutated in place, capped at 500); alogTickcounter drives re-renders. Avoids per-append array-copy that grew quadratic on long crawls. walk_diris nowpubinexport.rsandcommands::export_job_zipcallsexport::zip_directoryinstead of its own inlineadd_dir_to_ziprecursion. Eliminates ~25 lines of duplicated ZIP-walking logic.- Error classification uses typed downcasts:
crawler::orchestrator::is_disk_errornow walks theanyhow::Errorchain looking forstd::io::Errorand matches onErrorKind::PermissionDenied/StorageFull/ReadOnlyFilesystem. The original substring-based logic is kept asis_disk_error_strand used as a fallback only when noio::Erroris present in the chain.HttpFetcher::is_transient_errornow downcasts toreqwest::Errorand usesis_timeout()/is_connect()/is_request(). Substring matching remains as a fallback for non-reqwest errors.
Fixed
- Headless feature build:
tab.close()now passes the requiredfire_unload: boolargument (tab.close(false)) socargo check --features headlesssucceeds against headless_chrome 1.x. Previously failed to compile in the headless build.
Tests
- 2 new tests for
is_disk_error: classifiesio::ErrorKind::PermissionDenied/StorageFull/ReadOnlyFilesystemvia cause chain; falls back to string matching when noio::Erroris present. - 1 new test for
is_allowed_asset_mimecovering images, fonts, CSS, JS, JSON, PDF, audio/video, octet-stream, charset suffixes, empty content-type, and rejection oftext/html/application/xhtml+xml.
v0.3.3
2026-06-14Added
- Window size setting: new dropdown in Settings → Window with 5 presets (1280×900 Compact, 1600×1000 Standard, 1920×1080 Full HD, 2560×1440 QHD, 3840×2160 UHD/4K). Selection applies immediately — the window resizes and centers on the current monitor without restart. Persisted across sessions via
tauri-plugin-store. On startup, the saved size is applied before the window becomes visible. Oversized selections (e.g. UHD on a 1080p display) are clamped to the available monitor dimensions with a toast notice. Minimum window size constraint of 1280×900 enforced viatauri.conf.json.
Fixed
- Dashboard stats showing zero:
DashboardStatsstruct was missing#[serde(rename_all = "camelCase")], so the backend sentpages_savedbut the frontend expectedpagesSaved— all fields wereundefinedand fell back to0. - Recent Exports always empty:
list_recent_exportsscanned the nonexistentapp_data_dir/exports/directory. Exports are actually written to{outputDir}/zip/. Rewrote the function to accept a list of job output dirs and scan each{dir}/zip/subfolder.list_exportscommand now collects unique output dirs from all active + persisted jobs.
Changed
- Removed unused
Managerimport fromcommands.rs.
v0.3.2
2026-06-14Added
- Auto-organized output folders: every crawl now creates three subfolders under the global output directory:
{outputDir}/{targetName}/main/for crawled content,{outputDir}/{targetName}/zip/for exported ZIPs, and{outputDir}/{targetName}/formats/for format exports (MD files, PDF files, merged variants). Folders are created automatically when a crawl starts — no manual setup required. - targetName extraction: the subfolder name is derived from the crawl URL's domain (e.g.
docs.example.com), keeping results organized by site. - Simplified ExportModal: export destination is now fully automatic — the format picker is all you need. ZIP exports land in the job's
zip/subfolder; all other formats (Markdown files, PDF files, merged MD, merged PDF) land in theformats/subfolder. No more manual folder picker step. - "Open folder" opens main/ subfolder: History and ResultBrowser "Open output folder" buttons now open the
main/subfolder directly, showing the crawled content instead of the parent directory. - Live dashboard stats: dashboard stats (pages saved, total size, crawl velocity, fail rate) now update in real-time during active crawls, not just after completion.
- Animated stat counters: stat cards use a smooth count-up animation with ease-out cubic interpolation when values change.
Changed
- Dashboard stats cache removed: the 30-second cache TTL that served stale zeros during active crawls has been eliminated. Stats are computed fresh on every 3-second poll.
collect_all_jobsnow async: uses.read().awaitinstead oftry_read()on bothactive_jobsandpersisted_jobsRwLocks, so active jobs are no longer silently skipped when the orchestrator holds a write lock.- Crawl velocity includes active jobs: velocity is now computed from wall-clock time (
Utc::now() - start_time) for running jobs, falling back toend_time - start_timefor completed jobs. - Total size includes active job output:
total_size_bytesnow sums output directories for all jobs (active + completed), not just completed ones. compute_velocityextracted: velocity logic moved to a dedicated function that handles both running and completed jobs.- Output directory setting moved to Settings only: the per-crawl output directory picker in New Crawl has been removed. The output directory is configured once in Settings and applies to all crawls, reducing configuration friction and ensuring a consistent folder structure.
- Export commands use subfolder structure:
export_jobandexport_job_zipnow read crawled content from{outputDir}/main/and write ZIPs to{outputDir}/zip/.export_job_v2auto-derives the destination to{outputDir}/formats/when no explicit destination is provided, with thedestinationparameter becoming optional. resolve_output_dirsimplified: generates{baseDir}/{domain}(no date/id suffix), matching the cleaner subfolder structure.
v0.3.1
2026-06-14Added
- Tests: 3 regression tests for URL-to-path query-string stripping in
writer/fs.rs(verifies?queryand#fragmentare stripped from filenames) - Logs memory cap:
NewCrawl.tsxnow caps log entries at 500, preventing unbounded memory growth during long crawls - Search debounce:
ResultSearch.tsxdebounces input by 200ms to reduce re-rendering of ResultTree and MarkdownPreview during typing - Dashboard error logging: empty
catchblocks inDashboard.tsxnow emitconsole.warnwith context for debugging
Fixed
- Startup crash:
AppState::init()calledHandle::current().block_on()before Tauri starts its Tokio runtime, causing an immediate panic; reverted to synchronousstd::fsfor the one-time startup load - prefillUrl re-trigger: removed the
if (prev.url) return prevguard inNewCrawl.tsxso quick-start URLs are always applied, even after the user has manually edited the URL field
v0.3.0
2026-06-14Added
- Domain filtering (
stay_within_domain): new config option (defaulttrue) that restricts the crawler to links within the same domain as the start URL; implemented inorchestrator.rsURL queue with checkbox in New Crawl and Settings views - robots.txt enforcement (
respect_robots_txt): newrobots.rsmodule that fetches and parsesrobots.txtfrom the target site, honoringUser-agent,Disallow,Allow, andCrawl-delaydirectives; enforced as the first URL-level filter in the crawl loop - SSRF protection (
ssrf_protection): newssrf.rsmodule that detects and blocks requests to private/internal IP addresses (loopback, link-local, RFC 1918, IPv6 ULA) via IP-literal checks, known-local hostname patterns, and DNS resolution; configurable per-crawl with checkbox UI - 10 unit tests for SSRF detection covering IPv4 private ranges, IPv6 loopback/ULA, localhost variants,
.localTLD, and public-host passthrough
Security
- XSS prevention in MarkdownPreview: added DOMPurify sanitization after Markdown-to-HTML rendering and after search-query highlighting; restricted allowed tags/attributes and blocked
javascript:URIs in links - CSP hardened: removed
'unsafe-inline'fromscript-src; setwithGlobalTauri: falseso the Content Security Policy now actually blocks injected inline scripts
Changed
useCrawlEvents: migrated fromwindow.__TAURI__.event.listenback to typedlisten()from@tauri-apps/api/eventwith proper async cleanup and TypeScript-safe event payloads- Async disk I/O:
save_job_to_disk,load_job_from_disk,load_all_jobs, anddelete_job_from_diskinstate.rsnow usetokio::fsinstead of blockingstd::fs, preventing the crawl runtime from stalling on disk operations;AppState::init()bridges the sync/async boundary viaHandle::current().block_on() persist_jobandremove_persisted_jobnow.awaitthe underlying async disk operations- System stats caching:
system.rsnow uses aLazyLocksingleton instead of creating a new> sysinfo::System::new_all()on every 2-second poll, reducing allocation overhead and improving accuracy of CPU readings - Asset download size limit:
fetch_bytesnow checks thecontent-lengthheader before consuming the response body, rejecting assets larger than 50 MB to prevent memory exhaustion from adversarial or oversized downloads - Dashboard polling merged: three separate
useEffectintervals (jobs 3s, stats 3s, exports 5s) inDashboard.tsxconsolidated into a single 3s interval - Parallel asset downloads:
orchestrator.rsasset-download loop replaced withtokio::task::JoinSetso all assets per page download concurrently instead of sequentially - Shared StatusBadge: extracted
StatusIconandStatusBadgeintosrc/components/StatusBadge.tsx;Dashboard.tsx,History.tsx, andNewCrawl.tsxnow import the shared component instead of duplicating local versions - LiveConsole event processing: fixed event loss by switching from
events[events.length - 1]to index-based tracking withlastProcessedIdxref, ensuring all events between renders are processed - History loading flicker:
loadJobsnow accepts ashowSpinnerparameter; background polls skipsetLoading(true)so the spinner only appears on initial load
Fixed
- UTF-8 panic in search preview:
extract_previewnow useschar_safe_start/char_safe_endhelpers to find valid UTF-8 character boundaries before slicing, preventing panics on non-ASCII content (e.g. German umlauts, CJK characters) - Panic-safe CSS selector: replaced
unwrap()on hardcodeda[href]selector inDomParserwithexpect()carrying an explanatory message - ZIP export path error:
export_job_zipnow propagates an explicit error whenoutput_dirhas no parent directory instead of silently falling back viaunwrap_or - Silent polling failure in New Crawl:
get_jobpolling now tracks consecutive errors via a ref; after 3 consecutive failures the interval is cleared and the job status is set tofailedso the UI reflects the broken state instead of freezing silently - Invalid exclude patterns ignored silently:
validate_crawl_inputnow validates each exclude pattern before the crawl starts and returns an actionable error if any pattern is malformed;Orchestrator::newpropagates the error via?instead of discarding it with.ok() stay_within_domainwas defined but never enforced:CrawlConfig.stay_within_domainexisted in the struct since v0.2.X but had no filtering logic — links to external domains were silently followed; now correctly skipped when enabledrespect_robots_txtwas defined but never enforced: same issue — the config field existed without any parsing or checking; now actively fetched and consulted
v0.2.X Releases ▼
v0.2.4
2026-06-05Added
- Dashboard stats expanded to 4 metric cards: Pages Saved, Total Size, Crawl Velocity (pages/min), Fail Rate
- Recent Exports panel on Dashboard — lists exported ZIPs (name, size, date), click-to-reveal in file manager
list_exportscommand +exports.rshelper scanningapp_data_dir/exports/- Top status bar: session ID (short hex) + live uptime counter
- Bottom system status bar: live CPU%, RAM used/total, active output path
get_system_stats/get_session_infocommands via newsystem.rs(sysinfo-based)- Global toast container (bottom-left, max 3 visible, auto-dismiss after 6s except errors)
- Dependencies:
sysinfo = "0.31",uuid(v4 feature)
Changed
useCrawlEventsnow pusheserrorevents into the global toast system- Main layout adjusted (
h-[calc(100vh-44px)]) to accommodate top/bottom bars
v0.2.3
2026-05-28Added
- Pause/Resume for active crawl jobs:
pause_crawl/resume_crawlcommands,should_pauseflag +Notify-based resume signal onCrawlHandle - Pause/Resume buttons in New Crawl view (orange Pause / green Resume / red Cancel)
- Disk-error auto-pause: write failures (permission denied, no space, read-only) now pause the job with an actionable hint to fix the output path and resume, instead of marking it failed
- E2E test (
tests/e2e_crawl.rs) using a wiremock static-site fixture (index + 2 sub-pages + image), verifying full crawl writes Markdown + assets with rewritten relative paths - Dependency:
wiremock = "0.6"(dev)
Changed
- Orchestrator main loop now checks
should_pausebeforeshould_stop, persists job state asPaused, aborts in-flight tasks gracefully, and resumes cleanly back toRunning
v0.2.2
2026-05-15Changed
- AppSettings default output dir to
~/.docurip;Orchestrator::newauto-creates output directory viastd::fs::create_dir_all - Dashboard stats fallback: when
job.output_diris empty, use AppSettings default output dir - Frontend event listener migrated from
@tauri-apps/api/eventlistentowindow.__TAURI__.event.listenfor reliable synchronous cleanup - EventBus
emitnow calls synchronousapp.emitfor reliable event delivery useCrawlEventscontext simplified — removederror/clearErrorstate; retains onlyeventsandactiveJobIds
Fixed
- History view polling: 3-second interval ensures job list stays in sync after navigation
- New Crawl live monitor: replaced unreliable
crawl-eventdiffing with directget_jobpolling every 2s - Config object indentation in
start_crawlinvocation - Removed unused
useCrawlEvents/ error-handling dead code fromNewCrawl.tsx - LiveConsole "Unknown event": unwrapped Tauri v2
{ id, payload }wrapper inuseCrawlEvents - App.tsx tab-switch reset: removed dynamic
key={activeTab}to prevent view unmount/remount on navigation
v0.2.1
2026-05-02Added
dirscrate for cross-platform home directory resolutionevent_bus.register()typed wrapper returning(&Self, broadcast::Receiver)withReceiver::start()
v0.2.0
2026-05-01Added
- Multi-format export: Markdown, Merged MD, PDF, Merged PDF
- ExportModal UI with format picker, headless detection, directory picker
export_job_v2command withExportFormatenum (Md, MergedMd, Pdf, MergedPdf)- Headless Chrome PDF export (feature-gated:
--features headless) check_headless_supportcommand for runtime feature detectioncopy_md_filesandmerge_md_filesfunctions inexport.rspulldown-cmarkfor MD→HTML conversion in PDF export- Footer: "made with love by moku" link to https://moku.cx
Changed
- Version: v0.1.0-alpha → v0.2.0
- Logo: 20% larger, centered in sidebar
- ExportModal: fixed centering (ported to
document.body,inset-0 m-auto)
Fixed
- ExportModal centering: framer-motion
transformconflict resolved md_to_htmlgated behindheadlessfeature- PDF export: tab leak fixed with
drop(tab), files sorted, early exit on error
v0.1.X Releases ▼
v0.1.0
2026-04-10Initial
- Tauri v2 desktop app
- Documentation crawler with HTTP and headless Chrome fetcher
- DOM parsing, HTML-to-Markdown conversion, filesystem writer
- Dashboard, New Crawl, History, Settings views
- Job persistence (disk-backed JSON)
- ZIP export