diff --git a/packages/app/src/app.tsx b/packages/app/src/app.tsx index c4eadbd5f..9554a6363 100644 --- a/packages/app/src/app.tsx +++ b/packages/app/src/app.tsx @@ -14,6 +14,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/solid-query" import { Effect } from "effect" import { type Component, + createEffect, createMemo, createResource, createSignal, @@ -40,7 +41,7 @@ import { NotificationProvider } from "@/context/notification" import { PermissionProvider } from "@/context/permission" import { PromptProvider } from "@/context/prompt" import { ServerConnection, ServerProvider, serverName, useServer } from "@/context/server" -import { SettingsProvider } from "@/context/settings" +import { SettingsProvider, useSettings } from "@/context/settings" import { TerminalProvider } from "@/context/terminal" import DirectoryLayout from "@/pages/directory-layout" import Layout from "@/pages/layout" @@ -48,11 +49,6 @@ import { ErrorPage } from "./pages/error" import { useCheckServerHealth } from "./utils/server-health" import { ServersProvider } from "./context/servers" -if (import.meta.env.VITE_OPENCODE_CHANNEL !== "prod") { - document.body.classList.remove("text-12-regular") - document.body.classList.add("font-(family-name:--font-family-text)", "text-[13px]", "font-[440]") -} - const HomeRoute = lazy(() => import("@/pages/home")) const Session = lazy(() => import("@/pages/session")) @@ -97,9 +93,26 @@ function QueryProvider(props: ParentProps) { return {props.children} } +function BodyDesignClass() { + const settings = useSettings() + + createEffect(() => { + if (typeof document === "undefined") return + + const enabled = settings.general.newLayoutDesigns() + document.body.classList.toggle("text-12-regular", !enabled) + document.body.classList.toggle("font-(family-name:--font-family-text)", enabled) + document.body.classList.toggle("text-[13px]", enabled) + document.body.classList.toggle("font-[440]", enabled) + }) + + return null +} + function AppShellProviders(props: ParentProps) { return ( + diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index e26217c90..84fe7495f 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -79,8 +79,6 @@ import { pathKey } from "@/utils/path-key" import { base64Encode } from "@opencode-ai/core/util/encode" import { displayName } from "@/pages/layout/helpers" -const USE_V2_INPUT = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod" - interface PromptInputProps { class?: string variant?: "dock" | "new-session" @@ -1456,7 +1454,7 @@ export const PromptInput: Component = (props) => { t={(key) => language.t(key as Parameters[0])} /> - +
= (props) => { onKeyDown={handleKeyDown} classList={{ "select-text": true, - "min-h-[52px] w-full px-4 pt-4 pb-2 focus:outline-none whitespace-pre-wrap leading-5 text-[13px] font-[440] text-v2-text-text-faint [font-family:Inter,var(--font-family-sans)]": true, + "min-h-[52px] w-full px-4 pt-4 pb-2 focus:outline-none whitespace-pre-wrap leading-5 text-[13px] font-[440] text-v2-text-text-base": true, "[&_[data-type=file]]:text-syntax-property": true, "[&_[data-type=agent]]:text-syntax-type": true, "font-mono!": store.mode === "shell", diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index f028f7cea..1c2cf47a7 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -45,8 +45,6 @@ const OPEN_APPS = [ "sublime-text", ] as const -const USE_V2_TITLEBAR = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod" - type OpenApp = (typeof OPEN_APPS)[number] type OS = "macos" | "windows" | "linux" | "unknown" @@ -157,11 +155,11 @@ export function SessionHeader() { }) const hotkey = createMemo(() => command.keybind("file.open")) const os = createMemo(() => detectOS(platform)) - const isDesktopV2 = platform.platform === "desktop" && USE_V2_TITLEBAR - const search = createMemo(() => (isDesktopV2 ? settings.general.showSearch() : true)) - const tree = createMemo(() => (isDesktopV2 ? settings.general.showFileTree() : true)) - const term = createMemo(() => (isDesktopV2 ? settings.general.showTerminal() : true)) - const status = createMemo(() => (isDesktopV2 ? settings.general.showStatus() : true)) + const isDesktopV2 = createMemo(() => platform.platform === "desktop" && settings.general.newLayoutDesigns()) + const search = createMemo(() => (isDesktopV2() ? settings.general.showSearch() : true)) + const tree = createMemo(() => (isDesktopV2() ? settings.general.showFileTree() : true)) + const term = createMemo(() => (isDesktopV2() ? settings.general.showTerminal() : true)) + const status = createMemo(() => (isDesktopV2() ? settings.general.showStatus() : true)) const [exists, setExists] = createStore>>({ finder: true, diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx index 243aa6c36..bd95370a6 100644 --- a/packages/app/src/components/settings-general.tsx +++ b/packages/app/src/components/settings-general.tsx @@ -399,6 +399,18 @@ export const SettingsGeneral: Component = () => { />
+ + +
+ settings.general.setNewLayoutDesigns(checked)} + /> +
+
) @@ -802,7 +814,7 @@ export const SettingsGeneral: Component = () => { - + diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx index 2d956584a..d7a265384 100644 --- a/packages/app/src/components/titlebar.tsx +++ b/packages/app/src/components/titlebar.tsx @@ -55,7 +55,6 @@ const legacyTitlebarHeight = 40 const v2TitlebarHeight = 44 const minTitlebarZoom = 0.25 const windowsControlsBaseWidth = 138 // 3 native Windows caption buttons at 46px each. -const USE_V2_TITLEBAR = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod" const makeSessionHref = (b64Dir: string, sessionId: string) => `/${b64Dir}/session/${sessionId}` @@ -75,6 +74,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) { const navigate = useNavigate() const location = useLocation() const params = useParams() + const useV2Titlebar = createMemo(() => settings.general.newLayoutDesigns()) const mac = createMemo(() => platform.platform === "desktop" && platform.os === "macos") const windows = createMemo(() => platform.platform === "desktop" && platform.os === "windows") @@ -85,7 +85,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) { const titlebarZoom = () => (windows() ? Math.max(zoom(), minTitlebarZoom) : zoom()) const counterZoom = () => (windows() && titlebarZoom() < 1 ? 1 / titlebarZoom() : 1) const minHeight = () => { - const height = USE_V2_TITLEBAR ? v2TitlebarHeight : legacyTitlebarHeight + const height = useV2Titlebar() ? v2TitlebarHeight : legacyTitlebarHeight if (mac()) return `${height / zoom()}px` if (windows()) return `${height / Math.min(titlebarZoom(), 1)}px` return undefined @@ -119,7 +119,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) { const canBack = createMemo(() => history.index > 0) const canForward = createMemo(() => history.index < history.stack.length - 1) const hasProjects = createMemo(() => layout.projects.list().length > 0) - const nav = createMemo(() => (USE_V2_TITLEBAR ? settings.general.showNavigation() : true)) + const nav = createMemo(() => (useV2Titlebar() ? settings.general.showNavigation() : true)) const updateState = createMemo(() => { const version = props.update?.version() return { @@ -222,8 +222,8 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
- + {(_) => { const serverSync = useServerSync() const navigate = useNavigate() diff --git a/packages/app/src/context/settings.tsx b/packages/app/src/context/settings.tsx index a4e01d53a..84288d31a 100644 --- a/packages/app/src/context/settings.tsx +++ b/packages/app/src/context/settings.tsx @@ -33,6 +33,7 @@ export interface Settings { editToolPartsExpanded: boolean showSessionProgressBar: boolean showCustomAgents: boolean + newLayoutDesigns?: boolean } updates: { startup: boolean @@ -54,6 +55,7 @@ export interface Settings { export const monoDefault = "System Mono" export const sansDefault = "System Sans" export const terminalDefault = "JetBrainsMono Nerd Font Mono" +export const newLayoutDesignsDefault = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod" const monoFallback = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace' @@ -242,6 +244,10 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont setShowCustomAgents(value: boolean) { setStore("general", "showCustomAgents", value) }, + newLayoutDesigns: withFallback(() => store.general?.newLayoutDesigns, newLayoutDesignsDefault), + setNewLayoutDesigns(value: boolean) { + setStore("general", "newLayoutDesigns", value) + }, }, updates: { startup: withFallback(() => store.updates?.startup, defaultSettings.updates.startup), diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index f58257b07..29f662f73 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -791,6 +791,8 @@ export const dict = { "settings.general.row.showSessionProgressBar.title": "Show session progress bar", "settings.general.row.showSessionProgressBar.description": "Display the animated progress bar at the top of the session when the agent is working", + "settings.general.row.newLayoutDesigns.title": "New layout and designs", + "settings.general.row.newLayoutDesigns.description": "Enable the redesigned layout, home, composer, and session UI", "settings.general.row.pinchZoom.title": "Pinch to zoom", "settings.general.row.pinchZoom.description": "Allow trackpad pinch and Ctrl-scroll gestures to zoom", diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index 2997efa56..96a44ffd8 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -31,8 +31,8 @@ import { messageAgentColor } from "@/utils/agent" import { sessionPermissionRequest } from "@/pages/session/composer/session-request-tree" import { ServerHealthIndicator } from "@/components/server/server-row" import { useServers } from "@/context/servers" +import { useSettings } from "@/context/settings" -const USE_HOME_DESIGN = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod" const HOME_SESSION_LIMIT = 15 const HOME_ROW = "flex min-w-0 w-full shrink-0 cursor-default items-center rounded-[6px] border-0 bg-transparent text-left text-v2-text-text-muted transition-colors duration-[120ms] ease-in-out hover:bg-v2-overlay-simple-overlay-hover focus-visible:bg-v2-overlay-simple-overlay-hover focus-visible:outline-none" @@ -52,8 +52,12 @@ type HomeSessionGroup = { } export default function Home() { - if (USE_HOME_DESIGN) return - return + const settings = useSettings() + return ( + }> + + + ) } function HomeDesign() { diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index a943b436e..70154a34d 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -89,8 +89,6 @@ import { import { ProjectDragOverlay, SortableProject, type ProjectSidebarContext } from "./layout/sidebar-project" import { SidebarContent } from "./layout/sidebar-shell" -const USE_NEW_DESIGN = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod" - export default function Layout(props: ParentProps) { const [store, setStore, , ready] = persisted( Persist.global("layout.page", ["layout.page.v1"]), @@ -129,6 +127,7 @@ export default function Layout(props: ParentProps) { const command = useCommand() const theme = useTheme() const language = useLanguage() + const newDesign = createMemo(() => settings.general.newLayoutDesigns()) const initialDirectory = decode64(params.dir) const location = useLocation() const route = createMemo(() => { @@ -154,7 +153,7 @@ export default function Layout(props: ParentProps) { const currentDir = createMemo(() => route().dir) const [state, setState] = createStore({ - autoselect: !initialDirectory && !USE_NEW_DESIGN, + autoselect: !initialDirectory && !newDesign(), busyWorkspaces: {} as Record, hoverProject: undefined as string | undefined, scrollSessionKey: undefined as string | undefined, @@ -1142,7 +1141,7 @@ export default function Layout(props: ParentProps) { }, ] - if (!USE_NEW_DESIGN) + if (!newDesign()) Array.from({ length: 9 }, (_, i) => { const index = i const number = index + 1 @@ -1821,7 +1820,7 @@ export default function Layout(props: ParentProps) { createEffect(() => { document.documentElement.style.setProperty( "--dialog-left-margin", - USE_NEW_DESIGN ? "0px" : `${layout.sidebar.opened() ? layout.sidebar.width() : 48}px`, + newDesign() ? "0px" : `${layout.sidebar.opened() ? layout.sidebar.width() : 48}px`, ) }) @@ -2363,179 +2362,180 @@ export default function Layout(props: ParentProps) { /> ) - if (USE_NEW_DESIGN) { - return ( -
+ return ( + + {autoselecting() ?? ""} + +
+ }> + {props.children} + +
+ {import.meta.env.DEV && } + +
+ } + > +
{autoselecting() ?? ""} -
- }> - {props.children} - -
- {import.meta.env.DEV && } - -
- ) - } - - return ( -
- {autoselecting() ?? ""} - - - - -
-
-
- - - - - - -