Skip to content

feat(ui): add scalable instance library workspace - #181

Merged
HsiangNianian merged 3 commits into
mainfrom
feat/alpha7-instance-library
Jul 30, 2026
Merged

HsiangNianian merged 3 commits into
mainfrom
feat/alpha7-instance-library

Conversation

@HsiangNianian

@HsiangNianian HsiangNianian commented Jul 29, 2026 •

Copy link
Copy Markdown
Member

Summary

  • redesign the instance library around search, sorting, persistent list/grid views, readiness, and an active detail workspace
  • keep launch selection synchronized with Home and add Java override, migration, export, duplication, and deletion-recovery affordances
  • cover 0, 1, 20, and 100-instance states with English/Chinese docs, accessibility checks, and Darwin/Linux visual baselines

Impact

Large instance collections remain scannable at the default and minimum launcher sizes, while launch remains the prominent action and destructive actions retain explicit recovery guidance.

Validation

Disclosure

This PR contains LLM-assisted code with author-provided quality assurance.

Closes #175

Summary by Sourcery

Redesign the instances page into a searchable, sortable instance library with a persistent list/grid view and an active detail workspace that stays synchronized with the launcher.

New Features:

  • Add instance search, sorting, and list/grid view modes with per-device persistence for large libraries.
  • Introduce an active instance detail workspace showing launch readiness, Java and memory overrides, mod counts, and metadata, with a dedicated launch button.
  • Provide per-instance actions for opening folders, editing, duplicating, managing mods, browsing content, exporting archives, and guided deletion with recovery hints.
  • Support Java path overrides in the instance editor to control the runtime used for a given environment.

Enhancements:

  • Improve instance store refresh behavior with deduplicated concurrent loads and explicit loading/error states surfaced in the UI.
  • Add progressive launch readiness checks per instance so large libraries remain responsive while surfacing attention/ready/error markers.
  • Refine keyboard and accessibility flows for the launcher and instance library, including active selection and view toggles.
  • Extend launcher fixtures to cover empty, single, medium, large, loading, error, and preselected-grid instance library scenarios for visual and behavioral validation.

Documentation:

  • Add English and Chinese manual pages documenting the instance library, its search/sort/view behaviors, active workspace, import/migration, and safe deletion guidance.
  • Update fixture documentation to describe new instance library-related fixture states for developers and testers.

Tests:

  • Expand UI tests to cover scalable instance library behavior, including search, sorting, view switching, active workspace, loading/error states, and Chinese locale accessibility.
  • Update existing launcher accessibility and keyboard navigation tests to align with the new instance library heading and flows.

@github-actions

Copy link
Copy Markdown
Contributor

Workspace change through: 741e470

0 changesets found

Planned changes to release
Package Bump Level Current Version Next Version

@sourcery-ai

sourcery-ai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Redesigns the Instances page into a scalable "instance library" with search, sorting, list/grid layouts, readiness probes, and an active detail workspace, while wiring it into refreshed instance-store behavior, fixtures, tests, and documentation for both English and Chinese.

Sequence diagram for the new instance library launch flow

sequenceDiagram
    actor User
    participant InstancesPage
    participant InstanceStore as useInstanceStore
    participant GameStore as useGameStore
    participant Client as ClientAPI

    User->>InstancesPage: click LibraryItem.onLaunch(instance)
    activate InstancesPage
    InstancesPage->>InstancesPage: launchInstance(instance)

    note over InstancesPage: Ensure instance is active
    InstancesPage->>InstanceStore: setActiveInstance(instance)
    activate InstanceStore
    InstanceStore-->>InstancesPage: Promise resolved
    deactivate InstanceStore

    InstancesPage->>InstancesPage: checkReadiness(instance)
    InstancesPage->>Client: getLaunchReadiness(instance.id, instance.versionId)
    activate Client
    Client-->>InstancesPage: LaunchReadiness
    deactivate Client
    InstancesPage->>InstancesPage: setReadinessById({ status: ready/attention })

    alt runningInstanceId === instance.id
        InstancesPage->>GameStore: stopGame(instance.id)
        activate GameStore
        GameStore-->>InstancesPage: stop complete
        deactivate GameStore
    else not running
        alt !instance.versionId
            InstancesPage->>InstancesPage: toast.error("instances.noVersionError")
        else !account
            InstancesPage->>InstancesPage: toast.info("instances.loginFirst")
        else ready to launch
            InstancesPage->>GameStore: startGame(instance.id, instance.versionId)
            activate GameStore
            GameStore-->>InstancesPage: launch started
            deactivate GameStore
        end
    end

    deactivate InstancesPage
