Skip to content

feat(extraction): track archive extraction progress - #219

Merged
Nat3z merged 1 commit into
mainfrom
t3code/add-extraction-progress-tracking
Aug 5, 2026
Merged

feat(extraction): track archive extraction progress#219
Nat3z merged 1 commit into
mainfrom
t3code/add-extraction-progress-tracking

Conversation

@Nat3z

@Nat3z Nat3z commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • Track archive extraction, chunk merging, and file-moving progress.
  • Display determinate or indeterminate processing states in download views.
  • Add archive size parsing and extraction progress coverage.

Testing

  • Not run.

Summary by CodeRabbit

  • New Features

    • Added progress tracking for chunk merging and archive extraction.
    • Download screens now show processing phases, percentages, and animated progress when exact values are unavailable.
    • Added support for progress reporting across ZIP and RAR extraction workflows.
  • Bug Fixes

    • Improved progress completion handling so processing reaches 100% only after finalization.
    • Enhanced archive extraction safety and cleanup through staged processing.

- Report archive extraction and chunk merging progress
- Display processing phases in download views
@Nat3z

Nat3z commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

Review complete for PR #219.

I've submitted my review as a GitHub PR review.

@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
open-game-installer-web Ready Ready Preview Aug 5, 2026 5:58am

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds progress reporting for chunk merging and archive extraction. Electron IPC and preload events carry processing phases and progress values. Frontend state and views display determinate or indeterminate processing progress.

Changes

Processing progress

Layer / File(s) Summary
Extraction progress contracts
packages/ogi-addon/src/extraction-progress.ts, packages/ogi-addon/src/extraction.ts, packages/ogi-addon/tests/extraction-progress.test.ts
Archive support checks, UnRAR detection, archive-size parsers, progress types, and parser tests were added.
Staged extraction progress
packages/ogi-addon/src/extraction.ts
Extraction now validates archives, stages files, measures extracted bytes, reports progress, merges staged files, and cleans up temporary data.
Merge and IPC progress transport
application/src/electron/handlers/handler.ddl.ts, application/src/electron/handlers/handler.fs.ts, application/src/electron/preload.mts
Chunk merging and archive extraction now send phase-aware progress through Electron IPC and preload DOM events.
Processing state and UI
application/src/frontend/store.svelte.ts, application/src/frontend/managers/DownloadManager.svelte, application/src/frontend/components/StorePage.svelte, application/src/frontend/views/DownloadView.svelte
The frontend tracks processing phases and renders determinate or indeterminate progress during merging, moving, and extraction.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant handler.fs.ts
  participant extraction
  participant preload.mts
  participant DownloadManager.svelte
  participant StorePage.svelte
  handler.fs.ts->>extraction: Start extraction with progress callback
  extraction-->>handler.fs.ts: Report extraction progress
  handler.fs.ts->>preload.mts: Send processing:progress IPC event
  preload.mts->>DownloadManager.svelte: Dispatch processing:progress DOM event
  DownloadManager.svelte->>StorePage.svelte: Update processing phase and progress
Loading

Possibly related PRs

Suggested reviewers: nat-openclaw

Poem

A rabbit hops where chunks unite,
Progress bars glow with gentle light.
Archives unfold through staged-file trails,
IPC carries status details.
“Merging chunks!” the bunny sings,
While percentages grow their wings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the archive extraction progress work, which is a significant part of the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/add-extraction-progress-tracking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Nat3z Nat3z left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review by @nat-openclaw

Summary

Solid
},
{
"file": "packages/ogi-addon/src/extraction.ts",
"line": 324,
"side": "RIGHT",
"severity": "important",
"body": "Progress polling calls getRegularFileBytes(stagingDir), which recursively readdirs and stats the entire staging tree every 150ms. For game archives with tens of thousands of files, this adds significant I/O during extraction and can slow extraction itself (each walk may take seconds while the extractor is still writing).\n\nConsider throttling more aggressively (e.g. 500ms–1s), skipping polls while measuring is true and not scheduling another until the prior walk completes, or estimating progress from archive list metadata plus a single directory size check at a lower frequency."
}
]


### Verdict



### What Looks Good



---

<details>
<summary>Findings (2)</summary>

