diff --git a/packages/app/src/app.tsx b/packages/app/src/app.tsx
index f4cf14518c..b7f87a09cd 100644
--- a/packages/app/src/app.tsx
+++ b/packages/app/src/app.tsx
@@ -9,7 +9,7 @@ import { Font } from "@opencode-ai/ui/font"
import { Splash } from "@opencode-ai/ui/logo"
import { ThemeProvider } from "@opencode-ai/ui/theme/context"
import { MetaProvider } from "@solidjs/meta"
-import { type BaseRouterProps, Navigate, Route, Router, useParams, useSearchParams } from "@solidjs/router"
+import { type BaseRouterProps, Navigate, Route, Router, useNavigate, useParams, useSearchParams } from "@solidjs/router"
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"
import { Effect } from "effect"
import {
@@ -32,7 +32,7 @@ import { CommandProvider, useCommand, type CommandOption } from "@/context/comma
import { CommentsProvider } from "@/context/comments"
import { FileProvider } from "@/context/file"
import { ServerSDKProvider } from "@/context/server-sdk"
-import { ServerSyncProvider } from "@/context/server-sync"
+import { ServerSyncProvider, useServerSync } from "@/context/server-sync"
import { GlobalProvider, useGlobal } from "@/context/global"
import { HighlightsProvider } from "@/context/highlights"
import { LanguageProvider, type Locale, useLanguage } from "@/context/language"
@@ -52,9 +52,10 @@ import LegacyLayout from "@/pages/layout"
import NewLayout from "@/pages/layout-new"
import { ErrorPage } from "./pages/error"
import { useCheckServerHealth } from "./utils/server-health"
-import { legacySessionServer, requireServerKey, sessionHref } from "./utils/session-route"
+import { legacySessionHref, legacySessionServer, requireServerKey, sessionHref } from "./utils/session-route"
+import { createSessionLineage } from "@/pages/session/session-lineage"
-import { SessionPage, TargetSessionRouteContent } from "@/pages/session"
+import { SessionPage, SessionRouteErrorBoundary, TargetSessionRouteContent } from "@/pages/session"
import { NewHome, LegacyHome } from "@/pages/home"
const NewSession = lazy(() => import("@/pages/new-session"))
@@ -88,10 +89,14 @@ const SessionRoute = () => {
tabs.newDraft({ server: server.key, directory: sdk().directory }, search.prompt)
})
- return
+ return (
+
+
+
+ )
}
-const TargetSessionRoute = () => {
+function TargetServerRoute(props: ParentProps) {
const params = useParams<{ serverKey: string; id: string }>()
const global = useGlobal()
const conn = createMemo(() => {
@@ -105,14 +110,47 @@ const TargetSessionRoute = () => {
// re-resolves reactively instead); both rely on this key for server changes.
-
-
-
+ {props.children}
)
}
+const TargetSessionRoute = () => (
+
+
+
+)
+
+function LegacyTargetSessionRoute() {
+ const params = useParams<{ serverKey: string; id: string }>()
+ return (
+
+
+
+
+
+ )
+}
+
+function LegacyTargetSessionRedirect() {
+ const params = useParams<{ id: string }>()
+ const navigate = useNavigate()
+ const sync = useServerSync()
+ const current = createSessionLineage(
+ () => params.id,
+ () => sync().session.lineage,
+ )
+
+ createEffect(() => {
+ const directory = current()?.session.directory
+ if (!directory) return
+ navigate(legacySessionHref(directory, params.id), { replace: true })
+ })
+
+ return null
+}
+
// Wraps the non-draft routes. They are gated on (and keyed to) the globally selected
// server via ServerKey, then provide the server-scoped shell (Permission/Layout/
// Notification/Models + the visual Layout) for that server.
@@ -542,7 +580,14 @@ function Routes(props: { serverScoped?: JSX.Element }) {
{routeProps.children}
)}
>
- {}
+
+ {
+ <>
+
+
+ >
+ }
+
} />
@@ -550,15 +595,15 @@ function Routes(props: { serverScoped?: JSX.Element }) {
-
+
+
-
>
)
}
-function LegacyTargetSessionRoute() {
+function NewLayoutLegacySessionRedirect() {
const server = useServer()
const tabs = useTabs()
const params = useParams<{ id: string }>()
diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx
index 762b8b2c8e..c4d84a96f8 100644
--- a/packages/app/src/pages/home.tsx
+++ b/packages/app/src/pages/home.tsx
@@ -35,7 +35,7 @@ import { usePlatform } from "@/context/platform"
import { DateTime } from "luxon"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { useDirectoryPicker } from "@/components/directory-picker"
-import { useSettingsCommand } from "@/components/settings-dialog"
+import { useSettingsDialog } from "@/components/settings-dialog"
import { DialogSelectServer, useServerManagementController } from "@/components/dialog-select-server"
import { DialogServerV2 } from "@/components/settings-v2/dialog-server-v2"
import { ServerConnection, serverName, useServer } from "@/context/server"
@@ -267,7 +267,7 @@ export function NewHome() {
const command = useCommand()
const notification = useNotification()
const marked = useMarked()
- const openSettings = useSettingsCommand()
+ const openSettings = useSettingsDialog()
let focusSessionSearch: (() => void) | undefined
const [state, setState] = createStore({
search: "",
diff --git a/packages/app/src/pages/layout-new.tsx b/packages/app/src/pages/layout-new.tsx
index 329b7c56b0..f73e7b17eb 100644
--- a/packages/app/src/pages/layout-new.tsx
+++ b/packages/app/src/pages/layout-new.tsx
@@ -6,11 +6,13 @@ import { Titlebar, type TitlebarUpdate } from "@/components/titlebar"
import { usePlatform } from "@/context/platform"
import { setNavigate } from "@/utils/notification-click"
import { setV2Toast, ToastRegion } from "@/utils/toast"
+import { useSettingsCommand } from "@/components/settings-dialog"
export default function NewLayout(props: ParentProps) {
const platform = usePlatform()
const navigate = useNavigate()
setNavigate(navigate)
+ useSettingsCommand()
createEffect(() => setV2Toast(true))
diff --git a/packages/app/src/pages/new-session.tsx b/packages/app/src/pages/new-session.tsx
index 461be60450..7831ed8514 100644
--- a/packages/app/src/pages/new-session.tsx
+++ b/packages/app/src/pages/new-session.tsx
@@ -6,7 +6,6 @@ import { Tooltip } from "@opencode-ai/ui/tooltip"
import { NewSessionDesignView } from "@/components/session"
import { PromptInput } from "@/components/prompt-input"
import { StatusPopoverV2 } from "@/components/status-popover"
-import { useSettingsCommand } from "@/components/settings-dialog"
import {
PromptProjectAddButton,
PromptProjectSelector,
@@ -45,7 +44,6 @@ export default function NewSessionPage() {
const [searchParams, setSearchParams] = useSearchParams<{ draftId?: string; prompt?: string }>()
useComposerCommands()
- useSettingsCommand()
let inputRef: HTMLDivElement | undefined
diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx
index 46bd907167..84f6b01309 100644
--- a/packages/app/src/pages/session.tsx
+++ b/packages/app/src/pages/session.tsx
@@ -35,7 +35,7 @@ import { previewSelectedLines } from "@opencode-ai/session-ui/pierre/selection-b
import { Button } from "@opencode-ai/ui/button"
import { showToast } from "@/utils/toast"
import { base64Encode, checksum } from "@opencode-ai/core/util/encode"
-import { Navigate, useLocation, useNavigate, useParams, useSearchParams } from "@solidjs/router"
+import { useLocation, useNavigate, useParams, useSearchParams } from "@solidjs/router"
import { NewSessionView, SessionHeader } from "@/components/session"
import { ErrorPage } from "@/pages/error"
import { CommentsProvider, useComments } from "@/context/comments"
@@ -56,7 +56,6 @@ import { useSync } from "@/context/sync"
import { useTabs } from "@/context/tabs"
import { TerminalProvider, useTerminal } from "@/context/terminal"
import { PromptInput } from "@/components/prompt-input"
-import { useSettingsCommand } from "@/components/settings-dialog"
import { type FollowupDraft, sendFollowupDraft } from "@/components/prompt-input/submit"
import {
createPromptInputController,
@@ -153,7 +152,7 @@ export function TargetSessionRouteContent() {
)
}
-function SessionRouteErrorBoundary(
+export function SessionRouteErrorBoundary(
props: ParentProps<{ sessionID?: string; serverKey?: ServerConnection.Key; padded?: boolean }>,
) {
const settings = useSettings()
@@ -221,7 +220,6 @@ function SessionErrorFallback(props: { error: unknown; sessionID?: string; serve
function ResolvedTargetSessionRoute() {
const params = useParams<{ serverKey: string; id: string }>()
- const settings = useSettings()
const tabs = useTabs()
const sync = useServerSync()
const serverKey = createMemo(() => requireServerKey(params.serverKey))
@@ -248,16 +246,11 @@ function ResolvedTargetSessionRoute() {
the terminal. Same-workspace tab switches keep it open because warm
targets resolve synchronously from the sync cache. */}
- }
- >
-
-
-
-
-
-
+
+
+
+
+
)
@@ -1021,7 +1014,6 @@ export default function Page() {
}
useComposerCommands()
- useSettingsCommand()
useSessionCommands({
navigateMessageByOffset,
setActiveMessage,