Skip to content

feat(ui): redesign home around launch readiness - #180

Merged
HsiangNianian merged 4 commits into
mainfrom
feat/alpha7-home-readiness
Jul 29, 2026
Merged

feat(ui): redesign home around launch readiness#180
HsiangNianian merged 4 commits into
mainfrom
feat/alpha7-home-readiness

Conversation

@HsiangNianian

@HsiangNianian HsiangNianian commented Jul 29, 2026

Copy link
Copy Markdown
Member

Description

Redesigns the launcher home as a launch-readiness command center. The primary action now follows the real account, instance, Java, memory, file, download, and game-process state, with recovery actions and diagnostics kept in the same workflow.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • UI/UX improvement
  • Performance improvement
  • Code refactoring (no functional changes)
  • Configuration change
  • Test addition or update

LLM-Generated Code Disclosure

  • This PR contains LLM-generated code, and I provide quality assurance
  • This PR contains LLM-generated code, and I do not provide quality assurance
  • This PR does not contain LLM-generated code

Related Issues

Closes #174

Changes Made

Backend (Rust)

  • Add a typed get_launch_readiness command that shares Java resolution rules with the real launch path.
  • Validate version metadata, client artifacts, runtime selection, and instance memory before reporting ready.

Frontend (React)

  • Replace the decorative home with one state-driven launch command center.
  • Cover no-account, no-instance, not-ready, downloading, launching, running, stopped, failed, and ready states.
  • Add live download aggregation, retained session logs, exit/error recovery, instance switching, and real corrective actions.
  • Keep the core workflow usable at 1024x768 and 905x575, with keyboard, reduced-motion, dark/light, English, and Chinese coverage.

Configuration and documentation

  • Preserve the runtime invoke adapter when regenerating the Tauri TypeScript client.
  • Upload lightweight Linux screenshot candidates only when visual CI fails.
  • Document readiness, recovery, events, fixtures, and troubleshooting in English and Chinese.

Testing

Test Environment

  • OS: macOS local; Ubuntu GitHub Actions
  • DropOut Version: 0.2.0-alpha.7 development
  • Minecraft Version Tested: deterministic 1.21.1 fixture
  • Mod Loader: deterministic Fabric 0.16.14 fixture

Test Cases

  • Tested on Windows
  • Tested on macOS
  • Tested on Linux
  • Tested with vanilla Minecraft
  • Tested with Fabric fixture
  • Tested with Forge
  • Tested game launch lifecycle through the deterministic runtime fixture
  • Tested authentication recovery flow through the deterministic runtime fixture
  • Tested Java readiness and repair routing

Validation

  • pnpm -C packages/ui test:ui — 80/80 passed locally
  • Ubuntu UI Checker run 30461838209 — lint, build, and 80/80 regression suite passed
  • pnpm docs:build
  • Rust readiness command test
  • Pre-commit Rust formatting, cargo check, and Clippy

Steps to Test

  1. Run pnpm -C packages/ui test:ui.
  2. Open fixture states with ?fixture=ready, downloading, failed, or the other documented state names.
  3. Verify launch, stop, recovery, keyboard navigation, locale, theme, and both target viewports.

Checklist

Code Quality

  • My code follows the projects style guidelines
  • I have performed a self-review of my own code
  • I have commented code where the intent is not evident from types and naming
  • My changes generate no new warnings or errors

Testing Verification

  • I have tested my changes locally
  • I have added tests that prove the feature works
  • New and existing tests pass locally with my changes
  • I have tested on at least one target platform

Documentation

  • I have updated the documentation accordingly
  • I have updated the README
  • I have added or updated code comments where necessary

Dependencies

  • I have checked that no unnecessary dependencies were added
  • No new dependencies were introduced
  • Lockfiles are unchanged

Screenshots / Videos

The automated suite records dark/light baselines for all command-center states at 1024x768 and 905x575 on macOS and Linux.

Additional Notes

Real platform packaging and live Minecraft smoke testing remain milestone release-gate work; this PR validates the launcher state machine and recovery UX deterministically.

Breaking Changes

None.

Summary by Sourcery

Redesign the launcher home into a state-driven launch readiness command center that tracks account, instance, Java, memory, files, downloads, and game lifecycle, with recovery actions and diagnostics integrated into the primary workflow.

New Features:

  • Introduce a backend launch readiness API that validates version metadata, client artifacts, Java runtime compatibility, and instance memory before allowing launch.
  • Add a state-based home view that adapts the primary action and messaging across authentication, instance setup, readiness, downloading, launching, running, stopped, failed, and data-error states.
  • Provide live download monitoring for game and Java assets with aggregated progress, file details, and accessible status messaging.
  • Persist recent launcher and game logs on the home page with shortcuts to open the full Minecraft log directory.
  • Support deterministic launcher fixtures for all launch and recovery states, including locale (English/Chinese) and theme variations.

Enhancements:

  • Refine game lifecycle state management to record last exits and failures, stream launcher/stdout/stderr logs, and expose them to the UI for recovery UX.
  • Extend auth, instance, and settings stores with explicit load/error status to drive readiness resolution instead of relying on implicit data presence.
  • Integrate download and game event listeners during app bootstrap so fixture and real environments share the same runtime wiring.
  • Adjust language handling to update the document language attribute in sync with i18n, improving accessibility and fixture-driven locale testing.

Build:

  • Adjust Tauri TypeScript client generation to import invoke via the launcher runtime adapter, keeping browser fixtures and production builds aligned.

CI:

  • Enhance the GitHub Actions check workflow to upload Linux UI screenshot candidates on visual test failures for easier review of regression diffs.

Documentation:

  • Revise English and Chinese getting-started guides to describe the new home launch readiness workflow, download card, and log inspection flow.
  • Document launch readiness and recovery APIs, events, and troubleshooting guidance in both English and Chinese, including the new get_launch_readiness command and related runtime events.

Tests:

  • Expand Playwright UI regression suite to cover all launcher home states, keyboard operability of the primary flow, download and failure diagnostics behavior, and Chinese plus reduced-motion rendering.
  • Update home-related accessibility and visual-baseline tests to assert new headings, state copy, and deterministic screenshots for both supported window sizes.

@sourcery-ai

sourcery-ai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Redesigns the launcher home into a state-driven launch-readiness command center backed by a new backend readiness probe, download and game lifecycle stores, and updated fixtures, tests, docs, and CI to cover all launch and recovery states.

Sequence diagram for home launch-readiness primary action

sequenceDiagram
  actor User
  participant HomePage
  participant GameStore
  participant DownloadStore
  participant Client as TauriClient
  participant Backend

  User->>HomePage: click handlePrimaryAction()
  HomePage->>HomePage: resolveHomeLaunchState(...)

  alt homeState == "files-missing"
    HomePage->>Client: installVersion(activeInstance.id, activeInstance.versionId)
    Client->>Backend: get_launch_readiness(instance_id, version_id)
    Backend-->>Client: LaunchReadiness
    Client-->>HomePage: LaunchReadiness
    HomePage->>HomePage: setProbeRevision(+1)
  else homeState == "ready" or homeState == "stopped"
    HomePage->>GameStore: startGame(activeInstance.id, activeInstance.versionId)
    GameStore->>Backend: start_game(instance_id, version_id)
    Backend-->>GameStore: message
    GameStore-->>HomePage: message
  else homeState == "running"
    HomePage->>GameStore: stopGame(runningInstanceId)
    GameStore->>Backend: stop_game()
    Backend-->>GameStore: message
    GameStore-->>HomePage: message
  else homeState == "downloading" or homeState == "failed"
    HomePage->>HomePage: focusActivity()
  else homeState == "java-missing" or "memory-invalid"
    HomePage->>HomePage: navigate("/settings")
  else homeState == "no-instance" or "version-missing"
    HomePage->>HomePage: navigate("/instances")
  else homeState == "no-account"
    HomePage->>HomePage: setShowLoginModal(true)
  else homeState == "data-error"
    HomePage->>HomePage: refreshReadiness()
    HomePage->>AuthStore: refreshAuth()
    HomePage->>SettingsStore: refreshSettings()
    HomePage->>InstanceStore: refreshInstances()
  end
Loading

File-Level Changes