**important** — packages/ogi-addon/src/extraction.ts:284 — Windows 7-Zip output path is passed as a single `-o${stagingDir}` argument. This PR changed from separate `-o` and `outputDir` args; combined `-o` without quoting breaks when the install path contains spaces (common on Windows, e.g. `C:\Program Files\...`). Use separate arguments like the pre-PR code: ```typescript child = yield* spawnProcess( sevenZipPath, ['x', filePath, '-o', stagingDir, '-y'], { stdio: 'ignore' } ); ```
**important** — packages/ogi-addon/src/extraction.ts:324 — Progress polling calls `getRegularFileBytes(stagingDir)`, which recursively `readdir`s and `stat`s the entire staging tree every 150ms. For game archives with tens of thousands of files, this adds significant I/O during extraction and can slow extraction itself (each walk may take seconds while the extractor is still writing). Consider throttling more aggressively (e.g. 500ms–1s), skipping polls while `measuring` is true *and* not scheduling another until the prior walk completes, or estimating progress from archive list metadata plus a single directory size check at a lower frequency.

</details>

if (process.platform === 'win32') {
child = yield* spawnProcess(
sevenZipPath,
['x', filePath, `-o${stagingDir}`, '-y'],

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windows 7-Zip output path is passed as a single -o${stagingDir} argument. This PR changed from separate -o and outputDir args; combined -o without quoting breaks when the install path contains spaces (common on Windows, e.g. C:\Program Files\...).

Use separate arguments like the pre-PR code:

child = yield* spawnProcess(
  sevenZipPath,
  ['x', filePath, '-o', stagingDir, '-y'],
  { stdio: 'ignore' }
);

return;
}
measuring = true;
activeMeasurement = getRegularFileBytes(stagingDir)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Progress polling calls getRegularFileBytes(stagingDir), which recursively readdirs and stats the entire staging tree every 150ms. For game archives with tens of thousands of files, this adds significant I/O during extraction and can slow extraction itself (each walk may take seconds while the extractor is still writing).

Consider throttling more aggressively (e.g. 500ms–1s), skipping polls while measuring is true and not scheduling another until the prior walk completes, or estimating progress from archive list metadata plus a single directory size check at a lower frequency.

@Nat3z

Nat3z commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

I hit an infra error before I could finish:

git worktree add -b pr-agent/pr-219-pr-219-1785909588468-4whj3r /home/nat/github-pr-agent-work/Nat3z/OpenGameInstaller/.worktrees/pr-219-1785909588468-4whj3r refs/remotes/pr-head-219/t3code/add-extraction-progress-tracking failed with 128
fatal: invalid reference: refs/remotes/pr-head-219/t3code/add-extraction-progress-tracking

React to this comment with 🚀 to retry.

@greptile-apps

greptile-apps Bot commented Aug 5, 2026

Copy link
Copy Markdown

Greptile Summary

Adds determinate and indeterminate progress reporting across archive extraction, chunk merging, and file-moving stages.

  • Extracts archives through a temporary staging directory while polling extracted byte counts.
  • Propagates processing progress through Electron IPC into frontend download state.
  • Updates download views to render phase labels and determinate or pulsing progress bars.
  • Adds archive-listing parsers and unit coverage for size calculation.

Confidence Score: 5/5

The PR appears safe to merge; no concrete blocking or independently actionable non-blocking defect was established.

The extraction lifecycle awaits archive processing, cleans its staging directory on both success and failure, preserves overwrite semantics during the staged merge, and propagates bounded progress state through the existing download lifecycle.

Important Files Changed

Filename Overview
packages/ogi-addon/src/extraction.ts Adds staged extraction, archive-size discovery, periodic byte-based progress reporting, overwrite-aware merging, and guaranteed staging cleanup.
packages/ogi-addon/src/extraction-progress.ts Adds platform/archive support checks and guarded parsers for 7-Zip, ZipInfo, and unrar listing output.
application/src/electron/handlers/handler.ddl.ts Reports throttled byte progress while merging chunk files and identifies the processing phase in IPC events.
application/src/electron/handlers/handler.fs.ts Forwards archive extraction progress to the renderer for downloads with an associated ID.
application/src/frontend/managers/DownloadManager.svelte Tracks moving and extraction phases, maps indeterminate progress to NaN, and clears processing state on completion.
application/src/frontend/views/DownloadView.svelte Renders processing phases with clamped determinate progress or a pulsing indeterminate bar.
application/src/frontend/components/StorePage.svelte Updates the store download card to distinguish downloading from determinate or indeterminate processing.
packages/ogi-addon/tests/extraction-progress.test.ts Covers archive support detection, unrar implementation detection, size parsing, and unsafe totals.

Sequence Diagram

sequenceDiagram
  participant UI as Download UI
  participant Manager as DownloadManager
  participant Electron as Electron handlers
  participant Extractor as Archive extractor
  participant FS as Staging filesystem
  Electron->>Extractor: extraction(archive, output, onProgress)
  Extractor->>FS: Create staging directory
  Extractor-->>Manager: processing:progress (0 or indeterminate)
  loop Every 150 ms
    Extractor->>FS: Measure extracted regular-file bytes
    Extractor-->>Manager: processing:progress (ratio)
    Manager-->>UI: Update phase and progress
  end
  Extractor->>FS: Merge staged files into output
  Extractor-->>Manager: processing:progress (1)
  Manager-->>UI: Mark processing complete
Loading

Reviews (1): Last reviewed commit: "feat(extraction): track archive extracti..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
application/src/electron/handlers/handler.ddl.ts (1)

1372-1373: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Multi-part merges do not report the new progress.

mergeChunkFilesForPart now accepts onProgress, but only mergeChunkFiles at Line 2632 supplies it. The multi-part caller at Line 1186 omits it, so a multi-part download shows the merging state without the Merging chunks phase and without merge progress. Pass a callback there as well to make the two paths consistent.

♻️ Proposed change at the multi-part call site
       // Merge chunk files
-      yield* this.mergeChunkFilesForPart(part);
+      yield* this.mergeChunkFilesForPart(part, (progress) =>
+        this.sendProgress({ progress, processingPhase: 'Merging chunks' })
+      );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/src/electron/handlers/handler.ddl.ts` around lines 1372 - 1373,
Update the multi-part caller of mergeChunkFilesForPart to pass the existing
merge-progress callback, matching the callback supplied by mergeChunkFiles.
Ensure multi-part downloads report the “Merging chunks” phase and incremental
merge progress while preserving the single-part behavior.
application/src/frontend/components/StorePage.svelte (1)

579-586: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Indeterminate progress bars lack progress semantics and a reduced-motion guard. Both views added a full-width pulsing bar for indeterminate processing. Neither bar declares progress semantics, so a screen reader user cannot perceive that processing is running or how far it has advanced. animate-pulse also runs continuously and ignores prefers-reduced-motion.

  • application/src/frontend/components/StorePage.svelte#L579-L586: add role="progressbar" with aria-valuemin/aria-valuemax, set aria-valuenow only when hasDeterminateProgress(activeDownload.progress) is true, set aria-valuetext to the processing phase otherwise, and change class:animate-pulse to class:motion-safe:animate-pulse.
  • application/src/frontend/views/DownloadView.svelte#L609-L619: add the same role="progressbar" attributes to both branches, set aria-valuenow on the determinate branch and aria-valuetext on the indeterminate branch, and replace animate-pulse with motion-safe:animate-pulse.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/src/frontend/components/StorePage.svelte` around lines 579 - 586,
Update the progress-bar elements in
application/src/frontend/components/StorePage.svelte lines 579-586 and
application/src/frontend/views/DownloadView.svelte lines 609-619: add
role="progressbar" with aria-valuemin and aria-valuemax, expose aria-valuenow
only for determinate progress and aria-valuetext with the processing phase for
indeterminate progress, and replace animate-pulse with
motion-safe:animate-pulse.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@application/src/frontend/views/DownloadView.svelte`:
- Around line 621-626: Update the progress display in the DownloadView
progress-stats markup so the ellipsis is rendered only when
hasDeterminateProgress(download.progress) is false, and change determinate
percentage formatting to Math.round(download.progress * 100), matching
StorePage.svelte.

In `@packages/ogi-addon/src/extraction.ts`:
- Around line 365-370: Update the cleanup effect in the extraction pipeline’s
Effect.ensuring block so fsAsync.rm failures are caught and converted into a
successful, non-failing cleanup result. Keep staging cleanup best-effort while
ensuring cleanup rejection cannot become an untyped defect or alter the
successful extraction outcome.

---

Nitpick comments:
In `@application/src/electron/handlers/handler.ddl.ts`:
- Around line 1372-1373: Update the multi-part caller of mergeChunkFilesForPart
to pass the existing merge-progress callback, matching the callback supplied by
mergeChunkFiles. Ensure multi-part downloads report the “Merging chunks” phase
and incremental merge progress while preserving the single-part behavior.

In `@application/src/frontend/components/StorePage.svelte`:
- Around line 579-586: Update the progress-bar elements in
application/src/frontend/components/StorePage.svelte lines 579-586 and
application/src/frontend/views/DownloadView.svelte lines 609-619: add
role="progressbar" with aria-valuemin and aria-valuemax, expose aria-valuenow
only for determinate progress and aria-valuetext with the processing phase for
indeterminate progress, and replace animate-pulse with
motion-safe:animate-pulse.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0daad6c0-e3dc-4638-b19b-3f8575c0e0fe

📥 Commits

Reviewing files that changed from the base of the PR and between 980074a and bbaf8b1.

📒 Files selected for processing (10)
  • application/src/electron/handlers/handler.ddl.ts
  • application/src/electron/handlers/handler.fs.ts
  • application/src/electron/preload.mts
  • application/src/frontend/components/StorePage.svelte
  • application/src/frontend/managers/DownloadManager.svelte
  • application/src/frontend/store.svelte.ts
  • application/src/frontend/views/DownloadView.svelte
  • packages/ogi-addon/src/extraction-progress.ts
  • packages/ogi-addon/src/extraction.ts
  • packages/ogi-addon/tests/extraction-progress.test.ts

Comment on lines 621 to +626
<div class="progress-stats">
<span class="progress-percentage">
Merging files...
{download.processingPhase ?? 'Processing files'}...
{#if hasDeterminateProgress(download.progress)}
{Math.floor(download.progress * 100)}%
{/if}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Ellipsis and percentage contradict each other.

Line 623 always appends ..., and Line 625 then appends the percentage. A determinate merge renders Merging chunks... 42%. StorePage.svelte renders the same state as Merging chunks 42% and uses Math.round instead of Math.floor. Show the ellipsis only when progress is indeterminate, and use the same rounding as StorePage.svelte.

🐛 Proposed fix
                     <span class="progress-percentage">
-                      {download.processingPhase ?? 'Processing files'}...
                       {`#if` hasDeterminateProgress(download.progress)}
-                        {Math.floor(download.progress * 100)}%
+                        {download.processingPhase ?? 'Processing files'}
+                        {Math.round(download.progress * 100)}%
+                      {:else}
+                        {download.processingPhase ?? 'Processing files'}...
                       {/if}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div class="progress-stats">
<span class="progress-percentage">
Merging files...
{download.processingPhase ?? 'Processing files'}...
{#if hasDeterminateProgress(download.progress)}
{Math.floor(download.progress * 100)}%
{/if}
<div class="progress-stats">
<span class="progress-percentage">
{`#if` hasDeterminateProgress(download.progress)}
{download.processingPhase ?? 'Processing files'}
{Math.round(download.progress * 100)}%
{:else}
{download.processingPhase ?? 'Processing files'}...
{/if}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/src/frontend/views/DownloadView.svelte` around lines 621 - 626,
Update the progress display in the DownloadView progress-stats markup so the
ellipsis is rendered only when hasDeterminateProgress(download.progress) is
false, and change determinate percentage formatting to
Math.round(download.progress * 100), matching StorePage.svelte.

Comment on lines +365 to +370
}).pipe(
Effect.ensuring(
Effect.promise(() =>
fsAsync.rm(stagingDir, { recursive: true, force: true })
)
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Cleanup rejection becomes an untyped defect.

Effect.promise treats a rejected promise as a defect, not a typed failure. fsAsync.rm with force: true still rejects for EPERM, EBUSY, or ENOTEMPTY, which occurs on Windows when an antivirus or the extractor still holds a handle on a staged file. A successful extraction then dies with a defect that ExtractionError does not describe, and the caller in handler.fs.ts cannot convert it to a user-facing message.

Make the cleanup non-failing.

🛡️ Proposed fix to make staging cleanup non-failing
     }).pipe(
       Effect.ensuring(
-        Effect.promise(() =>
-          fsAsync.rm(stagingDir, { recursive: true, force: true })
-        )
+        Effect.tryPromise({
+          try: () => fsAsync.rm(stagingDir, { recursive: true, force: true }),
+          catch: (cause) => cause,
+        }).pipe(Effect.ignore)
       )
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
}).pipe(
Effect.ensuring(
Effect.promise(() =>
fsAsync.rm(stagingDir, { recursive: true, force: true })
)
)
}).pipe(
Effect.ensuring(
Effect.tryPromise({
try: () => fsAsync.rm(stagingDir, { recursive: true, force: true }),
catch: (cause) => cause,
}).pipe(Effect.ignore)
)
);
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type ChildProcess, spawn } from 'node:child_process';
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ogi-addon/src/extraction.ts` around lines 365 - 370, Update the
cleanup effect in the extraction pipeline’s Effect.ensuring block so fsAsync.rm
failures are caught and converted into a successful, non-failing cleanup result.
Keep staging cleanup best-effort while ensuring cleanup rejection cannot become
an untyped defect or alter the successful extraction outcome.

@Nat3z
Nat3z merged commit 92aa2b6 into main Aug 5, 2026
8 checks passed
@Nat3z
Nat3z deleted the t3code/add-extraction-progress-tracking branch August 5, 2026 07:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant