Bladeren bron

feat(desktop): prevent overlapping composer borders (#37490)

usrnk1 3 weken geleden
bovenliggende
commit
cc34084dfe

+ 2 - 0
packages/app/src/components/prompt-input-v2.tsx

@@ -36,6 +36,7 @@ import {
 export type PromptInputV2ComposerProps = {
   class?: string
   controller: PromptInputV2ComposerController
+  borderUnderlay?: boolean
   edit?: PromptInputProps["edit"]
   onEditLoaded?: PromptInputProps["onEditLoaded"]
 }
@@ -57,6 +58,7 @@ export function PromptInputV2Composer(props: PromptInputV2ComposerProps) {
     <div class="flex flex-col gap-3">
       <PromptInputV2
         controller={props.controller}
+        borderUnderlay={props.borderUnderlay}
         class={props.class}
         modelControl={
           <PromptInputV2ModelControl

+ 1 - 1
packages/app/src/components/prompt-input.tsx

@@ -1460,10 +1460,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
         t={(key) => language.t(key as Parameters<typeof language.t>[0])}
       />
       <DockShellForm
+        data-dock-border-underlay="legacy"
         onSubmit={handleSubmit}
         classList={{
           "group/prompt-input": true,
-          "focus-within:shadow-xs-border": true,
           "border-icon-info-active border-dashed": store.draggingType !== null,
           [props.class ?? ""]: !!props.class,
         }}

+ 1 - 0
packages/app/src/pages/session.tsx

@@ -2265,6 +2265,7 @@ export default function Page() {
                     return (
                       <PromptInputV2Composer
                         controller={controller}
+                        borderUnderlay
                         edit={editingFollowup()}
                         onEditLoaded={clearFollowupEdit}
                       />

+ 9 - 3
packages/app/src/pages/session/composer/session-revert-dock.stories.tsx

@@ -1,5 +1,6 @@
 import { For } from "solid-js"
 import { createStore } from "solid-js/store"
+import { DockShell } from "@opencode-ai/ui/dock-surface"
 import { SessionRevertDock } from "@/pages/session/composer/session-revert-dock"
 import { SettingsProvider, useSettings } from "@/context/settings"
 
@@ -78,12 +79,17 @@ function Stage(props: { count: number }) {
       {/* Reproduce the real composer stack: dock + card overlapping the dock's bottom by lift() = 18px */}
       <div style={{ display: "flex", "flex-direction": "column" }}>
         <SessionRevertDock items={store.items} onRestore={restore} />
-        <div
+        <DockShell
+          data-dock-border-underlay={v2() ? "v2" : "legacy"}
           style={{ position: "relative", "z-index": 70, "margin-top": "-18px" }}
-          class="min-h-24 w-full rounded-[12px] border border-v2-border-border-base bg-v2-background-bg-base px-4 py-3 text-[13px] text-v2-text-text-faint"
+          classList={{
+            "min-h-24 w-full rounded-[12px] px-4 py-3 text-[13px]": true,
+            "bg-v2-background-bg-base text-v2-text-text-faint": v2(),
+            "text-text-weak": !v2(),
+          }}
         >
           Ask anything...
-        </div>
+        </DockShell>
       </div>
 
       <div class="text-[12px] text-v2-text-text-faint">

+ 26 - 5
packages/app/src/pages/session/composer/session-todo-dock.tsx

@@ -8,8 +8,10 @@ import { TextReveal } from "@opencode-ai/ui/text-reveal"
 import { TextStrikethrough } from "@opencode-ai/ui/text-strikethrough"
 import { createResizeObserver } from "@solid-primitives/resize-observer"
 import { Index, createEffect, createMemo } from "solid-js"
+import { Dynamic } from "solid-js/web"
 import { createStore } from "solid-js/store"
 import { useLanguage } from "@/context/language"
+import { useSettings } from "@/context/settings"
 
 const doneToken = "\u0000done\u0000"
 const totalToken = "\u0000total\u0000"
@@ -48,6 +50,7 @@ export function SessionTodoDock(props: {
   dockProgress: number
 }) {
   const language = useLanguage()
+  const settings = useSettings()
   const [store, setStore] = createStore({
     height: 78,
   })
@@ -91,8 +94,13 @@ export function SessionTodoDock(props: {
   })
 
   return (
-    <DockTray
+    <Dynamic
+      component={settings.general.newLayoutDesigns() ? "div" : DockTray}
       data-component="session-todo-dock"
+      classList={{
+        "w-full overflow-hidden rounded-xl border-[0.5px] border-v2-border-border-base bg-v2-background-bg-layer-01":
+          settings.general.newLayoutDesigns(),
+      }}
       style={{
         "overflow-x": "visible",
         "overflow-y": "hidden",
@@ -102,7 +110,11 @@ export function SessionTodoDock(props: {
       <div ref={contentRef}>
         <div
           data-action="session-todo-toggle"
-          class="pl-3 pr-2 py-2 flex items-center gap-2 overflow-visible"
+          classList={{
+            "flex items-center gap-2 overflow-visible": true,
+            "h-[42px] pl-4 pr-2": settings.general.newLayoutDesigns(),
+            "pl-3 pr-2 py-2": !settings.general.newLayoutDesigns(),
+          }}
           role="button"
           tabIndex={0}
           onClick={props.onToggle}
@@ -113,7 +125,12 @@ export function SessionTodoDock(props: {
           }}
         >
           <span
-            class="text-14-regular text-text-strong cursor-default inline-flex items-baseline shrink-0 overflow-visible"
+            classList={{
+              "cursor-default inline-flex items-baseline shrink-0 overflow-visible": true,
+              "font-[440] text-[13px] leading-5 tracking-[-0.04px] text-v2-text-text-muted":
+                settings.general.newLayoutDesigns(),
+              "text-14-regular text-text-strong": !settings.general.newLayoutDesigns(),
+            }}
             aria-label={label()}
             style={{
               "--tool-motion-odometer-ms": "600ms",
@@ -145,7 +162,11 @@ export function SessionTodoDock(props: {
             }}
           >
             <TextReveal
-              class="text-14-regular text-text-base cursor-default"
+              class={
+                settings.general.newLayoutDesigns()
+                  ? "cursor-default text-[13px] font-[440] leading-5 tracking-[-0.04px] text-v2-text-text-faint"
+                  : "text-14-regular text-text-base cursor-default"
+              }
               text={props.collapsed ? preview() : undefined}
               duration={600}
               travel={25}
@@ -191,7 +212,7 @@ export function SessionTodoDock(props: {
           <TodoList todos={props.todos} />
         </div>
       </div>
-    </DockTray>
+    </Dynamic>
   )
 }
 

+ 7 - 2
packages/session-ui/src/v2/components/prompt-input/index.tsx

@@ -36,6 +36,7 @@ export type PromptInputV2Props = {
   controller: PromptInputV2Interaction
   disabled?: boolean
   readOnly?: boolean
+  borderUnderlay?: boolean
   class?: string
   modelControl?: JSX.Element
 }
@@ -102,8 +103,12 @@ export function PromptInputV2(props: PromptInputV2Props) {
       </Show>
       <form
         data-component="prompt-input-v2"
-        class="group/prompt-input relative min-h-[96px] w-full rounded-xl bg-v2-background-bg-base shadow-[var(--v2-elevation-raised)]"
-        classList={{ "border border-v2-icon-icon-info border-dashed": state.drag === "active" }}
+        data-dock-border-underlay={props.borderUnderlay ? "v2" : undefined}
+        class="group/prompt-input relative min-h-[96px] w-full rounded-xl bg-v2-background-bg-base"
+        classList={{
+          "shadow-[var(--v2-elevation-raised)]": !props.borderUnderlay,
+          "border border-v2-icon-icon-info border-dashed": state.drag === "active",
+        }}
         onSubmit={(event) => {
           event.preventDefault()
           if (!props.disabled) props.controller.submit()

+ 14 - 0
packages/ui/src/components/dock-surface.css

@@ -7,6 +7,20 @@
   overflow: clip;
 }
 
+[data-dock-border-underlay] {
+  /* Later shadows paint underneath, so this solid ring masks overlapping surfaces without changing border geometry. */
+  box-shadow:
+    var(--dock-shell-visual-shadow, var(--shadow-xs-border)),
+    0 0 0 var(--dock-shell-border-underlay-width, 1px)
+      var(--dock-shell-border-underlay-background, var(--surface-raised-stronger-non-alpha));
+}
+
+[data-dock-border-underlay="v2"] {
+  --dock-shell-visual-shadow: var(--v2-elevation-raised);
+  --dock-shell-border-underlay-width: 0.5px;
+  --dock-shell-border-underlay-background: var(--v2-background-bg-base);
+}
+
 [data-dock-surface="tray"] {
   background-color: var(--background-base);
   border: 1px solid var(--border-weak-base);