Change Details Files
Home page redesigned as a launch-readiness command center driven by real account/instance/settings/readiness/game/download state.
  • Replaced decorative home layout with a structured command-center UI that renders state-specific copy and a single primary action.
  • Integrated auth, instance, settings, download, and game stores plus a backend launch-readiness probe to compute a unified HomeLaunchState.
  • Implemented readiness checklist items for account, instance, version, loader, Java, memory, and files, with tone-based styling and icons.
  • Added contextual primary actions for sign-in, instance management, settings repair, downloads, launch/stop, and data refresh, including keyboard focus management.
  • Surfaced recent game/launcher logs and failure details with buttons to open the instance log folder and focus diagnostics.
  • Added locale, reduced-motion, and viewport-aware styling to keep the workflow usable at 1024x768 and 905x575 in dark/light and English/Chinese.
packages/ui/src/pages/home.tsx
packages/ui/src/lib/launch-readiness.ts
packages/ui/src/components/download-monitor.tsx
packages/ui/src/models/auth.ts
packages/ui/src/models/settings.ts
packages/ui/src/models/instance.ts
packages/ui/src/models/downloads.ts
packages/ui/src/models/game.ts
packages/ui/src/i18n.ts
packages/ui/src/lib/launcher-runtime.ts
packages/ui/src/pages/index.tsx
packages/ui/src/locales/en.json
packages/ui/src/locales/zh-CN.json
Backend exposes a typed launch-readiness API that reuses real Java resolution and version metadata checks, with TS bindings wired through the runtime adapter.
  • Added LaunchReadiness struct carrying version installation status, required Java major version, and resolved Java installation.
  • Implemented get_launch_readiness Tauri command that checks version metadata JSON, client JAR presence, and resolves Java using the same rules as start_game.
  • Registered get_launch_readiness in the Tauri command list and exported its TS bindings.
  • Changed generated TS API client to import invoke from the launcher-runtime adapter instead of directly from @tauri-apps/api/core.
src-tauri/src/main.rs
src-tauri/src/utils/api.rs
packages/ui/src/client.ts
packages/ui/src/types/bindings/core.ts
Download and game lifecycle handling moved into dedicated stores that consume runtime events and feed the home UI and fixtures.
  • Introduced a zustand download store that listens to download-start, download-progress, download-complete, and java-download-progress events and maintains aggregate progress stats.
  • Expanded the game store to track lastExit, lastError, lastErrorInstanceId, recentLogs, and to initialize listeners for game-exited, launcher-log, game-stdout, and game-stderr.
  • Refactored startGame/stopGame to use centralized event initialization, log appends, and structured failure handling with messages stored in recentLogs.
  • Updated fixture bootstrap to preseed download and game store state for downloading, launching, running, stopped, and failed scenarios.
packages/ui/src/models/downloads.ts
packages/ui/src/models/game.ts
packages/ui/src/fixtures/bootstrap.ts
packages/ui/src/fixtures/launcher.ts
Fixture system, tests, and docs extended to cover all home launch/recovery states, locales, and accessibility flows.
  • Added new fixture names and helpers for no-account, no-instance, not-ready, ready, downloading, launching, running, stopped, failed, error, and migration, including locale selection.
  • Updated Playwright visual and accessibility tests to assert state-specific headings, download monitor behavior, keyboard operability, recovery actions, Chinese locale, and reduced motion.
  • Documented fixtures, launch-readiness protocol, backend APIs, and troubleshooting flows in English and Chinese manuals and implementation docs.
  • Updated README to describe new fixtures, locale parameter, and expanded regression coverage.
  • Ensured i18n language selection sets document.lang to match the active locale.
packages/ui/src/lib/launcher-runtime.ts
packages/ui/tests/launcher-fixtures.spec.ts
packages/ui/README.md
packages/docs/content/en/manual/getting-started.mdx
packages/docs/content/zh/manual/getting-started.mdx
packages/docs/content/en/development/implementation.mdx
packages/docs/content/zh/development/implementation.mdx
packages/docs/content/en/manual/troubleshooting.mdx
packages/docs/content/zh/manual/troubleshooting.mdx
packages/ui/src/i18n.ts
CI updated to preserve visual diagnostics for Linux UI regression failures.
  • Extended GitHub Actions UI job to upload actual PNG screenshots from Playwright when the UI fixture regression suite fails.
  • Configured artifact name, path, compression, and retention to keep Linux screenshot candidates lightweight and short-lived.
