mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-06 09:10:47 -04: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 { getFilename } from "@opencode-ai/core/util/path"
|
||||||
import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js"
|
import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
|
import { createMediaQuery } from "@solid-primitives/media"
|
||||||
import { Portal } from "solid-js/web"
|
import { Portal } from "solid-js/web"
|
||||||
import { useCommand } from "@/context/command"
|
import { useCommand } from "@/context/command"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
@@ -158,6 +159,7 @@ export function SessionHeader() {
|
|||||||
const isV2 = settings.general.newLayoutDesigns
|
const isV2 = settings.general.newLayoutDesigns
|
||||||
const search = settings.visibility.search
|
const search = settings.visibility.search
|
||||||
const status = settings.visibility.status
|
const status = settings.visibility.status
|
||||||
|
const isDesktop = createMediaQuery("(min-width: 768px)")
|
||||||
|
|
||||||
const [exists, setExists] = createStore<Partial<Record<OpenApp, boolean>>>({
|
const [exists, setExists] = createStore<Partial<Record<OpenApp, boolean>>>({
|
||||||
finder: true,
|
finder: true,
|
||||||
@@ -236,6 +238,7 @@ export function SessionHeader() {
|
|||||||
statusLabel: language.t("status.popover.trigger"),
|
statusLabel: language.t("status.popover.trigger"),
|
||||||
reviewLabel: language.t("command.review.toggle"),
|
reviewLabel: language.t("command.review.toggle"),
|
||||||
reviewKeybind: command.keybind("review.toggle"),
|
reviewKeybind: command.keybind("review.toggle"),
|
||||||
|
reviewVisible: isDesktop(),
|
||||||
reviewOpened: view().reviewPanel.opened(),
|
reviewOpened: view().reviewPanel.opened(),
|
||||||
onReviewToggle: () => view().reviewPanel.toggle(),
|
onReviewToggle: () => view().reviewPanel.toggle(),
|
||||||
}))
|
}))
|
||||||
@@ -518,6 +521,7 @@ type SessionHeaderV2ActionsState = {
|
|||||||
statusLabel: string
|
statusLabel: string
|
||||||
reviewLabel: string
|
reviewLabel: string
|
||||||
reviewKeybind: string
|
reviewKeybind: string
|
||||||
|
reviewVisible: boolean
|
||||||
reviewOpened: boolean
|
reviewOpened: boolean
|
||||||
onReviewToggle: () => void
|
onReviewToggle: () => void
|
||||||
}
|
}
|
||||||
@@ -530,20 +534,22 @@ function SessionHeaderV2Actions(props: { state: SessionHeaderV2ActionsState }) {
|
|||||||
<StatusPopoverV2 />
|
<StatusPopoverV2 />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Show>
|
</Show>
|
||||||
<TooltipKeybind title={props.state.reviewLabel} keybind={props.state.reviewKeybind}>
|
<Show when={props.state.reviewVisible}>
|
||||||
<IconButtonV2
|
<TooltipKeybind title={props.state.reviewLabel} keybind={props.state.reviewKeybind}>
|
||||||
type="button"
|
<IconButtonV2
|
||||||
variant="ghost-muted"
|
type="button"
|
||||||
size="large"
|
variant="ghost-muted"
|
||||||
class="!w-9 shrink-0"
|
size="large"
|
||||||
state={props.state.reviewOpened ? "pressed" : undefined}
|
class="!w-9 shrink-0"
|
||||||
onClick={props.state.onReviewToggle}
|
state={props.state.reviewOpened ? "pressed" : undefined}
|
||||||
aria-label={props.state.reviewLabel}
|
onClick={props.state.onReviewToggle}
|
||||||
aria-expanded={props.state.reviewOpened}
|
aria-label={props.state.reviewLabel}
|
||||||
aria-controls="review-panel"
|
aria-expanded={props.state.reviewOpened}
|
||||||
icon={<IconV2 name="sidebar-right" />}
|
aria-controls="review-panel"
|
||||||
/>
|
icon={<IconV2 name="sidebar-right" />}
|
||||||
</TooltipKeybind>
|
/>
|
||||||
|
</TooltipKeybind>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ export function NewHome() {
|
|||||||
|
|
||||||
return (
|
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="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
|
<HomeProjectColumn
|
||||||
projects={projects()}
|
projects={projects()}
|
||||||
selected={state.selection}
|
selected={state.selection}
|
||||||
@@ -355,7 +355,7 @@ export function NewHome() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<section
|
<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")}
|
aria-label={language.t("sidebar.project.recentSessions")}
|
||||||
>
|
>
|
||||||
<HomeSessionSearch
|
<HomeSessionSearch
|
||||||
@@ -419,6 +419,12 @@ export function NewHome() {
|
|||||||
</div>
|
</div>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</section>
|
</section>
|
||||||
|
<HomeUtilityNav
|
||||||
|
class="flex lg:hidden"
|
||||||
|
openSettings={openSettings}
|
||||||
|
openHelp={() => platform.openLink("https://opencode.ai/desktop-feedback")}
|
||||||
|
language={language}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -443,7 +449,7 @@ function HomeProjectColumn(props: {
|
|||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
const controller = useServerManagementController({ navigateOnAdd: false })
|
const controller = useServerManagementController({ navigateOnAdd: false })
|
||||||
return (
|
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="flex h-7 min-w-0 items-center justify-between pl-1.5">
|
||||||
<div class={HOME_SECTION_LABEL}>{props.language.t("home.projects")}</div>
|
<div class={HOME_SECTION_LABEL}>{props.language.t("home.projects")}</div>
|
||||||
<Show when={global.servers.list().length === 1}>
|
<Show when={global.servers.list().length === 1}>
|
||||||
@@ -489,28 +495,44 @@ function HomeProjectColumn(props: {
|
|||||||
}}
|
}}
|
||||||
</For>
|
</For>
|
||||||
</Show>
|
</Show>
|
||||||
<div class="mt-4 flex min-w-0 flex-col gap-1">
|
<HomeUtilityNav
|
||||||
<button
|
class="mt-4 hidden lg:flex"
|
||||||
type="button"
|
openSettings={props.openSettings}
|
||||||
class={`${HOME_PROJECT_NAV_ROW} text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted`}
|
openHelp={props.openHelp}
|
||||||
onClick={props.openSettings}
|
language={props.language}
|
||||||
>
|
/>
|
||||||
<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>
|
|
||||||
</aside>
|
</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: {
|
function HomeServerRow(props: {
|
||||||
server: ServerConnection.Any
|
server: ServerConnection.Any
|
||||||
selected: boolean
|
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 (
|
return (
|
||||||
<div class="relative size-full overflow-hidden flex flex-col">
|
<div class="relative size-full overflow-hidden flex flex-col">
|
||||||
{sessionSync() ?? ""}
|
{sessionSync() ?? ""}
|
||||||
<SessionHeader />
|
<SessionHeader />
|
||||||
<div
|
<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={{
|
classList={{
|
||||||
"gap-2 p-2": settings.general.newLayoutDesigns(),
|
"gap-2 p-2": settings.general.newLayoutDesigns(),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Show when={!isDesktop() && !!params.id}>
|
<Show when={!isDesktop() && !!params.id && !settings.general.newLayoutDesigns()}>{mobileTabs()}</Show>
|
||||||
<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>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
classList={{
|
classList={{
|
||||||
@@ -1620,6 +1622,9 @@ export default function Page() {
|
|||||||
"shadow-[var(--v2-elevation-raised)]": settings.general.newLayoutDesigns() && !!params.id,
|
"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">
|
<div class="flex-1 min-h-0 overflow-hidden">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={params.id && mobileChanges()}>
|
<Match when={params.id && mobileChanges()}>
|
||||||
@@ -1627,8 +1632,8 @@ export default function Page() {
|
|||||||
{reviewContent({
|
{reviewContent({
|
||||||
diffStyle: "unified",
|
diffStyle: "unified",
|
||||||
classes: {
|
classes: {
|
||||||
root: "pb-8",
|
root: "pb-8 [&_[data-slot=session-review-list]]:pb-0",
|
||||||
header: "px-4",
|
header: "px-4 !h-16 !pb-4",
|
||||||
container: "px-4",
|
container: "px-4",
|
||||||
},
|
},
|
||||||
loadingClass: "px-4 py-4 text-text-weak",
|
loadingClass: "px-4 py-4 text-text-weak",
|
||||||
@@ -1684,7 +1689,7 @@ export default function Page() {
|
|||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Show when={params.id || !newSessionDesign()}>{composerRegion("dock")}</Show>
|
<Show when={(params.id || !newSessionDesign()) && !mobileChanges()}>{composerRegion("dock")}</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Show when={desktopReviewOpen()}>
|
<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,
|
"sticky top-0 z-30 bg-[linear-gradient(to_bottom,var(--background-stronger)_48px,transparent)]": true,
|
||||||
"w-full": true,
|
"w-full": true,
|
||||||
"pb-4": 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,
|
"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}>
|
<div data-slot="session-review-container" class={props.classes?.container}>
|
||||||
<Show when={hasDiffs()} fallback={props.empty}>
|
<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}>
|
<Accordion multiple value={open()} onChange={handleChange}>
|
||||||
<For each={files()}>
|
<For each={files()}>
|
||||||
{(file) => {
|
{(file) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user