Fix: startup indexing progress rendering - #184
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes incorrect startup indexing progress rendering in Obsidian by avoiding multi-node JSX text reconciliation issues and by making progress updates more responsive during long initial indexing.
Changes:
- Reworks the startup progress UI to render/update the progress as a single DOM text node.
- Adds a periodic yield mechanism during indexing and emits
index-progressevents to keep the UI repainting.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/ui/loading-boundary.tsx | Switches progress rendering to a ref-driven single text node and subscribes to index-progress events for updates. |
| src/index/datacore.ts | Adds periodic yielding during initialization and triggers index-progress events to drive the loading UI. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Local CI ResultsPR: #184 | SHA:
Overall: ❌ 1 check failed (pre-existing, not introduced by this PR)Failures
|
Automated Code ReviewRisk Level: Medium | Verdict: MINOR_CONCERNS
Opened IssuesSummaryThe PR cleanly fixes the duplicated-count rendering (single text node via a ref callback) and adds periodic yields so the loading counter repaints during long indexing — both reasonable and well-scoped. One real edge case: when the files remaining after the last periodic yield all finish inside the 16 ms yield window ( |
Local CI ResultsPR: #184 | SHA:
Overall: ❌ 1 check failed (pre-existing, not introduced by this PR)SummaryAll 321 tests across 17 test suites pass. TypeScript type checking passes. Build completes successfully. The only failure is a pre-existing prettier formatting issue in Failures
|
Automated Code ReviewRe-review — PR updated since the prior review (prior SHA Risk Level: Medium | Verdict: CLEAN What changed in the delta (commit
|
| Persona | Severity | Finding |
|---|---|---|
| Security Auditor | — | None. No new inputs, secrets, auth, or attack surface in the delta. |
| Regression Skeptic | — | Resolved. The prior P2 (terminal count stuck for targetTotal % 32 ∈ [1,8] vaults) is fixed by the new terminal-state branch. No new regression introduced. |
| Performance Critic | — | Delta adds one extra index-progress trigger at completion (negligible). The prior P3 yield-drain tradeoff is unchanged and acceptable. |
| Architecture Judge | P3 | (unchanged from prior review) window.setTimeout is still not registered with the Component lifecycle (registerInterval), so it isn't cleared on unload — the callback is guarded by this.active so it's safe, just not clean. index-progress is still fired via this.core.events.trigger(...) directly, bypassing the typed trigger overloads on Datacore. |
| QA Lead | P3 | (unchanged) No automated coverage for the yield/progress logic; the 321 passing tests are pre-existing and don't exercise this path. The manual test (1,751 files) now exercises the terminal branch, but the small-vault edge case (targetTotal % 32 ∈ [1,8]) still lacks an automated test. |
| Product Reliability | — | Pending setTimeout survives unload (≤16 ms, guarded by active → no-op). No observability gap. |
Opened Issues
- [Auto-Detected] Startup progress counter can get stuck before targetTotal for small vaults #186 — Resolved and closed. The terminal-state branch in this commit addresses the finding; closing comment posted with the fix trace.
Summary
The follow-up commit is a minimal, correct fix that resolves the only substantive finding from the prior review (#186, P2). The terminal index-progress event now fires exactly once at completion (initialized is monotonic and single-threaded inside handleResult, so no double-fire or race). Remaining items are P3 tech-debt nits (lifecycle registration of the timeout, typed trigger overload, test coverage) that are non-blocking and suitable for optional follow-up. Safe to merge.
| window.setTimeout(() => { | ||
| this.yielding = false; | ||
| this.runNext(); | ||
| }, DatacoreInitializer.YIELD_DELAY_MS); |
There was a problem hiding this comment.
🔍 [Architecture Judge] P3: window.setTimeout is not registered with the Component lifecycle (registerInterval), so it isn't cleared on unload. The callback is guarded by this.active so it's safe, just not clean. Consider this.registerInterval(fn, YIELD_DELAY_MS) or tracking the timer for cleanup in onunload.
| this.runNext(); | ||
| // Always publish the terminal state, even if the final import completes during a yield. | ||
| if (this.initialized == this.targetTotal) { | ||
| this.core.events.trigger("index-progress"); |
There was a problem hiding this comment.
🔍 [Architecture Judge] P3: index-progress is fired via this.core.events.trigger(...) directly, bypassing the typed trigger overloads on Datacore (lines 292-300). Consider adding an index-progress overload so the event is type-checked at the call site.
Local CI ResultsPR: #184 | SHA:
Overall: ❌ 1 check failed (pre-existing, not introduced by this PR)Failures
|
Fixes #183.
Changes
Testing
1751 / 1751.