Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 2 additions & 60 deletions packages/app/src/pages/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import {
batch,
createEffect,
Expand Down Expand Up @@ -38,13 +38,6 @@
import { setOpenSettings } from "@/utils/settings-navigation"
import { Worktree as WorktreeState } from "@/utils/worktree"
import { usePinnedDraft } from "@/components/prompt-input/pinned-draft"
import {
runHomepageMigration,
HOMEPAGE_MIGRATION_SENTINEL_KEY,
type LegacyHomepagePromptStore,
} from "@/components/prompt-input/homepage-migration"
import { usePortableDraft } from "@/components/prompt-input/portable-draft"
import { createMigrationStorageIO } from "@/components/prompt-input/homepage-migration-storage"

import { useDialog } from "@opencode-ai/ui/context/dialog"
import { useTheme } from "@opencode-ai/ui/theme/context"
Expand All @@ -64,6 +57,7 @@
import { findPawworkSessionNavigationTarget } from "./layout/pawwork-session-nav"
import { createShellNavigation } from "./layout/shell-navigation"
import { useUpdatePolling } from "./layout/layout-update-polling"
import { useHomepageMigration } from "./layout/layout-homepage-migration"
import { sessionNotificationHref, useSDKNotificationToasts } from "./layout/layout-sdk-event-effects"
import { registerLayoutCommands } from "./layout/layout-commands"
import { LayoutShellFrame } from "./layout/layout-shell-frame"
Expand Down Expand Up @@ -681,59 +675,7 @@
resetWorkspace,
})

// Run the v7 homepage-draft migration as soon as a directory becomes
// available (fire-and-forget). currentDir() can be empty during the initial
// autoselect phase, so onMount alone would skip migration for that session.
// The migration writes a sentinel internally and is idempotent, so subsequent
// effect ticks are no-ops once it has run.
let homepageMigrationStarted = false
createEffect(() => {
if (homepageMigrationStarted) return
const directory = currentDir()
if (!directory) return
homepageMigrationStarted = true

const portable = usePortableDraft()
const sentinelTarget = Persist.global(HOMEPAGE_MIGRATION_SENTINEL_KEY)
const { read: readRaw, write: writeRaw, remove: removeRaw } = createMigrationStorageIO(platform)

void runHomepageMigration({
portable,
currentDirectory: directory,
readSentinel: async () => {
const raw = await readRaw(sentinelTarget)
if (!raw) return null
try {
return JSON.parse(raw) as import("@/components/prompt-input/homepage-migration").MigrationSentinel
} catch {
return null
}
},
writeSentinel: async (sentinel) => {
await writeRaw(sentinelTarget, JSON.stringify(sentinel))
},
loadLegacyHomepage: async (dir) => {
const target = Persist.workspace(dir, "prompt")
const raw = await readRaw(target)
if (!raw) return null
try {
return JSON.parse(raw) as LegacyHomepagePromptStore
} catch {
return null
}
},
clearLegacyHomepage: async (dir) => {
// Must await: desktop removeItem is async and a rejection here must
// propagate up to homepage-migration's failed-sentinel path. Without
// the await, the migration would write status: "complete" even if
// the legacy store delete failed.
await removeRaw(Persist.workspace(dir, "prompt"))
},
}).catch((err) => {
// Log diagnostic; migration retries automatically on next boot.
console.warn("[homepage-migration] unexpected failure", err)
})
})
useHomepageMigration({ currentDir, platform })

async function renameProject(project: LocalProject, next: string) {
const current = displayName(project)
Expand Down
66 changes: 66 additions & 0 deletions packages/app/src/pages/layout/layout-homepage-migration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { createEffect } from "solid-js"
import type { Platform } from "@/context/platform"
import { Persist } from "@/utils/persist"
import {
runHomepageMigration,
HOMEPAGE_MIGRATION_SENTINEL_KEY,
type LegacyHomepagePromptStore,
} from "@/components/prompt-input/homepage-migration"
import { usePortableDraft } from "@/components/prompt-input/portable-draft"
import { createMigrationStorageIO } from "@/components/prompt-input/homepage-migration-storage"

export function useHomepageMigration(input: { currentDir: () => string; platform: Platform }) {
// Run the v7 homepage-draft migration as soon as a directory becomes
// available (fire-and-forget). currentDir() can be empty during the initial
// autoselect phase, so onMount alone would skip migration for that session.
// The migration writes a sentinel internally and is idempotent, so subsequent
// effect ticks are no-ops once it has run.
let homepageMigrationStarted = false
createEffect(() => {
if (homepageMigrationStarted) return
const directory = input.currentDir()
if (!directory) return
homepageMigrationStarted = true

const portable = usePortableDraft()
const sentinelTarget = Persist.global(HOMEPAGE_MIGRATION_SENTINEL_KEY)
const { read: readRaw, write: writeRaw, remove: removeRaw } = createMigrationStorageIO(input.platform)

void runHomepageMigration({
portable,
currentDirectory: directory,
readSentinel: async () => {
const raw = await readRaw(sentinelTarget)
if (!raw) return null
try {
return JSON.parse(raw) as import("@/components/prompt-input/homepage-migration").MigrationSentinel
} catch {
return null
}
},
writeSentinel: async (sentinel) => {
await writeRaw(sentinelTarget, JSON.stringify(sentinel))
},
loadLegacyHomepage: async (dir) => {
const target = Persist.workspace(dir, "prompt")
const raw = await readRaw(target)
if (!raw) return null
try {
return JSON.parse(raw) as LegacyHomepagePromptStore
} catch {
return null
}
},
clearLegacyHomepage: async (dir) => {
// Must await: desktop removeItem is async and a rejection here must
// propagate up to homepage-migration's failed-sentinel path. Without
// the await, the migration would write status: "complete" even if
// the legacy store delete failed.
await removeRaw(Persist.workspace(dir, "prompt"))
},
}).catch((err) => {
// Log diagnostic; migration retries automatically on next boot.
console.warn("[homepage-migration] unexpected failure", err)
})
})
}
Loading