diff --git a/docs/plans/2026-06-26-import-offer.md b/docs/plans/2026-06-26-import-offer.md index 078e0fa59..dcb29a757 100644 --- a/docs/plans/2026-06-26-import-offer.md +++ b/docs/plans/2026-06-26-import-offer.md @@ -20,30 +20,30 @@ ## File Structure -| File | Status | Responsibility | -| --- | --- | --- | -| `backend/internal/legacyimport/orchestrator.go` (+`_test.go`) | delete | orchestrator-session mapping/import (out of scope) | -| `backend/internal/legacyimport/claude.go` (+`_test.go`) | delete | Claude transcript relocation (out of scope) | -| `backend/internal/storage/sqlite/store/session_import_store.go` (+`_test.go`) | delete | `ImportSession` (only used by orchestrator import) | -| `backend/internal/legacyimport/importer.go` | modify | trim `Store`/`Options`/`Report`; drop orchestrator loop; add `quote()` | -| `backend/internal/legacyimport/config.go` | modify | `Repo` -> `*yaml.Node`; tolerate `*yaml.TypeError` | -| `backend/internal/legacyimport/paths.go` | modify | drop `defaultClaudeProjectsDir` + `projectSessionsDir` | -| `backend/internal/legacyimport/{importer,config,project}_test.go` | modify | projects-only assertions | -| `backend/internal/cli/import.go` | modify | drop orchestrator/transcript copy + summary lines | -| `backend/internal/service/importer/importer.go` (+`_test.go`) | create | `Status`/`Run` over the daemon store | -| `backend/internal/httpd/controllers/imports.go` (+`_test.go`) | create | `GET`/`POST /import`, 501 on nil svc | -| `backend/internal/httpd/controllers/dto.go` | modify | `ImportStatusResponse`, `ImportRunResponse` | -| `backend/internal/httpd/apispec/specgen/build.go` | modify | `import` tag + ops + schemaNames | -| `backend/internal/httpd/api.go` | modify | `APIDeps.Import`, `API.imports`, wire + Register | -| `backend/internal/daemon/daemon.go` | modify | `Import: importsvc.New(...)` | -| `backend/internal/cli/start.go` | modify | delete `maybeFirstBootImport` + 2 imports | -| `backend/internal/httpd/apispec/openapi.yaml` | regenerate | — | -| `frontend/src/api/schema.ts` | regenerate | — | -| `frontend/src/shared/daemon-discovery.ts` (+`.test.ts`) | modify | `shouldAdoptDiscoveredPort` | -| `frontend/src/main.ts` | modify | external daemon discovery loop | -| `frontend/src/renderer/hooks/useImportStatus.ts` | create | `useImportStatus`/`useRunImport` | -| `frontend/src/renderer/components/ImportOffer.tsx` (+`.test.tsx`) | create | the banner | -| `frontend/src/renderer/components/SessionsBoard.tsx` | modify | render `` | +| File | Status | Responsibility | +| ----------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------- | +| `backend/internal/legacyimport/orchestrator.go` (+`_test.go`) | delete | orchestrator-session mapping/import (out of scope) | +| `backend/internal/legacyimport/claude.go` (+`_test.go`) | delete | Claude transcript relocation (out of scope) | +| `backend/internal/storage/sqlite/store/session_import_store.go` (+`_test.go`) | delete | `ImportSession` (only used by orchestrator import) | +| `backend/internal/legacyimport/importer.go` | modify | trim `Store`/`Options`/`Report`; drop orchestrator loop; add `quote()` | +| `backend/internal/legacyimport/config.go` | modify | `Repo` -> `*yaml.Node`; tolerate `*yaml.TypeError` | +| `backend/internal/legacyimport/paths.go` | modify | drop `defaultClaudeProjectsDir` + `projectSessionsDir` | +| `backend/internal/legacyimport/{importer,config,project}_test.go` | modify | projects-only assertions | +| `backend/internal/cli/import.go` | modify | drop orchestrator/transcript copy + summary lines | +| `backend/internal/service/importer/importer.go` (+`_test.go`) | create | `Status`/`Run` over the daemon store | +| `backend/internal/httpd/controllers/imports.go` (+`_test.go`) | create | `GET`/`POST /import`, 501 on nil svc | +| `backend/internal/httpd/controllers/dto.go` | modify | `ImportStatusResponse`, `ImportRunResponse` | +| `backend/internal/httpd/apispec/specgen/build.go` | modify | `import` tag + ops + schemaNames | +| `backend/internal/httpd/api.go` | modify | `APIDeps.Import`, `API.imports`, wire + Register | +| `backend/internal/daemon/daemon.go` | modify | `Import: importsvc.New(...)` | +| `backend/internal/cli/start.go` | modify | delete `maybeFirstBootImport` + 2 imports | +| `backend/internal/httpd/apispec/openapi.yaml` | regenerate | — | +| `frontend/src/api/schema.ts` | regenerate | — | +| `frontend/src/shared/daemon-discovery.ts` (+`.test.ts`) | modify | `shouldAdoptDiscoveredPort` | +| `frontend/src/main.ts` | modify | external daemon discovery loop | +| `frontend/src/renderer/hooks/useImportStatus.ts` | create | `useImportStatus`/`useRunImport` | +| `frontend/src/renderer/components/ImportOffer.tsx` (+`.test.tsx`) | create | the banner | +| `frontend/src/renderer/components/SessionsBoard.tsx` | modify | render `` | --- @@ -52,6 +52,7 @@ **Files:** Delete `legacyimport/orchestrator.go`, `orchestrator_test.go`, `claude.go`, `claude_test.go`, `storage/sqlite/store/session_import_store.go`, `session_import_store_test.go`. Modify `legacyimport/importer.go`, `config.go`, `paths.go`, and their tests; `cli/import.go`. **Interfaces:** + - Produces: trimmed `legacyimport.Store` (`GetProject`, `UpsertProject` only), `legacyimport.Options{Root, DryRun, Now, RepoOriginURL}`, `legacyimport.Report{DryRun, ProjectsImported, ProjectsSkipped, Notes}`. **Tasks 2 and 3 depend on this `Report` shape.** - [ ] **Step 1: Delete the orchestrator/transcript files.** @@ -84,7 +85,7 @@ func quote(s string) string { - [ ] **Step 3: `legacyimport/config.go` parsing-robustness fix (PR-bundled, improves project import).** - Add `"errors"` import. - - Change the project config's `Repo string \`yaml:"repo"\`` field to `Repo *yaml.Node \`yaml:"repo"\`` with a comment that it is captured but never consumed (origin is re-resolved from the repo path). + - Change the project config's `Repo string \`yaml:"repo"\``field to`Repo \*yaml.Node \`yaml:"repo"\`` with a comment that it is captured but never consumed (origin is re-resolved from the repo path). - In `loadLegacyConfig`, when `yaml.Unmarshal` errors, keep the partial decode on a `*yaml.TypeError` instead of failing the whole registry: ```go @@ -107,6 +108,7 @@ func quote(s string) string { **Files:** Create `backend/internal/service/importer/importer.go`, `…/importer_test.go` **Interfaces:** + - Consumes: trimmed `legacyimport.Store`, `legacyimport.Run`, `legacyimport.HasLegacyData`, `legacyimport.DefaultLegacyRootDir`, `legacyimport.Report`, `legacyimport.Options{Root}`; `domain.ProjectRecord`. - Produces: `importer.Status{Available bool; LegacyRoot string}`, `importer.Service` (`Status(ctx)`, `Run(ctx)`), `importer.Deps{Store, Root}`, `importer.New(Deps) *Manager`. **Controller (Task 3) and daemon (Task 5) depend on these exact names.** @@ -214,6 +216,7 @@ func (m *Manager) Run(ctx context.Context) (legacyimport.Report, error) { **Files:** Create `backend/internal/httpd/controllers/imports.go`, `…/imports_test.go`; Modify `…/controllers/dto.go` **Interfaces:** + - Consumes: `importsvc.Status`, `legacyimport.Report`, `apispec.NotImplemented`, `envelope.WriteJSON`/`WriteError`, `httpd.NewRouterWithControl(cfg, log, termMgr, APIDeps, ControlDeps)`. - Produces: `controllers.ImportService`, `controllers.ImportController{Svc}`, `controllers.ImportStatusResponse`, `controllers.ImportRunResponse`. **api.go (Task 5) and specgen (Task 4) depend on these.** @@ -332,9 +335,13 @@ type ImportRunResponse struct { - [ ] **Step 1:** Add `import { ImportOffer } from "./ImportOffer";` and, directly under the `` line, insert: ```tsx - {/* First-run legacy-AO import opt-in. Renders only when the daemon - reports an importable install, and only on the top-level board. */} - {!projectId && } +{ + /* First-run legacy-AO import opt-in. Renders only when the daemon + reports an importable install, and only on the top-level board. */ +} +{ + !projectId && ; +} ``` - [ ] **Step 2:** `npm --prefix frontend run typecheck && npm --prefix frontend run test` → expect PASS. diff --git a/docs/superpowers/plans/2026-06-26-legacy-migration-popup.md b/docs/superpowers/plans/2026-06-26-legacy-migration-popup.md index 0f665abc7..d2ff54662 100644 --- a/docs/superpowers/plans/2026-06-26-legacy-migration-popup.md +++ b/docs/superpowers/plans/2026-06-26-legacy-migration-popup.md @@ -22,20 +22,20 @@ ## File Structure -| File | Status | Responsibility | -| --- | --- | --- | -| (backend import API) | per import-offer plan | projects-only `legacyimport`, `service/importer`, controller, DTOs, OpenAPI, wiring | -| `backend/internal/service/importer/importer.go` | create (modified Status) | `Status` = `HasLegacyData` only; `Run` | -| `backend/internal/cli/start.go` | modify | drop the `§6.4` TODO comment (`:78`) | -| `frontend/src/main/app-state.ts` | modify | `MigrationState`, schema v2, preserve, `updateMigration`, `readMigrationState` | -| `frontend/src/main/app-state.test.ts` | modify | marker v2 + migration tests | -| `frontend/src/main.ts` | modify | `appState:getMigration` / `appState:setMigration` IPC handlers | -| `frontend/src/preload.ts` | modify | `ao.appState` bridge methods | -| `frontend/src/renderer/lib/bridge.ts` | modify | `appState` preview fallback | -| `frontend/src/renderer/hooks/useMigrationOffer.ts` | create | gate: IPC marker + daemon GET | -| `frontend/src/renderer/components/MigrationPopup.tsx` | create | the three-action dialog | -| `frontend/src/renderer/components/MigrationPopup.test.tsx` | create | popup tests | -| `frontend/src/renderer/routes/_shell.index.tsx` | modify | mount `` on the board | +| File | Status | Responsibility | +| ---------------------------------------------------------- | ------------------------ | ----------------------------------------------------------------------------------- | +| (backend import API) | per import-offer plan | projects-only `legacyimport`, `service/importer`, controller, DTOs, OpenAPI, wiring | +| `backend/internal/service/importer/importer.go` | create (modified Status) | `Status` = `HasLegacyData` only; `Run` | +| `backend/internal/cli/start.go` | modify | drop the `§6.4` TODO comment (`:78`) | +| `frontend/src/main/app-state.ts` | modify | `MigrationState`, schema v2, preserve, `updateMigration`, `readMigrationState` | +| `frontend/src/main/app-state.test.ts` | modify | marker v2 + migration tests | +| `frontend/src/main.ts` | modify | `appState:getMigration` / `appState:setMigration` IPC handlers | +| `frontend/src/preload.ts` | modify | `ao.appState` bridge methods | +| `frontend/src/renderer/lib/bridge.ts` | modify | `appState` preview fallback | +| `frontend/src/renderer/hooks/useMigrationOffer.ts` | create | gate: IPC marker + daemon GET | +| `frontend/src/renderer/components/MigrationPopup.tsx` | create | the three-action dialog | +| `frontend/src/renderer/components/MigrationPopup.test.tsx` | create | popup tests | +| `frontend/src/renderer/routes/_shell.index.tsx` | modify | mount `` on the board | --- @@ -54,6 +54,7 @@ Execute the committed plan `docs/plans/2026-06-26-import-offer.md`, **Tasks 1, 3 **Files:** Create `backend/internal/service/importer/importer.go`, `…/importer_test.go` **Interfaces:** + - Produces: `importer.Status{Available bool; LegacyRoot string}`, `importer.Service` (`Status(ctx)`, `Run(ctx)`), `importer.Deps{Store, Root}`, `importer.New(Deps) *Manager`. `Store` is exactly `legacyimport.Store` (the projects-only `GetProject`/`UpsertProject`) — **no `ListProjects`** (the design drops the empty-DB heuristic; the app marker governs prompting). - [ ] **Step 1: Write `importer.go`:** @@ -229,6 +230,7 @@ func TestNew_DefaultsRoot(t *testing.T) { **Files:** Modify `frontend/src/main/app-state.ts`, `frontend/src/main/app-state.test.ts` **Interfaces:** + - Produces: `MigrationStatus`, `MigrationState`, `updateMigration({stateDir, migration, now})`, `readMigrationState(stateDir) => Promise`. `AppStateMarker` gains `migration?: MigrationState`. `SCHEMA_VERSION === 2`. **B2/B3/B4 depend on these names.** - [ ] **Step 1 (TDD): add tests to `app-state.test.ts`** (follows the file's existing temp-dir style): @@ -239,12 +241,7 @@ import os from "node:os"; import path from "node:path"; import { mkdtemp } from "node:fs/promises"; import { describe, expect, it } from "vitest"; -import { - APP_STATE_FILE_NAME, - readMigrationState, - updateMigration, - writeAppStateMarker, -} from "./app-state"; +import { APP_STATE_FILE_NAME, readMigrationState, updateMigration, writeAppStateMarker } from "./app-state"; const fixedNow = () => new Date("2026-06-26T10:00:00.000Z"); async function tmp() { @@ -478,7 +475,8 @@ const { getMock, postMock, getMigration, setMigration } = vi.hoisted(() => ({ vi.mock("../lib/api-client", () => ({ apiClient: { GET: getMock, POST: postMock }, - apiErrorMessage: (e: unknown, fb = "Request failed") => (e instanceof Error ? e.message : (e as { message?: string })?.message ?? fb), + apiErrorMessage: (e: unknown, fb = "Request failed") => + e instanceof Error ? e.message : ((e as { message?: string })?.message ?? fb), })); vi.mock("../lib/bridge", () => ({ aoBridge: { appState: { getMigration, setMigration } } })); @@ -629,14 +627,12 @@ export function MigrationPopup() { Import projects from your earlier AO? - We found an existing install at{" "} - {legacyRoot}. Importing brings in - your projects. Your old files are never modified, and you can do this later. + We found an existing install at {legacyRoot}. + Importing brings in your projects. Your old files are never modified, and you can do this later. {error && (
- Migration failed: {error}. Your legacy projects are untouched (nothing is ever deleted). You - can retry. + Migration failed: {error}. Your legacy projects are untouched (nothing is ever deleted). You can retry.
)}