.github/workflows/check.yml

Assessment against linked issues

Issue Objective Addressed Explanation
#174 Redesign the launcher home screen into a state-driven launch command center that surfaces instance/account/version/loader/Java/memory/file readiness, drives a single primary action per state (sign in, create/import, repair, download, launch, stop, inspect failure), mounts the download monitor, and provides access to launch logs/diagnostics while preserving background usability.
#174 Implement and validate fixtures, UI behavior, and tests for all launch lifecycle states (no-account, no-instance, not-ready, ready, downloading, launching, running, stopped, failed, error) with coverage of required window sizes, keyboard focus, contrast, reduced motion, and English/Chinese copies.
#174 Update English and Chinese documentation to describe the new launch-readiness home workflow, including readiness checks, recovery actions, download monitoring, and diagnostics/log access.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Workspace change through: 9abcc9c

0 changesets found

Planned changes to release
Package Bump Level Current Version Next Version

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="packages/ui/tests/launcher-fixtures.spec.ts" line_range="225-234" />
<code_context>
+test("download and failure states expose actionable diagnostics", async ({
</code_context>
<issue_to_address>
**suggestion (testing):** Download diagnostics test does not cover Java runtime downloads or bytes/progress details from the new monitor/store

Given the new DownloadMonitor and useDownloadStore behavior, please add a fixture-based test that drives a `java-download-progress` sequence and verifies:

- The monitor uses a "Java runtime" descriptor for kind === "java".
- The percentage and `aria-valuenow` match the boundedPercentage logic.
- Byte units (MB/GB) are formatted and rendered correctly.

This will ensure the new store wiring and Java download path remain observable and accessible, in addition to the existing game download coverage.

Suggested implementation:

```typescript
test("download and failure states expose actionable diagnostics", async ({
  page,
}) => {
  await page.goto("/?fixture=downloading&theme=dark#/");
  await expect(
    page.getByRole("progressbar", { name: "Download progress" }),
  ).toHaveAttribute("aria-valuenow", "65");
  await page.getByRole("button", { name: "Track download" }).click();
  await expect(page.locator("#download-monitor")).toBeFocused();

  await page.goto("/?fixture=failed&theme=dark#/");
});

test("java runtime download progress diagnostics remain observable and accessible", async ({
  page,
}) => {
  // Drive the java-download-progress fixture to exercise DownloadMonitor + useDownloadStore
  await page.goto("/?fixture=java-download-progress&theme=dark#/");

  // The monitor should expose a "Java runtime" descriptor when kind === "java"
  const javaRuntimeDescriptor = page.getByText(/Java runtime/i);
  await expect(javaRuntimeDescriptor).toBeVisible();

  // Progress bar should reflect boundedPercentage in both visual percentage and aria-valuenow
  const progressBar = page.getByRole("progressbar", { name: "Download progress" });
  const ariaValueNow = Number(await progressBar.getAttribute("aria-valuenow"));
  expect(ariaValueNow).toBeGreaterThanOrEqual(0);
  expect(ariaValueNow).toBeLessThanOrEqual(100);

  // If a percentage label is rendered, ensure it matches the bounded percentage
  const percentageLabel = page.getByText(/%$/);
  const percentageText = await percentageLabel.textContent();
  if (percentageText) {
    const numericPercentage = Number(percentageText.replace("%", "").trim());
    expect(numericPercentage).toBe(ariaValueNow);
  }

  // Open the download monitor and verify byte units are formatted/rendered correctly
  await page.getByRole("button", { name: "Track download" }).click();
  const monitor = page.locator("#download-monitor");
  await expect(monitor).toBeFocused();
  await expect(monitor).toContainText(/Java runtime/i);
  await expect(monitor).toContainText(/MB|GB/i);
}

```

- Ensure the `java-download-progress` fixture exists and exercises a `kind: "java"` download path via the new `DownloadMonitor` and `useDownloadStore`.
- If the accessible name for the progress bar or the Java descriptor differs (e.g., "Java runtime download" instead of "Download progress"), adjust the `getByRole` / `getByText` queries accordingly.
- If the percentage label uses a dedicated test id or different text pattern, update the `percentageLabel` locator to match your actual DOM (for example, `page.getByTestId("download-percentage")`).
- Confirm the download monitor container has the `#download-monitor` id; if not, align the selector with the component's implementation.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +225 to +234
test("download and failure states expose actionable diagnostics", async ({
page,
}) => {
await page.goto("/?fixture=downloading&theme=dark#/");
await expect(
page.getByRole("progressbar", { name: "Download progress" }),
).toHaveAttribute("aria-valuenow", "65");
await page.getByRole("button", { name: "Track download" }).click();
await expect(page.locator("#download-monitor")).toBeFocused();

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.

suggestion (testing): Download diagnostics test does not cover Java runtime downloads or bytes/progress details from the new monitor/store

Given the new DownloadMonitor and useDownloadStore behavior, please add a fixture-based test that drives a java-download-progress sequence and verifies:

  • The monitor uses a "Java runtime" descriptor for kind === "java".
  • The percentage and aria-valuenow match the boundedPercentage logic.
  • Byte units (MB/GB) are formatted and rendered correctly.

This will ensure the new store wiring and Java download path remain observable and accessible, in addition to the existing game download coverage.

Suggested implementation:

test("download and failure states expose actionable diagnostics", async ({
  page,
}) => {
  await page.goto("/?fixture=downloading&theme=dark#/");
  await expect(
    page.getByRole("progressbar", { name: "Download progress" }),
  ).toHaveAttribute("aria-valuenow", "65");
  await page.getByRole("button", { name: "Track download" }).click();
  await expect(page.locator("#download-monitor")).toBeFocused();

  await page.goto("/?fixture=failed&theme=dark#/");
});

test("java runtime download progress diagnostics remain observable and accessible", async ({
  page,
}) => {
  // Drive the java-download-progress fixture to exercise DownloadMonitor + useDownloadStore
  await page.goto("/?fixture=java-download-progress&theme=dark#/");

  // The monitor should expose a "Java runtime" descriptor when kind === "java"
  const javaRuntimeDescriptor = page.getByText(/Java runtime/i);
  await expect(javaRuntimeDescriptor).toBeVisible();

  // Progress bar should reflect boundedPercentage in both visual percentage and aria-valuenow
  const progressBar = page.getByRole("progressbar", { name: "Download progress" });
  const ariaValueNow = Number(await progressBar.getAttribute("aria-valuenow"));
  expect(ariaValueNow).toBeGreaterThanOrEqual(0);
  expect(ariaValueNow).toBeLessThanOrEqual(100);

  // If a percentage label is rendered, ensure it matches the bounded percentage
  const percentageLabel = page.getByText(/%$/);
  const percentageText = await percentageLabel.textContent();
  if (percentageText) {
    const numericPercentage = Number(percentageText.replace("%", "").trim());
    expect(numericPercentage).toBe(ariaValueNow);
  }

  // Open the download monitor and verify byte units are formatted/rendered correctly
  await page.getByRole("button", { name: "Track download" }).click();
  const monitor = page.locator("#download-monitor");
  await expect(monitor).toBeFocused();
  await expect(monitor).toContainText(/Java runtime/i);
  await expect(monitor).toContainText(/MB|GB/i);
}
  • Ensure the java-download-progress fixture exists and exercises a kind: "java" download path via the new DownloadMonitor and useDownloadStore.
  • If the accessible name for the progress bar or the Java descriptor differs (e.g., "Java runtime download" instead of "Download progress"), adjust the getByRole / getByText queries accordingly.
  • If the percentage label uses a dedicated test id or different text pattern, update the percentageLabel locator to match your actual DOM (for example, page.getByTestId("download-percentage")).
  • Confirm the download monitor container has the #download-monitor id; if not, align the selector with the component's implementation.

@HsiangNianian
HsiangNianian merged commit 4d02b3b into main Jul 29, 2026
24 of 25 checks passed
@HsiangNianian
HsiangNianian deleted the feat/alpha7-home-readiness branch July 29, 2026 14:49
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.

Redesign launcher home around launch readiness and recovery

1 participant