Loading

File-Level Changes

Change Details Files
Rebuilt Instances page as a searchable, sortable library with list/grid views and an active detail workspace synchronized with Home.
  • Replaced the simple list of instances with a LibraryItem component that supports per-instance launch, secondary actions, readiness display, and list/grid layouts.
  • Introduced a DetailWorkspace sidebar that shows readiness details, Java/memory overrides, mod counts, notes, environment metadata, and shortcut actions for the active instance.
  • Added search, sort, and view mode controls (with localStorage persistence) and wired them to filtered/sorted instance collections, including empty, loading, error, and no-result states.
  • Connected launch, selection, navigation, and file operations to useInstanceStore/useGameStore, ensuring active instance sync and guarded launch flows with toasts.
packages/ui/src/pages/instances/index.tsx
Extended instance store behavior and fixtures to support deterministic loading/error states, mod scanning, and large-library scenarios.
  • Added a shared pendingRefresh promise in the instance store to dedupe concurrent refresh calls and set explicit loading/error state with user feedback.
  • Changed delete() to return a boolean indicating success so callers can keep dialogs open if deletion fails.
  • Extended launcher fixtures to generate many synthetic instances, simulate instance-index errors/loading, and return per-instance launch readiness and mod lists.
  • Updated launcher runtime bootstrap to honor a grid-view fixture via localStorage.
  • Documented new fixture names and behavior in the UI README and launcher runtime metadata.
packages/ui/src/models/instance.ts
packages/ui/src/fixtures/launcher.ts
packages/ui/src/lib/launcher-runtime.ts
packages/ui/src/fixtures/bootstrap.ts
packages/ui/README.md
Enhanced the instance editor to support Java path overrides via manual entry or file chooser.
  • Added local state for an editable Java path field initialized from the current instance.
  • Persisted javaPathOverride back to the instance when saving, normalizing empty strings to null.
  • Introduced a Java path input with an attached folder picker button that calls openFileDialog and updates the field, plus helper hint text.
packages/ui/src/components/instance-editor-modal.tsx
Expanded UI tests to cover the new instance library flows, accessibility, and fixtures.
  • Added visual scenarios for various instance library fixture states (20/grid/empty/error) to the launcher-fixtures visual suite.
  • Adjusted accessibility routes and headings to use the new "Instance library" title instead of the old "Instances" heading.
  • Introduced end-to-end tests covering scaling from empty to single-instance workspace, large-library search/sort/view toggles, active selection, and deterministic loading/error/repair states.
  • Extended the i18n test to assert Chinese translations and accessibility of the instance library, including the active workspace label.
packages/ui/tests/launcher-fixtures.spec.ts
Documented the instance library feature and localized strings for both English and Chinese manuals.
  • Added English and Chinese MDX manual pages describing instance library search, readiness, workspace details, management actions, migration, and deletion guidance.
  • Linked the new feature docs in the manual features metadata for both locales.
  • Extended en and zh-CN locale JSONs with keys used by the new instance library UI and editor Java-path controls.
packages/docs/content/en/manual/features/instances.mdx
packages/docs/content/zh/manual/features/instances.mdx
packages/docs/content/en/manual/features/meta.json
packages/docs/content/zh/manual/features/meta.json
packages/ui/src/locales/en.json
packages/ui/src/locales/zh-CN.json

Assessment against linked issues

Issue Objective Addressed Explanation
#175 Redesign the instance library UI to support name search, sorting (by name, last played, creation time), deliberate grid and list presentations, display of key metadata (icon, version, loader, last played, readiness, active state), centralized secondary actions via a menu, and robust empty/loading/error/repair states while keeping launch as the primary, consistent action. ✅
#175 Add an instance detail workspace that tracks the active instance and shows notes, paths, Java and memory overrides, launch readiness details, mod counts, and quick shortcuts for open/edit/copy/export and related actions. ✅
#175 Ensure scalability and UX polish via fixtures, tests, and documentation: support libraries with 0, 1, 20, and 100 instances; keep selection and active instance state consistent with the home launch surface; preserve destructive-action confirmation and recovery guidance; and update English and Chinese instance-management documentation and i18n strings. ✅

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