You can run this again later.

diff --git a/docs/superpowers/specs/2026-06-26-legacy-migration-popup-design.md b/docs/superpowers/specs/2026-06-26-legacy-migration-popup-design.md index bff6c8ba8..2579d5177 100644 --- a/docs/superpowers/specs/2026-06-26-legacy-migration-popup-design.md +++ b/docs/superpowers/specs/2026-06-26-legacy-migration-popup-design.md @@ -25,20 +25,20 @@ or declined import loses nothing. ## 1. Ground truth (what the code is today) -| Fact | Value | Source | -| --- | --- | --- | -| Marker file | `~/.ao/app-state.json`, **app is sole writer**, written every launch | `frontend/src/main/app-state.ts`; spec §5, invariant 3 | -| Marker fields today | `schemaVersion, appPath, version, installedAt, lastReconciledAt, installSource` | `frontend/src/main/app-state.ts` `AppStateMarker` | -| Marker write call site | `app.whenReady()` before `createWindow()` | `frontend/src/main.ts:859` (inside `whenReady`, `:868`) | -| Go reader of marker | read-only, ignores unknown JSON fields, does NOT gate on `schemaVersion` | `backend/internal/cli/start.go` `appState` struct (~`:38`) | -| Import engine | `internal/legacyimport` (projects + per-project settings + orchestrator + transcripts) + `ao import` CLI | present from #314 | -| Import **daemon API** | **does not exist on main** (no `service/importer`, no `httpd/controllers/imports.go`) | verified | -| Daemon ownership | the app spawns + owns the daemon | `frontend/src/main.ts` `startDaemon` | -| HTTP client (renderer) | `apiClient` over the daemon loopback API | `frontend/src/renderer/lib/api-client.ts:81` | -| IPC bridge | `contextBridge.exposeInMainWorld("ao", api)`; renderer calls `window.ao..*` | `frontend/src/preload.ts:73` | -| IPC handler pattern | `ipcMain.handle(":", …)` in main | `frontend/src/main.ts:773-796` | -| Dashboard route | `_shell.index.tsx` (the board) | `frontend/src/renderer/routes/` | -| **Global Settings page** | **none** — only per-project `_shell.projects.$projectId_.settings.tsx` | verified | +| Fact | Value | Source | +| ------------------------ | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | +| Marker file | `~/.ao/app-state.json`, **app is sole writer**, written every launch | `frontend/src/main/app-state.ts`; spec §5, invariant 3 | +| Marker fields today | `schemaVersion, appPath, version, installedAt, lastReconciledAt, installSource` | `frontend/src/main/app-state.ts` `AppStateMarker` | +| Marker write call site | `app.whenReady()` before `createWindow()` | `frontend/src/main.ts:859` (inside `whenReady`, `:868`) | +| Go reader of marker | read-only, ignores unknown JSON fields, does NOT gate on `schemaVersion` | `backend/internal/cli/start.go` `appState` struct (~`:38`) | +| Import engine | `internal/legacyimport` (projects + per-project settings + orchestrator + transcripts) + `ao import` CLI | present from #314 | +| Import **daemon API** | **does not exist on main** (no `service/importer`, no `httpd/controllers/imports.go`) | verified | +| Daemon ownership | the app spawns + owns the daemon | `frontend/src/main.ts` `startDaemon` | +| HTTP client (renderer) | `apiClient` over the daemon loopback API | `frontend/src/renderer/lib/api-client.ts:81` | +| IPC bridge | `contextBridge.exposeInMainWorld("ao", api)`; renderer calls `window.ao..*` | `frontend/src/preload.ts:73` | +| IPC handler pattern | `ipcMain.handle(":", …)` in main | `frontend/src/main.ts:773-796` | +| Dashboard route | `_shell.index.tsx` (the board) | `frontend/src/renderer/routes/` | +| **Global Settings page** | **none** — only per-project `_shell.projects.$projectId_.settings.tsx` | verified | --- @@ -65,6 +65,7 @@ or declined import loses nothing. ## 3. Scope **In scope:** + - Backend: the projects-only import daemon API: `GET /api/v1/import` (availability) and `POST /api/v1/import` (run), plus `service/importer`, DTOs, OpenAPI regen. (= the import-offer plan, with the status-semantics tweak in §5.2.) @@ -75,6 +76,7 @@ or declined import loses nothing. reassurance. **Out of scope (deferred / separate):** + - Global Settings page + the Migration section / redo entry point → #2205. - The engine simplification details themselves live in the import-offer plan; this design consumes that API, it does not re-specify the engine internals. @@ -99,6 +101,7 @@ or declined import loses nothing. ## 5. Backend: the import daemon API ### 5.1 Surface (from the import-offer plan) + - `GET /api/v1/import` → `{ available: bool, legacyRoot: string }`. - `POST /api/v1/import` → `{ report: { dryRun, projectsImported, projectsSkipped, notes? } }`. - `internal/service/importer` wraps `legacyimport` (projects-only); wired into @@ -106,6 +109,7 @@ or declined import loses nothing. - A nil service answers OpenAPI-backed `501`. OpenAPI + `schema.ts` regenerated. ### 5.2 Status semantics (the one change vs the import-offer plan) + The import-offer plan computed `available = HasLegacyData(root) && len(projects)==0` (the empty-DB heuristic). **Here the marker governs whether to prompt**, so the DB heuristic is redundant and can be wrong (a user who added projects but never @@ -129,11 +133,11 @@ Bump `SCHEMA_VERSION` to `2` and extend `AppStateMarker` export type MigrationStatus = "pending" | "completed" | "declined" | "failed"; export interface MigrationState { - status: MigrationStatus; // absent file => treated as "pending" - lastAttemptAt?: string; // last Proceed attempt (success or failure) - completedAt?: string; // set when status -> completed + status: MigrationStatus; // absent file => treated as "pending" + lastAttemptAt?: string; // last Proceed attempt (success or failure) + completedAt?: string; // set when status -> completed report?: { projectsImported: number; projectsSkipped: number }; - error?: string; // last failure message (status === "failed") + error?: string; // last failure message (status === "failed") } export interface AppStateMarker { @@ -143,7 +147,7 @@ export interface AppStateMarker { installedAt: string; lastReconciledAt: string; installSource: string; - migration?: MigrationState; // new; preserved across launches + migration?: MigrationState; // new; preserved across launches } ``` @@ -166,14 +170,17 @@ Terminal states (never auto-prompt): `completed`, `declined`. Prompting states: ## 7. Renderer: detection, gate, popup ### 7.1 IPC additions + - main: `ipcMain.handle("appState:getMigration", …)` → returns `MigrationState` (or `{status:"pending"}` when absent); `ipcMain.handle("appState:setMigration", - (_e, partial) => updateMigration(...))`. +(_e, partial) => updateMigration(...))`. - preload: extend the `ao` bridge with `appState: { getMigration, setMigration }`. ### 7.2 Gate (where the popup decides to show) + A `useMigrationOffer()` hook, consumed on the dashboard (`_shell.index.tsx`): + 1. read `window.ao.appState.getMigration()`, 2. if `status ∈ {completed, declined}` → render nothing, 3. else `GET /api/v1/import`; show the popup only when `available === true` @@ -185,13 +192,14 @@ keeping `Don't Migrate` permanent. ### 7.3 Popup actions / data flow -| Action | Effect | -| --- | --- | -| **Proceed** | `POST /api/v1/import`. On success: `setMigration({status:"completed", completedAt, report})`, then invalidate the workspace query so projects appear. On failure: `setMigration({status:"failed", lastAttemptAt, error})` and show the reassurance inline. | -| **Skip** | session-only dismiss; no marker write; re-prompts next launch. | -| **Don't Migrate** (red) | `setMigration({status:"declined", lastAttemptAt})`; never auto-prompts again. | +| Action | Effect | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Proceed** | `POST /api/v1/import`. On success: `setMigration({status:"completed", completedAt, report})`, then invalidate the workspace query so projects appear. On failure: `setMigration({status:"failed", lastAttemptAt, error})` and show the reassurance inline. | +| **Skip** | session-only dismiss; no marker write; re-prompts next launch. | +| **Don't Migrate** (red) | `setMigration({status:"declined", lastAttemptAt})`; never auto-prompts again. | ### 7.4 Copy + - Heading: "Import projects from your earlier AO?" - Body: names the legacy root; "Importing brings in your projects. Your old files are never modified, and you can do this later." (projects-only; **no orchestrator @@ -212,6 +220,7 @@ keeping `Don't Migrate` permanent. `openapi.yaml` + `frontend/src/api/schema.ts`. **App / renderer (new in this design):** + - `frontend/src/main/app-state.ts` — `MigrationState`, schema v2, preserve + `updateMigration`. - `frontend/src/main.ts` — `appState:getMigration` / `appState:setMigration` diff --git a/frontend/src/main/app-state.test.ts b/frontend/src/main/app-state.test.ts index f8711ad81..2fd6ebc56 100644 --- a/frontend/src/main/app-state.test.ts +++ b/frontend/src/main/app-state.test.ts @@ -3,7 +3,13 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { APP_STATE_FILE_NAME, writeAppStateMarker, readMigrationState, updateMigration, type AppStateMarker } from "./app-state"; +import { + APP_STATE_FILE_NAME, + writeAppStateMarker, + readMigrationState, + updateMigration, + type AppStateMarker, +} from "./app-state"; // The exact key set the Go reader (start.go `appState`) unmarshals. const GO_READER_KEYS = ["schemaVersion", "appPath", "version", "installedAt", "lastReconciledAt", "installSource"]; diff --git a/frontend/src/renderer/components/MigrationPopup.test.tsx b/frontend/src/renderer/components/MigrationPopup.test.tsx index bf9dcf73f..add96fdc6 100644 --- a/frontend/src/renderer/components/MigrationPopup.test.tsx +++ b/frontend/src/renderer/components/MigrationPopup.test.tsx @@ -13,7 +13,8 @@ const { getMock, postMock, getMigration, setMigration } = vi.hoisted(() => ({ vi.mock("../lib/api-client", () => ({ apiClient: { GET: getMock, POST: postMock }, - apiErrorMessage: (e: unknown, fb = "Request failed") => (e instanceof Error ? e.message : (e as { message?: string })?.message ?? fb), + apiErrorMessage: (e: unknown, fb = "Request failed") => + e instanceof Error ? e.message : ((e as { message?: string })?.message ?? fb), })); vi.mock("../lib/bridge", () => ({ aoBridge: { appState: { getMigration, setMigration } } })); diff --git a/frontend/src/renderer/components/MigrationPopup.tsx b/frontend/src/renderer/components/MigrationPopup.tsx index 7ee52b5b9..28c5a5ffc 100644 --- a/frontend/src/renderer/components/MigrationPopup.tsx +++ b/frontend/src/renderer/components/MigrationPopup.tsx @@ -71,14 +71,12 @@ export function MigrationPopup() { Import projects from your earlier AO? - We found an existing install at{" "} - {legacyRoot}. Importing brings in - your projects. Your old files are never modified, and you can do this later. + We found an existing install at {legacyRoot}. + Importing brings in your projects. Your old files are never modified, and you can do this later. {error && (
- Migration failed: {error}. Your legacy projects are untouched (nothing is ever deleted). You - can retry. + Migration failed: {error}. Your legacy projects are untouched (nothing is ever deleted). You can retry.
)}

You can run this again later.