Skip to content

feat: add first-run setup wizard#1

Merged
tphakala merged 4 commits intomainfrom
feat/setup-wizard
Feb 7, 2026
Merged

feat: add first-run setup wizard#1
tphakala merged 4 commits intomainfrom
feat/setup-wizard

Conversation

@tphakala
Copy link
Copy Markdown
Owner

@tphakala tphakala commented Feb 7, 2026

Summary

  • Adds a 4-step setup wizard shown on first launch to guide users through essential configuration
  • Steps: Welcome → birda CLI check → Model installation (with live download progress) → Species language selection
  • Wizard is skippable and re-triggerable via Help > Setup Wizard menu item
  • Adds setup_completed flag to AppSettings to track first-run state

Test plan

  • Delete birda-gui-settings.json from userData and launch — wizard should appear
  • Walk through all 4 steps, verify birda CLI detection, model install with progress, language selection
  • Close and reopen — normal app should load (wizard skipped)
  • Use Help > Setup Wizard to re-trigger the wizard
  • Click "Skip setup" on welcome step — normal app should load immediately

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Introduced a multi-step Setup Wizard for CLI discovery, model installation (with license acceptance), and language selection.
    • Added a Help menu entry to open the Setup Wizard.
    • Real-time progress reporting during model installation and UI hooks to surface progress.
  • Improvements

    • Expanded English localization with new Wizard-related UI strings.
    • App settings now track setup completion to control wizard visibility.

Guide new users through essential configuration on first launch:
- Step 1: Welcome with skip option
- Step 2: birda CLI detection with manual path fallback
- Step 3: Model installation with live download progress streaming
- Step 4: Species language selection

Adds setup_completed flag to AppSettings, re-triggerable via Help menu.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @tphakala, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the user onboarding experience by implementing a comprehensive first-run setup wizard. This wizard guides new users through critical initial configurations, including verifying the birda command-line interface, facilitating the installation of necessary bird sound analysis models with real-time progress feedback, and allowing for the selection of the preferred species language. The introduction of this wizard aims to simplify the initial setup process, making the application more accessible and user-friendly from the very first launch.

Highlights

  • First-Run Setup Wizard: Introduces a new 4-step wizard to guide users through the initial application configuration.
  • Guided Configuration Steps: The wizard covers birda CLI verification, model installation with live progress updates, and species language selection.
  • User Flexibility: The setup wizard is skippable and can be re-accessed at any time via a new 'Help > Setup Wizard' menu item.
  • Persistent State Tracking: A new setup_completed flag has been added to AppSettings to track whether the wizard has been completed, ensuring it only appears on the first launch.
  • Real-time Model Installation Progress: Model installation now provides live progress updates to the user, improving transparency during downloads.
Changelog
  • messages/en.json
    • Added numerous new localization keys for the setup wizard's various steps, messages, and button labels.
  • shared/types.ts
    • Introduced a setup_completed: boolean property to the AppSettings interface to manage the wizard's display state.
  • src/main/birda/models.ts
    • Modified the installModel function to accept an optional onProgress callback, enabling real-time progress reporting during model downloads.
  • src/main/index.ts
    • Added a new 'Setup Wizard...' menu item under the 'Help' menu, allowing users to manually re-trigger the wizard.
  • src/main/ipc/models.ts
    • Updated the birda:models-install IPC handler to forward model installation progress messages from the main process to the renderer.
  • src/main/ipc/settings.ts
    • Initialized the new setup_completed setting to false by default, ensuring the wizard runs on first launch.
  • src/preload/index.ts
    • Whitelisted new IPC channels (birda:models-install-progress, menu:setup-wizard) to allow communication between the main and renderer processes for wizard functionality.
  • src/renderer/src/App.svelte
    • Integrated the SetupWizard component, added logic to conditionally render it based on the setup_completed setting.
    • Implemented event listeners for wizard completion and menu-triggered re-activation.
  • src/renderer/src/lib/components/SettingsPanel.svelte
    • Updated the default settings initialization to include setup_completed: true, assuming that if settings are being loaded/saved here, the setup is complete.
  • src/renderer/src/lib/components/SetupWizard.svelte
    • New Svelte component implementing the entire multi-step setup wizard UI and logic.
    • Handles birda CLI checks, model selection and installation with progress, species language configuration, and license agreement handling.
  • src/renderer/src/lib/utils/ipc.ts
    • Added new utility functions (onModelInstallProgress, offModelInstallProgress, onSetupWizard, offSetupWizard) to manage event listeners for model installation progress and setup wizard menu actions.
