diff --git a/docs/design/web-shell/web-shell-specular-composer-animation.md b/docs/design/web-shell/web-shell-specular-composer-animation.md deleted file mode 100644 index 3317d729082..00000000000 --- a/docs/design/web-shell/web-shell-specular-composer-animation.md +++ /dev/null @@ -1,73 +0,0 @@ -# Web Shell Specular Composer Animation - -## Goal - -Replace the current composer-only DAC glow with the supplied specular edge -animation, and add the supplied DotField animation behind the empty new-session -view. Existing composer behavior, layout, controls, and keyboard handling remain -unchanged. - -## Scope - -- Render a WebGL edge highlight around the complete composer surface. -- Follow pointer direction while the composer is unfocused and within 250 CSS - pixels of the pointer. -- Continue from the current highlight angle and rotate at 0.85 radians per - second while the editor is focused. -- Keep the supplied light and dark animation colors and support live theme - changes. -- Render the DotField only while `isChatEmptyState` is true. -- Play the empty composer placeholder twice with a three-second pause, then - leave the complete placeholder visible. -- Preserve the current 12 pixel composer radius and use that radius for the - shader geometry. -- Preserve all existing CodeMirror, mobile textarea, toolbar, attachment, - submission, and disabled/running behavior. - -The demo's page layout, toolbar styling, send button styling, and fixed -20-pixel composer radius are not part of this change. - -## Structure - -`SpecularComposerEffect` owns the composer canvas and all pointer, focus, -resize, animation-frame, and WebGL resources. It is an inert sibling of the -composer content and never receives pointer events. - -`NewSessionDotField` owns the empty-state background canvas. `App` mounts it -only for the empty new-session state, so starting or loading a session removes -the canvas and releases its resources. - -The composer effect uses WebGL2 directly, while the DotField uses the browser's -2D canvas API, matching the handoff without adding a runtime dependency. If -WebGL2 is unavailable, the specular canvas remains absent and the existing CSS -surface continues to work. - -## Motion and accessibility - -The composer highlight uses the supplied 32 degree highlight and 68 degree -fade, a one-CSS-pixel edge, 1.0 proximity intensity, and 1.4 focused intensity. -Focus and blur both lock the current angle before changing modes, so the -focused rotation stays clockwise and never jumps or reverses during -hover/focus transitions. The hover/focus underlay expands four pixels around -the existing radius. - -The DotField uses 1-pixel dots on a 15-pixel grid, a 500-pixel pointer radius, -0.1 pointer force, a 67-pixel bulge, and a 160-pixel glow. The glow erases the -canvas dots so the actual host background shows through without color -interpolation. - -The typewriter stops as soon as the user interacts with the editor. If the -empty editor loses focus, it gets another two-run sequence. Empty placeholder -strings do not mount the effect. - -When `prefers-reduced-motion: reduce` is active, both animation loops remain -disabled. Changes to the system preference apply without reloading the page. -The composer retains its ordinary CSS border and focus affordance. - -## Lifecycle and verification - -Each effect cancels animation frames, disconnects its `ResizeObserver`, and -removes event listeners. The composer effect also deletes its WebGL resources -and loses its context. Tests cover empty-state mounting, non-empty removal, -composer effect presence, and WebGL-unavailable fallback. Existing composer -interaction tests guard against functional regressions. diff --git a/packages/web-shell/client/App.module.css b/packages/web-shell/client/App.module.css index 2ddf87fd185..5193e9a76a4 100644 --- a/packages/web-shell/client/App.module.css +++ b/packages/web-shell/client/App.module.css @@ -719,16 +719,10 @@ the wrapper must shrink to its content instead of filling the pane — the same thing `.content` already does in this state. */ .appChatEmpty .chatViewWrap { - position: relative; - z-index: 1; flex: 0 0 auto; overflow: visible; } -.appChatEmpty .chatPane { - position: relative; -} - .footer { position: relative; width: min(100%, var(--chat-shell-width)); @@ -934,18 +928,12 @@ overflow: hidden; } - /* Override chatViewWrap's relative positioning so the absolute footer - anchors to chatPane. The wrap remains a flex item, so the z-index: 1 it - gets from `.appChatEmpty .chatViewWrap` still applies under static - positioning and keeps the welcome content and composer above the dot - field. The welcome-middle + custom-footer variant keeps the positioned - wrap: `.footerWithCustomFooter` renders `display: contents` there, so - the wrap must remain the containing block for `.bottomPanels`. */ + /* The custom-footer variant renders the composer footer `display: contents`, + so this wrap stays the containing block for the absolutely-positioned + bottom panels (status panel) anchored above it. */ .appChatEmpty - .chatViewWithMobileComposerBottom:not( - .chatViewWithWelcomeMiddle.chatViewWithCustomFooter - ) { - position: static; + .chatViewWithMobileComposerBottom.chatViewWithWelcomeMiddle.chatViewWithCustomFooter { + position: relative; } .appChatEmpty .chatViewWithMobileComposerBottom .footer { diff --git a/packages/web-shell/client/App.test.tsx b/packages/web-shell/client/App.test.tsx index f6f8e9c2c9f..dfd70b9d875 100644 --- a/packages/web-shell/client/App.test.tsx +++ b/packages/web-shell/client/App.test.tsx @@ -693,12 +693,6 @@ vi.mock('./components/ChatEditor', async () => { }; }); -vi.mock('./components/NewSessionDotField', () => ({ - NewSessionDotField: () => ( -