@@ -246,10 +273,9 @@ function HomeServerRow(props: {
)
}
-type HomeProjectListProps = {
- controller: HomeProjectsController
+type HomeProjectListProps = HomeProjectsViewProps & {
server: ServerConnection.Any
- projects: LocalProject[]
+ items: LocalProject[]
}
function HomeProjectList(props: HomeProjectListProps) {
@@ -276,10 +302,9 @@ function HomeProjectList(props: HomeProjectListProps) {
onDragEnd={(event) => {
const source = event.operation.source
if (event.canceled || !isSortable(source)) return
- if (source.initialIndex !== source.index)
- props.controller.moveProject(props.server, source.id.toString(), source.index)
- if (props.controller.selection().server !== ServerConnection.key(props.server))
- props.controller.selectProject(props.server, source.id.toString())
+ if (source.initialIndex !== source.index) props.onMoveProject(props.server, source.id.toString(), source.index)
+ if (props.selection().server !== ServerConnection.key(props.server))
+ props.onSelectProject(props.server, source.id.toString())
}}
>
@@ -288,7 +313,7 @@ function HomeProjectList(props: HomeProjectListProps) {
remounts all rows — killing any in-flight drag activation (the
row's sortable unregisters on unmount) and discarding animations.
String keys keep row elements alive and move them on reorder. */}
- project.worktree)}>
+ project.worktree)}>
{(worktree, index) => }
@@ -302,34 +327,35 @@ function HomeProjectSlot(
index: () => number
},
) {
- const project = createMemo(() => props.projects.find((item) => item.worktree === props.worktree))
+ const project = createMemo(() => props.items.find((item) => item.worktree === props.worktree))
return (
{(item) => (
)}
)
}
-function HomeProjectEmpty(props: {
- controller: HomeProjectsController
- server: ServerConnection.Any
- recentlyClosed: LocalProject[]
-}) {
- const unreachable = () => props.controller.serverHealth(props.server)?.healthy === false
+function HomeProjectEmpty(
+ props: HomeProjectsViewProps & {
+ server: ServerConnection.Any
+ items: LocalProject[]
+ },
+) {
+ const unreachable = () => props.serverHealth(props.server)?.healthy === false
return (
-
0}>
+ 0}>
-
- {props.controller.language.t("home.recentlyClosed")}
-
+
{props.language.t("home.recentlyClosed")}
-
- {(project) => }
+
+ {(project) => }
)
}
-function HomeRecentlyClosedRow(props: {
- controller: HomeProjectsController
- project: LocalProject
- server: ServerConnection.Any
-}) {
- const unreachable = () => props.controller.serverHealth(props.server)?.healthy === false
+function HomeRecentlyClosedRow(
+ props: HomeProjectsViewProps & {
+ project: LocalProject
+ server: ServerConnection.Any
+ },
+) {
+ const unreachable = () => props.serverHealth(props.server)?.healthy === false
const path = () => {
- const home = props.controller.homedir()
+ const home = props.homedir()
const worktree = props.project.worktree
if (home && (worktree === home || worktree.startsWith(`${home}/`))) return `~${worktree.slice(home.length)}`
return worktree
@@ -375,7 +400,7 @@ function HomeRecentlyClosedRow(props: {
data-component="home-recently-closed-row"
class={`${HOME_PROJECT_NAV_ROW} disabled:opacity-60`}
disabled={unreachable()}
- onClick={() => props.controller.addProjects(props.server, [props.project.worktree])}
+ onClick={() => props.onAddProjects(props.server, [props.project.worktree])}
>
{displayName(props.project)}
@@ -384,16 +409,17 @@ function HomeRecentlyClosedRow(props: {
)
}
-function HomeProjectRow(props: {
- controller: HomeProjectsController
- project: LocalProject
- server: ServerConnection.Any
- index: () => number
- serverSelected: boolean
- selected: boolean
- unseenCount: number
-}) {
- const serverUnreachable = () => props.controller.serverHealth(props.server)?.healthy === false
+function HomeProjectRow(
+ props: HomeProjectsViewProps & {
+ project: LocalProject
+ server: ServerConnection.Any
+ index: () => number
+ serverSelected: boolean
+ selected: boolean
+ unseen: number
+ },
+) {
+ const serverUnreachable = () => props.serverHealth(props.server)?.healthy === false
const sortable = useSortable({
get id() {
return props.project.worktree
@@ -403,7 +429,7 @@ function HomeProjectRow(props: {
},
})
let pointerDownSelected: boolean | undefined
- const menuID = () => props.controller.projectMenuID(props.server, props.project.worktree)
+ const menuID = () => props.projectMenuID(props.server, props.project.worktree)
return (
{
// The drag sensor calls preventDefault on post-drag clicks; never
@@ -440,12 +466,12 @@ function HomeProjectRow(props: {
if (event.defaultPrevented) return
// Keyboard activation and touch taps keep the original toggle.
if (event.detail === 0 || pointerDownSelected === undefined) {
- props.controller.selectProject(props.server, props.project.worktree)
+ props.onSelectProject(props.server, props.project.worktree)
return
}
// Mouse: pointerdown already selected unselected rows; a plain click
// on an already-selected row toggles it off.
- if (pointerDownSelected) props.controller.selectProject(props.server, props.project.worktree)
+ if (pointerDownSelected) props.onSelectProject(props.server, props.project.worktree)
pointerDownSelected = undefined
}}
>
@@ -454,14 +480,14 @@ function HomeProjectRow(props: {
diff --git a/packages/app/src/pages/home/home-sessions-controller.tsx b/packages/app/src/pages/home/home-sessions-controller.tsx
index 6467c06436..723d338167 100644
--- a/packages/app/src/pages/home/home-sessions-controller.tsx
+++ b/packages/app/src/pages/home/home-sessions-controller.tsx
@@ -560,18 +560,19 @@ function createHomeSessionHeaderController(groups: () => HomeSessionGroup[]) {
export type HomeSessionsController = ReturnType
export function HomeSessionStatusController(props: {
- controller: HomeSessionsController
+ server: Accessor
record: HomeSessionRecord
+ isOpenTab: (record: HomeSessionRecord) => boolean
render: (state: { unread: Accessor; loading: Accessor; open: Accessor }) => JSX.Element
}) {
const avatar = useSessionTabAvatarState(
- props.controller.server,
+ props.server,
() => props.record.session.directory,
() => props.record.session.id,
)
return props.render({
unread: avatar.unread,
loading: avatar.loading,
- open: () => props.controller.hasOpenTab(props.record),
+ open: () => props.isOpenTab(props.record),
})
}
diff --git a/packages/app/src/pages/home/home-sessions-view.tsx b/packages/app/src/pages/home/home-sessions-view.tsx
index 12b383c4a9..1e878ffe41 100644
--- a/packages/app/src/pages/home/home-sessions-view.tsx
+++ b/packages/app/src/pages/home/home-sessions-view.tsx
@@ -1,4 +1,5 @@
-import { type ComponentProps, createMemo, For, Show } from "solid-js"
+import type { Session } from "@opencode-ai/sdk/v2/client"
+import { type Accessor, createMemo, For, Show } from "solid-js"
import { Spinner } from "@opencode-ai/ui/spinner"
import { ScrollView } from "@opencode-ai/ui/scroll-view"
import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
@@ -6,14 +7,16 @@ import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
import { useLanguage } from "@/context/language"
+import { ServerConnection } from "@/context/server"
import { SessionTabAvatarView } from "@/pages/layout/session-tab-avatar"
import { sessionTitle } from "@/utils/session-title"
import { shouldOpenSessionInBackground } from "../home-session-open"
import {
HomeSessionStatusController,
homeSessionSearchKey,
+ type HomeSessionGroup,
type HomeSessionRecord,
- type HomeSessionsController,
+ type OpenSessionOptions,
} from "./home-sessions-controller"
const SHOW_HOME_SESSION_ARCHIVE = false
@@ -43,19 +46,52 @@ function isBackgroundOpen(event: MouseEvent) {
})
}
-export function HomeSessionsView(props: { controller: HomeSessionsController }) {
+export type HomeSessionsViewProps = {
+ language: ReturnType
+ groups: Accessor
+ loading: Accessor
+ showProjectName: Accessor
+ server: Accessor
+ canCreateSession: Accessor
+ searchValue: Accessor
+ searchPlaceholder: Accessor
+ searchOpen: Accessor
+ searchLoading: Accessor
+ searchResults: Accessor
+ searchActive: Accessor
+ searchNoResultsLabel: Accessor
+ titleOpacity: (id: HomeSessionGroup["id"]) => number
+ isOpenTab: (record: HomeSessionRecord) => boolean
+ onCreateSession: () => void
+ onOpenSession: (session: Session, options?: OpenSessionOptions) => void
+ onArchiveSession: (session: Session) => Promise
+ onSetHoverTarget: (element: HTMLElement) => void
+ onSetThumbTrack: (element: HTMLDivElement) => void
+ onSetContent: (element: HTMLDivElement) => void
+ onSetHeader: (id: HomeSessionGroup["id"], element: HTMLDivElement) => void
+ onWheel: (event: WheelEvent) => void
+ onSetSearchRoot: (element: HTMLDivElement) => void
+ onSetSearchInput: (element: HTMLInputElement) => void
+ onSetSearchList: (element: HTMLDivElement) => void
+ onSearchFocus: () => void
+ onSearchInput: (value: string) => void
+ onSearchClose: () => void
+ onSearchMove: (delta: number) => void
+ onSearchSelectActive: () => void
+ onSearchHighlight: (record: HomeSessionRecord) => void
+ onSearchSelect: (record: HomeSessionRecord, options?: OpenSessionOptions) => void
+}
+
+export function HomeSessionsView(props: HomeSessionsViewProps) {
return (
-
-
-
0 && props.controller.canCreateSession()}>
+
+
+
0 && props.canCreateSession()}>
- {props.controller.language.t("command.session.new")}
+ {props.language.t("command.session.new")}
-
+
}
>
0}
+ when={props.groups().length > 0}
fallback={
}
>
-
-
+
+
{(group, index) => (
<>
props.controller.header.setHeader(group.id, element)}
+ titleOpacity={props.titleOpacity(group.id)}
+ onSetRef={(element) => props.onSetHeader(group.id, element)}
elevated={index() === 0}
/>
-
- {(record) => }
-
+ {(record) => }
>
)}
@@ -124,14 +158,16 @@ export function HomeSessionsView(props: { controller: HomeSessionsController })
}
function HomeSessionLeadingController(props: {
- controller: HomeSessionsController
+ server: HomeSessionsViewProps["server"]
+ isOpenTab: HomeSessionsViewProps["isOpenTab"]
record: HomeSessionRecord
revealProjectOnHover: boolean
}) {
return (
(
-
-
+
+
@@ -193,25 +229,25 @@ function HomeSessionSearch(props: { controller: HomeSessionsController }) {
}
>
0}
+ when={props.searchResults().length > 0}
fallback={
- {props.controller.search.noResultsLabel()}
+ {props.searchNoResultsLabel()}
}
>
- {props.controller.language.t("home.sessions.search.sessions")}
+ {props.language.t("home.sessions.search.sessions")}
-
+
-
+
{(record) => (
)}
@@ -227,57 +263,57 @@ function HomeSessionSearch(props: { controller: HomeSessionsController }) {