Activity
  • This pull request introduces a significant new feature, the first-run setup wizard, aimed at improving the initial user experience.
  • The implementation involved adding a new Svelte component for the wizard's UI and logic.
  • Application settings were updated to include a flag for tracking wizard completion.
  • The birda CLI interaction for model installation was enhanced to provide real-time progress reporting.
  • New menu options were integrated to allow users to re-trigger the wizard manually.
  • Modifications spanned across main and renderer processes, including IPC communication and localization, indicating a comprehensive development effort.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 7, 2026

Warning

Rate limit exceeded

@tphakala has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 7 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

Adds a multi-step Setup Wizard (welcome, CLI, model install, language) and integrates it into the app and menu. Implements model-install progress reporting via IPC, expands settings with setup_completed, adds localization strings, and exposes renderer IPC helpers for wizard and install progress.

Changes

Cohort / File(s) Summary
Localization & Types
messages/en.json, shared/types.ts
Added many wizard-related UI strings and appended setup_completed: boolean to AppSettings.
Default Settings
src/main/ipc/settings.ts, src/renderer/src/lib/components/SettingsPanel.svelte
Included setup_completed default (false in load, true in settings panel default) to track wizard completion.
Model install & progress (main)
src/main/birda/models.ts, src/main/ipc/models.ts
installModel signature extended to accept optional onProgress callback; stdout/stderr emitted as line-delimited progress events and forwarded to renderer via IPC.
IPC allowlist & renderer helpers
src/preload/index.ts, src/renderer/src/lib/utils/ipc.ts
Added receive channels birda:models-install-progress and menu:setup-wizard; added helper functions to subscribe/unsubscribe to install-progress and setup-wizard events.
Menu & app wiring
src/main/index.ts, src/renderer/src/App.svelte
Added "Setup Wizard..." menu item (sends menu:setup-wizard) and conditional rendering in App.svelte to show the wizard when setup is incomplete; wired IPC trigger handling.
Setup Wizard UI
src/renderer/src/lib/components/SetupWizard.svelte
New multi-step Svelte component implementing welcome, CLI discovery, model listing/installation (with license modal and progress), and language selection; exports an oncomplete prop.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Renderer as Renderer Process
    participant Main as Main Process
    participant Installer as Child Process

    User->>Renderer: Trigger model install (via Wizard UI)
    Renderer->>Main: invoke installModel(name, progressCallback)
    Main->>Installer: spawn installer process
    Installer-->>Main: stdout/stderr chunks
    Main->>Main: buffer & split into trimmed lines
    Main->>Renderer: send 'birda:models-install-progress' (line)
    Renderer->>Renderer: update install progress UI
    Installer->>Main: process exits
    Main->>Renderer: resolve installModel result
    Renderer->>User: show install completion
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main change: adding a first-run setup wizard feature, which is the primary focus across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/setup-wizard

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 and usage tips.

Comment on lines +14 to +17
const win = BrowserWindow.getFocusedWindow();
return installModel(name, (line) => {
win?.webContents.send('birda:models-install-progress', line);
});

This comment was marked as outdated.

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.

Fixed in bf2cb0b — now using event.sender with isDestroyed() guard instead of getFocusedWindow().

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.