@HsiangNianian
HsiangNianian marked this pull request as ready for review July 30, 2026 01:37
@HsiangNianian
HsiangNianian merged commit fa2ec28 into main Jul 30, 2026
25 of 26 checks passed
@HsiangNianian
HsiangNianian deleted the feat/alpha7-instance-library branch July 30, 2026 01:37

@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 2 issues

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

## Individual Comments

### Comment 1
<location path="packages/ui/src/pages/instances/index.tsx" line_range="702-710" />
<code_context>
+  const activeInstance = useInstanceStore((state) => state.activeInstance);
+  const instanceStatus = useInstanceStore((state) => state.status);
+  const instanceError = useInstanceStore((state) => state.error);
+  const refreshInstances = useInstanceStore((state) => state.refresh);
+  const setActiveInstance = useInstanceStore(
+    (state) => state.setActiveInstance,
+  );
+  const deleteInstance = useInstanceStore((state) => state.delete);
+  const duplicateInstance = useInstanceStore((state) => state.duplicate);
+  const exportArchive = useInstanceStore((state) => state.exportArchive);
+  const importArchive = useInstanceStore((state) => state.importArchive);
+  const repairInstances = useInstanceStore((state) => state.repair);
   const account = useAuthStore((state) => state.account);
+  const accountIdentity = account
</code_context>
<issue_to_address>
**issue (bug_risk):** Instances are never initially refreshed, so the library can stay empty with status "idle"

In the previous version, `instancesStore.refresh()` was called on mount via `useEffect`; the new code never triggers `refreshInstances()`. This means `instances` and `instanceStatus` stay at their initial values, so the library can appear empty with status `idle` even when instances exist. Please add an initial `useEffect` (and, if needed, react to fixture changes) to call `refreshInstances()` to preserve the prior behavior.
</issue_to_address>

### Comment 2
<location path="packages/ui/src/pages/instances/index.tsx" line_range="315-324" />
<code_context>
+                    disabled={saving}
+                    className="flex-1"
+                  />
+                  <Button
+                    type="button"
+                    variant="outline"
</code_context>
<issue_to_address>
**nitpick:** Export buttons don’t reflect the exporting state consistently in the detail workspace

In the library item dropdown the export button reflects `exporting` via its label, but in `DetailWorkspace` the shortcut button only disables without changing the label. For long-running exports this leads to inconsistent feedback depending on where the user triggers export. Please reuse the same label logic (or add a spinner) in `DetailWorkspace` to make the export state clear and consistent.
</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 +702 to +710
const refreshInstances = useInstanceStore((state) => state.refresh);
const setActiveInstance = useInstanceStore(
(state) => state.setActiveInstance,
);
const deleteInstance = useInstanceStore((state) => state.delete);
const duplicateInstance = useInstanceStore((state) => state.duplicate);
const exportArchive = useInstanceStore((state) => state.exportArchive);
const importArchive = useInstanceStore((state) => state.importArchive);
const repairInstances = useInstanceStore((state) => state.repair);

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.

issue (bug_risk): Instances are never initially refreshed, so the library can stay empty with status "idle"

In the previous version, instancesStore.refresh() was called on mount via useEffect; the new code never triggers refreshInstances(). This means instances and instanceStatus stay at their initial values, so the library can appear empty with status idle even when instances exist. Please add an initial useEffect (and, if needed, react to fixture changes) to call refreshInstances() to preserve the prior behavior.

Comment on lines +315 to +324
<Button
type="button"
size={view === "grid" ? "sm" : "icon"}
variant={running ? "destructive" : active ? "default" : "outline"}
aria-label={launchLabel}
title={launchLabel}
className={cn(view === "grid" && "flex-1")}
onClick={() => onLaunch(instance)}
disabled={anotherRunning || launching || stopping}
>

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.

nitpick: Export buttons don’t reflect the exporting state consistently in the detail workspace

In the library item dropdown the export button reflects exporting via its label, but in DetailWorkspace the shortcut button only disables without changing the label. For long-running exports this leads to inconsistent feedback depending on where the user triggers export. Please reuse the same label logic (or add a spinner) in DetailWorkspace to make the export state clear and consistent.

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.

Polish the instance library with search, sorting, and a detail workspace

1 participant