diff --git a/src/web-ui/src/app/components/SceneTopBar/SceneTopBar.scss b/src/web-ui/src/app/components/SceneTopBar/SceneTopBar.scss index 338d6d80a3..e1dd669c6a 100644 --- a/src/web-ui/src/app/components/SceneTopBar/SceneTopBar.scss +++ b/src/web-ui/src/app/components/SceneTopBar/SceneTopBar.scss @@ -13,6 +13,17 @@ container-type: inline-size; user-select: none; + &:has(.window-controls--windows) { + // Extend through the scene surface's right gutter so Close reaches the edge. + width: calc(100% + var(--openbitfun-space-4)); + padding-block: 0; + padding-inline-end: 0; + + > [data-openbitfun-part='trailing'] { + align-self: stretch; + } + } + &[data-bordered='true'] { &::before, &::after { diff --git a/src/web-ui/src/app/components/WindowControls/WindowControls.scss b/src/web-ui/src/app/components/WindowControls/WindowControls.scss index ac193e59aa..6b50e2aa15 100644 --- a/src/web-ui/src/app/components/WindowControls/WindowControls.scss +++ b/src/web-ui/src/app/components/WindowControls/WindowControls.scss @@ -135,6 +135,38 @@ } } +// Windows caption buttons keep their full hit area even in a narrow window. +.window-controls--windows { + align-items: stretch; + gap: 0; + height: 100%; + + .window-controls__btn { + flex: 0 0 46px; + width: 46px; + height: 100%; + border: 0; + border-radius: 0; + color: var(--openbitfun-color-content-primary); + cursor: default; + + svg { + width: 12px; + height: 12px; + transition: none; + } + + &:focus-visible { + outline-offset: -2px; + } + + &--close:hover:not(:disabled) { + background: var(--openbitfun-color-status-danger-content); + color: var(--openbitfun-color-content-on-dark); + } + } +} + @media (prefers-reduced-motion: reduce) { .window-controls__btn { transition: none; diff --git a/src/web-ui/src/app/components/WindowControls/WindowControls.tsx b/src/web-ui/src/app/components/WindowControls/WindowControls.tsx index e74d8fb37f..d564e39a41 100644 --- a/src/web-ui/src/app/components/WindowControls/WindowControls.tsx +++ b/src/web-ui/src/app/components/WindowControls/WindowControls.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Tooltip } from '@openbitfun/ui'; import { useTranslation } from 'react-i18next'; +import { isWindowsDesktopRuntime } from '@/infrastructure/runtime'; import './WindowControls.scss'; export interface WindowControlsProps @@ -38,6 +39,12 @@ const CloseGlyph = () => ( ); +const WindowsGlyph = ({ d }: { d: string }) => ( + +); + /** Desktop-shell window commands. This is product chrome, not a public UI primitive. */ export const WindowControls: React.FC = ({ onMinimize, @@ -49,6 +56,7 @@ export const WindowControls: React.FC = ({ ...props }) => { const { t } = useTranslation('common'); + const isWindows = isWindowsDesktopRuntime(); const maximizeLabel = maximized ? t('window.restore') : t('window.maximize'); const run = (event: React.MouseEvent, command: () => void) => { @@ -60,7 +68,7 @@ export const WindowControls: React.FC = ({ return (
= ({ disabled={disabled} aria-label={t('window.minimize')} > - + {isWindows ? : } @@ -85,7 +93,11 @@ export const WindowControls: React.FC = ({ disabled={disabled} aria-label={maximizeLabel} > - {maximized ? : } + {isWindows ? ( + + ) : maximized ? : } @@ -97,7 +109,7 @@ export const WindowControls: React.FC = ({ disabled={disabled} aria-label={t('window.close')} > - + {isWindows ? : }