Fixed in bf2cb0b — now using event.sender with isDestroyed() guard instead of getFocusedWindow().

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a setup wizard for the first launch of the application, which is a great feature for user onboarding. However, it introduces two potential Denial of Service (DoS) vulnerabilities in the IPC handling and process management logic, where the main process can crash if a window is closed during model installation or if the installation process produces excessive output. Additionally, there are minor improvements suggested around code duplication and error handling to enhance maintainability and debuggability.

Comment on lines +53 to +70
stdout += text;
if (onProgress) {
for (const line of text.split('\n')) {
const trimmed = line.trim();
if (trimmed) onProgress(trimmed);
}
}
});

proc.stderr.on('data', (data: Buffer) => {
stderr += data.toString();
const text = data.toString();
stderr += text;
if (onProgress) {
for (const line of text.split('\n')) {
const trimmed = line.trim();
if (trimmed) onProgress(trimmed);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-medium medium

The installModel function accumulates the entire standard output and standard error of the spawned process into memory-resident strings (stdout and stderr) without a size limit. This can lead to a Denial of Service (DoS) vulnerability where an unbounded amount of data could cause memory exhaustion and crash the main process. Additionally, processing large chunks of data with text.split('\n') without a size limit can degrade performance. Consider avoiding this accumulation or implementing a strict buffer limit. Furthermore, the logic for processing stream data and reporting progress is duplicated for both stdout and stderr. Extracting this into a shared helper function would improve maintainability and reduce redundancy.

    const reportProgress = (text: string) => {
      if (onProgress) {
        for (const line of text.split('\n')) {
          const trimmed = line.trim();
          if (trimmed) onProgress(trimmed);
        }
      }
    };

    proc.stdout.on('data', (data: Buffer) => {
      const text = data.toString();
      stdout += text;
      reportProgress(text);
    });

    proc.stderr.on('data', (data: Buffer) => {
      const text = data.toString();
      stderr += text;
      reportProgress(text);
    });

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.

Fixed in bf2cb0b — extracted reportProgress helper with proper line buffering across chunk boundaries, and flushing remainders on close.

Comment on lines +14 to +17
const win = BrowserWindow.getFocusedWindow();
return installModel(name, (line) => {
win?.webContents.send('birda:models-install-progress', line);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-medium medium

The onProgress callback captures a reference to the focused window at the time the IPC call is initiated. If the user closes this window while the model installation is still in progress, subsequent attempts to call win.webContents.send will throw an 'Object has been destroyed' error. Since this occurs within an asynchronous callback outside the main IPC handler's promise chain, it will result in an unhandled exception that crashes the entire Electron main process.

To fix this, you should either check if the window is still valid before sending, or better yet, use _event.sender to identify the window that initiated the request and check its status.

  ipcMain.handle('birda:models-install', async (event, name: string) => {
    const webContents = event.sender;
    return installModel(name, (line) => {
      if (!webContents.isDestroyed()) {
        webContents.send('birda:models-install-progress', line);
      }
    });
  });

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.

Fixed in bf2cb0b — now using event.sender with isDestroyed() check instead of getFocusedWindow().

async function handleAcceptAndInstall() {
if (!licenseModel) return;
const id = licenseModel.id;
const name = licenseModel.name;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The name variable is declared but never used. It can be removed to clean up the code.

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.

Fixed in bf2cb0b — removed the unused name variable.

if (currentStep === 'language' && availableLanguages.length === 0 && birdaStatus?.available) {
void getAvailableLanguages()
.then((langs) => (availableLanguages = langs))
.catch(() => {});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This catch block currently swallows errors silently. While an error here might be expected if birda is not yet available, it's good practice to log the error for debugging purposes. This can help diagnose unexpected issues during development.

        .catch((err) => console.error('Failed to get available languages in wizard effect:', err));

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.

Intentionally silent — these are expected failures when birda is not yet available. The wizard handles this gracefully by showing the CLI-not-found state on the next step.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/renderer/src/App.svelte`:
- Around line 38-40: handleWizardComplete currently only hides the wizard but
doesn't refresh settings; call the same app-state reload used on component mount
to re-fetch updated settings and update appState and any derived vars (e.g.,
species_language, default_model, birda_path). Specifically, inside
handleWizardComplete invoke the function you use on mount to load the app state
(the initializer that populates appState) and assign/merge its result into
appState (and update any local reactive variables) so the UI reflects changes
made by the wizard.

In `@src/renderer/src/lib/components/SetupWizard.svelte`:
- Around line 176-187: Wrap the setSettings calls in handleSkip and handleFinish
with try/catch/finally so that if setSettings throws you capture the error into
a visible component state (e.g., a new saveError / errorMessage reactive
variable) and still call oncomplete() in the finally block; update handleSkip
and handleFinish to use try { await setSettings(...) } catch (err) { saveError =
String(err) || 'Failed to save settings' } finally { oncomplete() } so the user
isn't stuck and sees an error message.
🧹 Nitpick comments (4)
src/main/ipc/models.ts (1)

13-18: Use event.sender instead of getFocusedWindow() for reliable progress routing.

BrowserWindow.getFocusedWindow() returns null if the window isn't focused at the moment the IPC call arrives (e.g., user alt-tabbed). Since the _event parameter already carries the sender's webContents, you can route progress reliably regardless of focus state.

♻️ Proposed fix
-  ipcMain.handle('birda:models-install', async (_event, name: string) => {
-    const win = BrowserWindow.getFocusedWindow();
+  ipcMain.handle('birda:models-install', async (event, name: string) => {
+    const sender = event.sender;
     return installModel(name, (line) => {
-      win?.webContents.send('birda:models-install-progress', line);
+      if (!sender.isDestroyed()) {
+        sender.send('birda:models-install-progress', line);
+      }
     });
   });
src/main/birda/models.ts (1)

51-70: Partial-line delivery across chunk boundaries.

data events from child-process streams are not guaranteed to align with newline boundaries. A single line of progress output can be split across two data callbacks, causing onProgress to receive truncated fragments. For a progress display this is cosmetic, but it can produce garbled text.

Consider buffering incomplete lines (i.e., text after the last \n in each chunk) and flushing them when the next chunk arrives or the stream closes.

Example fix for stdout (apply analogously to stderr)
     let stdout = '';
     let stderr = '';
+    let stdoutRemainder = '';

     proc.stdout.on('data', (data: Buffer) => {
       const text = data.toString();
       stdout += text;
       if (onProgress) {
-        for (const line of text.split('\n')) {
+        const chunk = stdoutRemainder + text;
+        const parts = chunk.split('\n');
+        stdoutRemainder = parts.pop() ?? '';
+        for (const line of parts) {
           const trimmed = line.trim();
           if (trimmed) onProgress(trimmed);
         }
       }
     });

Then flush the remainder in the close handler:

     proc.on('close', (code) => {
+      if (onProgress && stdoutRemainder.trim()) onProgress(stdoutRemainder.trim());
       if (code !== 0) {
src/renderer/src/lib/components/SetupWizard.svelte (2)

115-138: Unused variable name on Line 118.

const name = licenseModel.name is assigned but never read.

Suggested fix
     const id = licenseModel.id;
-    const name = licenseModel.name;
     licenseModel = null;

165-174: No guard against concurrent fetches when navigating back and forth.

If the user rapidly switches to the model or language step while a fetch is already in flight (and availableModels.length is still 0), refreshModels() / getAvailableLanguages() will be called again, potentially leading to redundant network requests or race conditions on state assignment.

Consider adding a loading flag (e.g., modelsLoading) that's checked in the $effect condition.

- Use event.sender instead of getFocusedWindow() for reliable IPC progress
  routing and destroyed-window safety
- Re-fetch settings after wizard completion so appState reflects changes
- Wrap handleSkip/handleFinish in try/catch to prevent trapping user
- Buffer partial lines from child process streams before reporting progress
- Remove unused variable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/renderer/src/lib/components/SetupWizard.svelte`:
- Around line 66-73: browseBirdaPath currently awaits openExecutableDialog()
then calls setSettings and checkBirda without error handling, so rejections are
swallowed and birdaStatus/CLI feedback aren't updated; wrap the post-selection
work in a try/catch inside browseBirdaPath, call setSettings and await
checkBirda inside the try, and in the catch set birdaStatus to an error/failure
state (and optionally log or surface the error via your existing
logger/notification mechanism) so the UI reflects failures from setSettings or
checkBirda; keep openExecutableDialog usage the same and only run the try/catch
when a path is returned.
- Around line 58-63: The checkCli function can throw during setSettings({
birda_path: birdaPath }) leaving birdaStatus null and the UI stuck; wrap the
setSettings call in a try/catch (or perform setSettings in a safe await with
.catch) and on error set birdaStatus to an explicit failure state (or a
string/error object) and/or proceed to call checkBirda() in a finally path so
the spinner is cleared and the UI can show an error; update references to
birdaStatus, setSettings, and checkBirda in checkCli to ensure birdaStatus is
always assigned regardless of setSettings failing.
🧹 Nitpick comments (3)
src/renderer/src/lib/components/SetupWizard.svelte (2)

164-172: Fire-and-forget retry in $effect can loop on persistent failures.

If refreshModels() fails (setting modelsError but leaving availableModels empty), the condition availableModels.length === 0 remains true. Any subsequent change to a tracked dependency (e.g., toggling steps back and forth) re-triggers the failing call. Consider adding a flag or a short cooldown to avoid repeated failed fetches.


383-391: Next button is disabled until a model is installed—intentional but worth a note.

If the birda CLI isn't available, the user can't install models and can't advance past this step (only go back). If this is intentional gating, consider adding a small hint message when !birdaStatus?.available to explain why install buttons aren't working, so the user knows to go back and fix the CLI step first.

src/renderer/src/App.svelte (1)

121-133: Settings-to-state mapping is duplicated.

The logic that maps getSettings() results onto appState (theme, confidence, model) appears both in onMount (Lines 123–128) and handleWizardComplete (Lines 40–45). Consider extracting a small helper like applySettings(settings) to keep them in sync.

Example
function applySettings(settings: AppSettings) {
  appState.theme = settings.theme;
  appState.analysisConfidence = settings.default_confidence;
  if (settings.default_model) {
    appState.selectedModel = settings.default_model;
  }
}

Then call applySettings(settings) in both places.

Also applies to: 38-48

Wrap setSettings/checkBirda calls in try/catch to prevent permanent
loading spinner when IPC fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment on lines +287 to +290
<button onclick={nextStep} class="btn btn-primary gap-1">
{m.wizard_next()}
<ChevronRight size={16} />
</button>

This comment was marked as outdated.

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.

Good catch! Fixed in c22fef6 — the Next button on the CLI step is now disabled when birdaStatus?.available is false, preventing users from reaching the model step without a working birda CLI. Also added a defensive warning on the model step that shows a clear message if somehow reached without CLI, instead of the infinite loading spinner.

Prevent users from advancing to the model install step without a working
birda CLI, which would trap them in an infinite loading state. Also adds
a defensive warning on the model step if reached without CLI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tphakala tphakala merged commit 2a71a7d into main Feb 7, 2026
5 checks passed
@tphakala tphakala deleted the feat/setup-wizard branch February 7, 2026 16:56
tphakala added a commit that referenced this pull request Feb 15, 2026
Issue #1 (HIGH): Ensure CPU provider always available
- Combine birda providers with default CPU and deduplicate
- Prevents losing CPU fallback if birda doesn't include it

Issue #2 (MEDIUM): Remove duplicate GpuCapabilities type
- Import GpuCapabilities from shared/types.ts in ipc.ts
- Eliminates inline type duplication
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