|
@@ -66,6 +66,7 @@ import { Persist, persisted } from "@/utils/persist"
|
|
|
import { useMarked } from "@opencode-ai/ui/context/marked"
|
|
import { useMarked } from "@opencode-ai/ui/context/marked"
|
|
|
import { preloadMarkdown } from "@opencode-ai/session-ui/markdown-cache"
|
|
import { preloadMarkdown } from "@opencode-ai/session-ui/markdown-cache"
|
|
|
import { archiveHomeSession } from "./home-session-archive"
|
|
import { archiveHomeSession } from "./home-session-archive"
|
|
|
|
|
+import { shouldOpenSessionInBackground } from "./home-session-open"
|
|
|
import { showToast } from "@/utils/toast"
|
|
import { showToast } from "@/utils/toast"
|
|
|
|
|
|
|
|
const HOME_SESSION_LIMIT = 64
|
|
const HOME_SESSION_LIMIT = 64
|
|
@@ -238,6 +239,20 @@ function useHomeSessionHeaderOpacity(groups: () => HomeSessionGroup[]) {
|
|
|
return { setViewport, setContentRef, setHeaderRef, update, titleOpacity }
|
|
return { setViewport, setContentRef, setHeaderRef, update, titleOpacity }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Cmd+click on macOS (Ctrl+click elsewhere) opens a session tab in the
|
|
|
|
|
+// background without navigating, matching browser conventions.
|
|
|
|
|
+function isBackgroundOpen(event: MouseEvent) {
|
|
|
|
|
+ return shouldOpenSessionInBackground({
|
|
|
|
|
+ mac: typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform),
|
|
|
|
|
+ meta: event.metaKey,
|
|
|
|
|
+ ctrl: event.ctrlKey,
|
|
|
|
|
+ shift: event.shiftKey,
|
|
|
|
|
+ alt: event.altKey,
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type OpenSessionOptions = { background?: boolean }
|
|
|
|
|
+
|
|
|
export function NewHome() {
|
|
export function NewHome() {
|
|
|
const sync = useServerSync()
|
|
const sync = useServerSync()
|
|
|
const layout = useLayout()
|
|
const layout = useLayout()
|
|
@@ -377,9 +392,11 @@ export function NewHome() {
|
|
|
setState("searchFocused", false)
|
|
setState("searchFocused", false)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- function selectSearchSession(session: Session) {
|
|
|
|
|
- openSession(session)
|
|
|
|
|
- closeSearch()
|
|
|
|
|
|
|
+ function selectSearchSession(session: Session, options?: OpenSessionOptions) {
|
|
|
|
|
+ openSession(session, options)
|
|
|
|
|
+ // Background opens keep the search visible so several results can be
|
|
|
|
|
+ // opened in a row.
|
|
|
|
|
+ if (!options?.background) closeSearch()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
command.register("home", () => [
|
|
command.register("home", () => [
|
|
@@ -464,13 +481,17 @@ export function NewHome() {
|
|
|
.forEach((directory) => state.project.markViewed(directory))
|
|
.forEach((directory) => state.project.markViewed(directory))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- function openSession(session: Session) {
|
|
|
|
|
|
|
+ function openSession(session: Session, options?: OpenSessionOptions) {
|
|
|
const project = projectForSession(session, projects(), projectByID())
|
|
const project = projectForSession(session, projects(), projectByID())
|
|
|
const conn = focusedServer()
|
|
const conn = focusedServer()
|
|
|
if (!conn) return
|
|
if (!conn) return
|
|
|
const directory = project?.worktree ?? session.directory
|
|
const directory = project?.worktree ?? session.directory
|
|
|
const ctx = global.ensureServerCtx(conn)
|
|
const ctx = global.ensureServerCtx(conn)
|
|
|
ctx.projects.open(directory)
|
|
ctx.projects.open(directory)
|
|
|
|
|
+ if (options?.background) {
|
|
|
|
|
+ tabs.addSessionTab({ server: ServerConnection.key(conn), sessionId: session.id })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
ctx.projects.touch(directory)
|
|
ctx.projects.touch(directory)
|
|
|
startTransition(() => {
|
|
startTransition(() => {
|
|
|
const tab = tabs.addSessionTab({ server: ServerConnection.key(conn), sessionId: session.id })
|
|
const tab = tabs.addSessionTab({ server: ServerConnection.key(conn), sessionId: session.id })
|
|
@@ -1130,7 +1151,7 @@ function HomeSessionSearch(props: {
|
|
|
onInput: (value: string) => void
|
|
onInput: (value: string) => void
|
|
|
onFocus: () => void
|
|
onFocus: () => void
|
|
|
onClose: () => void
|
|
onClose: () => void
|
|
|
- onSelect: (session: Session) => void
|
|
|
|
|
|
|
+ onSelect: (session: Session, options?: OpenSessionOptions) => void
|
|
|
}) {
|
|
}) {
|
|
|
const language = useLanguage()
|
|
const language = useLanguage()
|
|
|
const [store, setStore] = createStore({ active: "" })
|
|
const [store, setStore] = createStore({ active: "" })
|
|
@@ -1244,7 +1265,7 @@ function HomeSessionSearch(props: {
|
|
|
server={props.server}
|
|
server={props.server}
|
|
|
selected={store.active === homeSessionSearchKey(record)}
|
|
selected={store.active === homeSessionSearchKey(record)}
|
|
|
onHighlight={() => setStore("active", homeSessionSearchKey(record))}
|
|
onHighlight={() => setStore("active", homeSessionSearchKey(record))}
|
|
|
- onSelect={(session) => props.onSelect(session)}
|
|
|
|
|
|
|
+ onSelect={(session, options) => props.onSelect(session, options)}
|
|
|
/>
|
|
/>
|
|
|
)}
|
|
)}
|
|
|
</For>
|
|
</For>
|
|
@@ -1324,7 +1345,7 @@ function HomeSessionSearchResultRow(props: {
|
|
|
server: ServerConnection.Key
|
|
server: ServerConnection.Key
|
|
|
selected: boolean
|
|
selected: boolean
|
|
|
onHighlight: () => void
|
|
onHighlight: () => void
|
|
|
- onSelect: (session: Session) => void
|
|
|
|
|
|
|
+ onSelect: (session: Session, options?: OpenSessionOptions) => void
|
|
|
}) {
|
|
}) {
|
|
|
const title = createMemo(() => sessionTitle(props.record.session.title) || props.record.session.id)
|
|
const title = createMemo(() => sessionTitle(props.record.session.title) || props.record.session.id)
|
|
|
const showProjectName = () => props.showProjectName && props.record.projectName
|
|
const showProjectName = () => props.showProjectName && props.record.projectName
|
|
@@ -1345,7 +1366,7 @@ function HomeSessionSearchResultRow(props: {
|
|
|
group: !!showProjectName(),
|
|
group: !!showProjectName(),
|
|
|
}}
|
|
}}
|
|
|
onMouseEnter={() => props.onHighlight()}
|
|
onMouseEnter={() => props.onHighlight()}
|
|
|
- onClick={() => props.onSelect(props.record.session)}
|
|
|
|
|
|
|
+ onClick={(event) => props.onSelect(props.record.session, { background: isBackgroundOpen(event) })}
|
|
|
>
|
|
>
|
|
|
<HomeSessionLeading
|
|
<HomeSessionLeading
|
|
|
project={props.record.project}
|
|
project={props.record.project}
|
|
@@ -1389,7 +1410,7 @@ function HomeSessionRow(props: {
|
|
|
record: HomeSessionRecord
|
|
record: HomeSessionRecord
|
|
|
showProjectName: boolean
|
|
showProjectName: boolean
|
|
|
server: ServerConnection.Key
|
|
server: ServerConnection.Key
|
|
|
- openSession: (session: Session) => void
|
|
|
|
|
|
|
+ openSession: (session: Session, options?: OpenSessionOptions) => void
|
|
|
archiveSession: (session: Session) => Promise<void>
|
|
archiveSession: (session: Session) => Promise<void>
|
|
|
}) {
|
|
}) {
|
|
|
const language = useLanguage()
|
|
const language = useLanguage()
|
|
@@ -1405,7 +1426,7 @@ function HomeSessionRow(props: {
|
|
|
type="button"
|
|
type="button"
|
|
|
data-component="home-session-row"
|
|
data-component="home-session-row"
|
|
|
class={`${HOME_ROW} h-10 min-w-0 flex-1 gap-2 py-3 pl-3 pr-10`}
|
|
class={`${HOME_ROW} h-10 min-w-0 flex-1 gap-2 py-3 pl-3 pr-10`}
|
|
|
- onClick={() => props.openSession(props.record.session)}
|
|
|
|
|
|
|
+ onClick={(event) => props.openSession(props.record.session, { background: isBackgroundOpen(event) })}
|
|
|
>
|
|
>
|
|
|
<HomeSessionLeading
|
|
<HomeSessionLeading
|
|
|
project={props.record.project}
|
|
project={props.record.project}
|