mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-24 11:46:17 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05778c111c | ||
|
|
43000a15d3 | ||
|
|
1a4f428ef1 | ||
|
|
45923c0293 |
@@ -10,6 +10,7 @@ import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||
import { getFilename } from "@opencode-ai/core/util/path"
|
||||
import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { createMediaQuery } from "@solid-primitives/media"
|
||||
import { Portal } from "solid-js/web"
|
||||
import { useCommand } from "@/context/command"
|
||||
import { useLanguage } from "@/context/language"
|
||||
@@ -158,6 +159,7 @@ export function SessionHeader() {
|
||||
const isV2 = settings.general.newLayoutDesigns
|
||||
const search = settings.visibility.search
|
||||
const status = settings.visibility.status
|
||||
const isDesktop = createMediaQuery("(min-width: 768px)")
|
||||
|
||||
const [exists, setExists] = createStore<Partial<Record<OpenApp, boolean>>>({
|
||||
finder: true,
|
||||
@@ -236,6 +238,7 @@ export function SessionHeader() {
|
||||
statusLabel: language.t("status.popover.trigger"),
|
||||
reviewLabel: language.t("command.review.toggle"),
|
||||
reviewKeybind: command.keybind("review.toggle"),
|
||||
reviewVisible: isDesktop(),
|
||||
reviewOpened: view().reviewPanel.opened(),
|
||||
onReviewToggle: () => view().reviewPanel.toggle(),
|
||||
}))
|
||||
@@ -518,6 +521,7 @@ type SessionHeaderV2ActionsState = {
|
||||
statusLabel: string
|
||||
reviewLabel: string
|
||||
reviewKeybind: string
|
||||
reviewVisible: boolean
|
||||
reviewOpened: boolean
|
||||
onReviewToggle: () => void
|
||||
}
|
||||
@@ -530,20 +534,22 @@ function SessionHeaderV2Actions(props: { state: SessionHeaderV2ActionsState }) {
|
||||
<StatusPopoverV2 />
|
||||
</Tooltip>
|
||||
</Show>
|
||||
<TooltipKeybind title={props.state.reviewLabel} keybind={props.state.reviewKeybind}>
|
||||
<IconButtonV2
|
||||
type="button"
|
||||
variant="ghost-muted"
|
||||
size="large"
|
||||
class="!w-9 shrink-0"
|
||||
state={props.state.reviewOpened ? "pressed" : undefined}
|
||||
onClick={props.state.onReviewToggle}
|
||||
aria-label={props.state.reviewLabel}
|
||||
aria-expanded={props.state.reviewOpened}
|
||||
aria-controls="review-panel"
|
||||
icon={<IconV2 name="sidebar-right" />}
|
||||
/>
|
||||
</TooltipKeybind>
|
||||
<Show when={props.state.reviewVisible}>
|
||||
<TooltipKeybind title={props.state.reviewLabel} keybind={props.state.reviewKeybind}>
|
||||
<IconButtonV2
|
||||
type="button"
|
||||
variant="ghost-muted"
|
||||
size="large"
|
||||
class="!w-9 shrink-0"
|
||||
state={props.state.reviewOpened ? "pressed" : undefined}
|
||||
onClick={props.state.onReviewToggle}
|
||||
aria-label={props.state.reviewLabel}
|
||||
aria-expanded={props.state.reviewOpened}
|
||||
aria-controls="review-panel"
|
||||
icon={<IconV2 name="sidebar-right" />}
|
||||
/>
|
||||
</TooltipKeybind>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ export function NewHome() {
|
||||
|
||||
return (
|
||||
<div class="rounded-[10px] shadow-[var(--v2-elevation-raised)] m-2 min-h-0 lg:overflow-hidden bg-v2-background-bg-base self-stretch flex-1">
|
||||
<div class="mx-auto grid w-full h-full max-w-[1080px] gap-8 px-6 pb-16 lg:grid-cols-[280px_minmax(0,720px)]">
|
||||
<div class="mx-auto grid h-full w-full max-w-[1080px] grid-rows-[auto_minmax(0,1fr)_auto] gap-4 px-3 pb-3 lg:grid-cols-[280px_minmax(0,720px)] lg:grid-rows-1 lg:gap-8 lg:px-6 lg:pb-16">
|
||||
<HomeProjectColumn
|
||||
projects={projects()}
|
||||
selected={state.selection}
|
||||
@@ -355,7 +355,7 @@ export function NewHome() {
|
||||
/>
|
||||
|
||||
<section
|
||||
class="min-h-0 min-w-0 flex-1 flex flex-col pt-12"
|
||||
class="min-h-0 min-w-0 flex-1 flex flex-col pt-6 lg:pt-12"
|
||||
aria-label={language.t("sidebar.project.recentSessions")}
|
||||
>
|
||||
<HomeSessionSearch
|
||||
@@ -419,6 +419,12 @@ export function NewHome() {
|
||||
</div>
|
||||
</ScrollView>
|
||||
</section>
|
||||
<HomeUtilityNav
|
||||
class="flex lg:hidden"
|
||||
openSettings={openSettings}
|
||||
openHelp={() => platform.openLink("https://opencode.ai/desktop-feedback")}
|
||||
language={language}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -443,7 +449,7 @@ function HomeProjectColumn(props: {
|
||||
const dialog = useDialog()
|
||||
const controller = useServerManagementController({ navigateOnAdd: false })
|
||||
return (
|
||||
<aside class="flex min-w-0 flex-col lg:pt-[52px] mt-14 gap-4" aria-label={props.language.t("home.projects")}>
|
||||
<aside class="mt-6 flex min-w-0 flex-col gap-4 lg:mt-14 lg:pt-[52px]" aria-label={props.language.t("home.projects")}>
|
||||
<div class="flex h-7 min-w-0 items-center justify-between pl-1.5">
|
||||
<div class={HOME_SECTION_LABEL}>{props.language.t("home.projects")}</div>
|
||||
<Show when={global.servers.list().length === 1}>
|
||||
@@ -489,28 +495,44 @@ function HomeProjectColumn(props: {
|
||||
}}
|
||||
</For>
|
||||
</Show>
|
||||
<div class="mt-4 flex min-w-0 flex-col gap-1">
|
||||
<button
|
||||
type="button"
|
||||
class={`${HOME_PROJECT_NAV_ROW} text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted`}
|
||||
onClick={props.openSettings}
|
||||
>
|
||||
<IconV2 name="settings-gear" size="small" />
|
||||
<span class={HOME_PROJECT_NAV_LABEL}>{props.language.t("sidebar.settings")}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class={`${HOME_PROJECT_NAV_ROW} text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted`}
|
||||
onClick={props.openHelp}
|
||||
>
|
||||
<IconV2 name="help" size="small" />
|
||||
<span class={HOME_PROJECT_NAV_LABEL}>{props.language.t("sidebar.help")}</span>
|
||||
</button>
|
||||
</div>
|
||||
<HomeUtilityNav
|
||||
class="mt-4 hidden lg:flex"
|
||||
openSettings={props.openSettings}
|
||||
openHelp={props.openHelp}
|
||||
language={props.language}
|
||||
/>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
|
||||
function HomeUtilityNav(props: {
|
||||
class?: string
|
||||
openSettings: () => void
|
||||
openHelp: () => void
|
||||
language: ReturnType<typeof useLanguage>
|
||||
}) {
|
||||
return (
|
||||
<div class={`${props.class ?? ""} min-w-0 flex-col gap-1`}>
|
||||
<button
|
||||
type="button"
|
||||
class={`${HOME_PROJECT_NAV_ROW} text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted`}
|
||||
onClick={props.openSettings}
|
||||
>
|
||||
<IconV2 name="settings-gear" size="small" />
|
||||
<span class={HOME_PROJECT_NAV_LABEL}>{props.language.t("sidebar.settings")}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class={`${HOME_PROJECT_NAV_ROW} text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted`}
|
||||
onClick={props.openHelp}
|
||||
>
|
||||
<IconV2 name="help" size="small" />
|
||||
<span class={HOME_PROJECT_NAV_LABEL}>{props.language.t("sidebar.help")}</span>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function HomeServerRow(props: {
|
||||
server: ServerConnection.Any
|
||||
selected: boolean
|
||||
|
||||
@@ -1568,40 +1568,42 @@ export default function Page() {
|
||||
/>
|
||||
)
|
||||
|
||||
const mobileTabs = (compact = false) => (
|
||||
<Tabs value={store.mobileTab} class="h-auto">
|
||||
<Tabs.List class={compact ? "!h-9" : undefined}>
|
||||
<Tabs.Trigger
|
||||
value="session"
|
||||
class="!w-1/2 !max-w-none"
|
||||
classes={{ button: compact ? "w-full !py-2" : "w-full" }}
|
||||
onClick={() => setStore("mobileTab", "session")}
|
||||
>
|
||||
{language.t("session.tab.session")}
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger
|
||||
value="changes"
|
||||
class="!w-1/2 !max-w-none !border-r-0"
|
||||
classes={{ button: compact ? "w-full !py-2" : "w-full" }}
|
||||
onClick={() => setStore("mobileTab", "changes")}
|
||||
>
|
||||
{hasReview()
|
||||
? language.t("session.review.filesChanged", { count: reviewCount() })
|
||||
: language.t("session.review.change.other")}
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
)
|
||||
|
||||
return (
|
||||
<div class="relative size-full overflow-hidden flex flex-col">
|
||||
{sessionSync() ?? ""}
|
||||
<SessionHeader />
|
||||
<div
|
||||
class="flex-1 min-h-0 flex flex-col md:flex-row "
|
||||
class="flex-1 min-h-0 flex flex-col md:flex-row"
|
||||
classList={{
|
||||
"gap-2 p-2": settings.general.newLayoutDesigns(),
|
||||
}}
|
||||
>
|
||||
<Show when={!isDesktop() && !!params.id}>
|
||||
<Tabs value={store.mobileTab} class="h-auto">
|
||||
<Tabs.List>
|
||||
<Tabs.Trigger
|
||||
value="session"
|
||||
class="!w-1/2 !max-w-none"
|
||||
classes={{ button: "w-full" }}
|
||||
onClick={() => setStore("mobileTab", "session")}
|
||||
>
|
||||
{language.t("session.tab.session")}
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger
|
||||
value="changes"
|
||||
class="!w-1/2 !max-w-none !border-r-0"
|
||||
classes={{ button: "w-full" }}
|
||||
onClick={() => setStore("mobileTab", "changes")}
|
||||
>
|
||||
{hasReview()
|
||||
? language.t("session.review.filesChanged", { count: reviewCount() })
|
||||
: language.t("session.review.change.other")}
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
</Show>
|
||||
<Show when={!isDesktop() && !!params.id && !settings.general.newLayoutDesigns()}>{mobileTabs()}</Show>
|
||||
|
||||
<div
|
||||
classList={{
|
||||
@@ -1620,6 +1622,9 @@ export default function Page() {
|
||||
"shadow-[var(--v2-elevation-raised)]": settings.general.newLayoutDesigns() && !!params.id,
|
||||
}}
|
||||
>
|
||||
<Show when={!isDesktop() && !!params.id && settings.general.newLayoutDesigns()}>
|
||||
{mobileTabs(true)}
|
||||
</Show>
|
||||
<div class="flex-1 min-h-0 overflow-hidden">
|
||||
<Switch>
|
||||
<Match when={params.id && mobileChanges()}>
|
||||
@@ -1627,8 +1632,8 @@ export default function Page() {
|
||||
{reviewContent({
|
||||
diffStyle: "unified",
|
||||
classes: {
|
||||
root: "pb-8",
|
||||
header: "px-4",
|
||||
root: "pb-8 [&_[data-slot=session-review-list]]:pb-0",
|
||||
header: "px-4 !h-16 !pb-4",
|
||||
container: "px-4",
|
||||
},
|
||||
loadingClass: "px-4 py-4 text-text-weak",
|
||||
@@ -1684,7 +1689,7 @@ export default function Page() {
|
||||
</Switch>
|
||||
</div>
|
||||
|
||||
<Show when={params.id || !newSessionDesign()}>{composerRegion("dock")}</Show>
|
||||
<Show when={(params.id || !newSessionDesign()) && !mobileChanges()}>{composerRegion("dock")}</Show>
|
||||
</div>
|
||||
|
||||
<Show when={desktopReviewOpen()}>
|
||||
|
||||
@@ -1296,7 +1296,9 @@ export function MessageTimeline(props: {
|
||||
"sticky top-0 z-30 bg-[linear-gradient(to_bottom,var(--background-stronger)_48px,transparent)]": true,
|
||||
"w-full": true,
|
||||
"pb-4": true,
|
||||
"pl-2 pr-3 md:pl-4 md:pr-3": true,
|
||||
"pr-3": true,
|
||||
"pl-4": settings.general.newLayoutDesigns(),
|
||||
"pl-2 md:pl-4": !settings.general.newLayoutDesigns(),
|
||||
"md:max-w-200 md:mx-auto 2xl:max-w-[1000px]": props.centered,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -386,7 +386,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
||||
>
|
||||
<div data-slot="session-review-container" class={props.classes?.container}>
|
||||
<Show when={hasDiffs()} fallback={props.empty}>
|
||||
<div class="pb-6">
|
||||
<div data-slot="session-review-list" class="pb-6">
|
||||
<Accordion multiple value={open()} onChange={handleChange}>
|
||||
<For each={files()}>
|
||||
{(file) => {
|
||||
|
||||
Reference in New Issue
Block a user