refactor: remove macOS vibrancy support and unify glass surfaces

Drops the macOS vibrancy toggle, IPC, and related settings copy
Updates dialogs, popovers, tooltips, and dropdowns to use shared glass styles
Adds sticky header fade behavior to model picker and sidebar lists
This commit is contained in:
Bohdan Triapitsyn
2026-08-10 15:10:37 +03:00
parent 90a16d1d44
commit 2c30af5807
36 changed files with 273 additions and 302 deletions
@@ -25,6 +25,8 @@ It is **not** a context-panel surface. It is not registered in
`lib/surfaces/registry.ts`, has no rail icon, no tab, no persisted width and no
resizer. It is a card floating inside the chat column — rounded border, faint
fill, its own margin — rather than a docked pane flush against the window edge.
When it overlays the transcript, it uses the shared `oc-glass-panel` surface;
the inline card keeps its lighter, non-blurred fill instead.
## Placement
@@ -178,13 +178,14 @@ export const WorkStatusPanel: React.FC<Props> = ({ sessionId, directory, visible
// Beside the transcript the translucent fill reads as depth; on top
// of it, message bubbles showed straight through the rows. Frosting
// separates the two without going fully opaque.
'bg-[var(--surface-muted)]/80 backdrop-blur-md',
'oc-glass-panel',
],
// An empty card is a border around a settings icon, which reads as a
// fault rather than as "nothing to report".
renderedSections === 0 && 'border-transparent bg-transparent shadow-none',
'motion-reduce:transition-none',
'rounded-xl border border-[var(--interactive-border)] bg-[var(--surface-muted)]/40',
'rounded-xl border border-[var(--interactive-border)]',
!overlay && 'bg-[var(--surface-muted)]/40',
// A lighter version of the composer's lift: the same shape, but this
// card is taller, so the composer's spread reads as heavy here.
'shadow-[0_2px_8px_-3px_rgb(0_0_0_/_0.08)]',
+1 -1
View File
@@ -336,7 +336,7 @@ const DesktopServicesMenu = React.memo(function DesktopServicesMenu({
</Tooltip>
<DropdownMenuContent
align="end"
className="w-[min(27rem,calc(100vw-2rem))] max-h-[75vh] overflow-y-auto bg-[var(--surface-elevated)] p-0"
className="w-[min(27rem,calc(100vw-2rem))] max-h-[75vh] overflow-y-auto p-0"
>
{isDesktopApp ? (
<div>
@@ -127,7 +127,7 @@ export const Sidebar: React.FC<SidebarProps> = ({ isOpen, isMobile, children, cl
ref={sidebarRef}
className={cn(
'relative flex h-full overflow-hidden border-r border-border will-change-[width] motion-reduce:transition-none',
'bg-sidebar oc-vibrancy-surface',
'bg-sidebar',
!isOpen && 'border-r-0',
className,
)}
@@ -28,7 +28,6 @@ const ICON_BUTTON_CLASS =
export const TitlebarLeftControls: React.FC = () => {
const { t } = useI18n();
const toggleSidebar = useUIStore((state) => state.toggleSidebar);
const isSidebarOpen = useUIStore((state) => state.isSidebarOpen);
const shortcutOverrides = useUIStore((state) => state.shortcutOverrides);
const projectActionsContext = useProjectActionsContext();
const clusterRef = React.useRef<HTMLDivElement | null>(null);
@@ -129,10 +128,6 @@ export const TitlebarLeftControls: React.FC = () => {
<ProjectActionsButton
projectRef={projectActionsContext.projectRef}
directory={projectActionsContext.directory}
// While the sidebar is open the controls sit over the frosted
// sidebar — let the pill share its translucency instead of painting
// an opaque surface (handled under [data-oc-vibrancy] in CSS).
className={isSidebarOpen ? 'oc-vibrancy-pill' : undefined}
/>
) : null}
</div>
@@ -288,6 +288,9 @@ const SortableProviderSection: React.FC<{
};
const STICKY_HEADER_OFFSET = 32;
const STICKY_FADE_MAX_SIZE = 48;
const STICKY_FADE_MIN_SIZE = 32;
const STICKY_FADE_CLEAR_MAX_SIZE = 24;
const scrollIntoView = (container: HTMLElement | null, node: HTMLElement | null) => {
if (!node) return;
@@ -417,6 +420,9 @@ export const ModelPickerList: React.FC<ModelPickerListProps> = ({
const selectionStore = selectionStoreRef.current;
const itemRefs = React.useRef<(HTMLDivElement | null)[]>([]);
const scrollRef = React.useRef<HTMLElement | null>(null);
const sectionHeaderSentinelRefs = React.useRef<Map<string, HTMLDivElement | null>>(new Map());
const stickyFadeSizeRef = React.useRef(0);
const [stuckSectionHeaders, setStuckSectionHeaders] = React.useState<Set<string>>(new Set());
const keyboardOwnsSelectionRef = React.useRef(false);
const lastMousePositionRef = React.useRef<{ x: number; y: number } | null>(null);
const collapsedRecord = useModelPickerSectionsStore((state) => state.collapsedSections);
@@ -495,6 +501,70 @@ export const ModelPickerList: React.FC<ModelPickerListProps> = ({
})
.filter((provider) => provider.models.length > 0), [allowedProviderSet, isHidden, isModelAllowed, matchesQuery, orderedProviders]);
const visibleSectionKeys = React.useMemo(() => [
...(filteredFavorites.length > 0 ? ['favorites'] : []),
...(filteredRecents.length > 0 ? ['recent'] : []),
...filteredProviders.map((provider) => `provider:${provider.id}`),
], [filteredFavorites.length, filteredProviders, filteredRecents.length]);
React.useEffect(() => {
if (!stickyHeaders || !scrollRef.current) {
setStuckSectionHeaders((previous) => previous.size === 0 ? previous : new Set());
return;
}
const root = scrollRef.current;
const observer = new IntersectionObserver((entries) => {
setStuckSectionHeaders((previous) => {
const next = new Set(previous);
let changed = false;
for (const entry of entries) {
const sectionKey = (entry.target as HTMLElement).dataset.modelSectionKey;
if (!sectionKey) continue;
const rootTop = entry.rootBounds?.top ?? root.getBoundingClientRect().top;
const isAboveScroller = !entry.isIntersecting && entry.boundingClientRect.top < rootTop;
if (next.has(sectionKey) === isAboveScroller) continue;
changed = true;
if (isAboveScroller) next.add(sectionKey);
else next.delete(sectionKey);
}
return changed ? next : previous;
});
}, { root, threshold: 0 });
sectionHeaderSentinelRefs.current.forEach((element) => {
if (element) observer.observe(element);
});
return () => observer.disconnect();
}, [stickyHeaders, visibleSectionKeys]);
const syncStickyFade = React.useCallback((scroller: HTMLElement) => {
const hasTopScroll = scroller.scrollTop > 1;
const fadeSize = hasTopScroll
? Math.min(STICKY_FADE_MIN_SIZE + scroller.scrollTop, STICKY_FADE_MAX_SIZE)
: 0;
stickyFadeSizeRef.current = fadeSize;
scroller.style.setProperty('--scroll-shadow-top-size', `${fadeSize}px`);
scroller.style.setProperty(
'--scroll-shadow-top-clear-size',
`${Math.min(Math.max(fadeSize - 8, 0), STICKY_FADE_CLEAR_MAX_SIZE)}px`,
);
}, []);
const blockStickyFadeInteraction = React.useCallback((
event: React.MouseEvent<HTMLDivElement> | React.PointerEvent<HTMLDivElement>,
) => {
if ((event.target as Element).closest('[data-overlay-scrollbar-thumb], [data-model-picker-sticky-header]')) return;
const eventY = event.clientY - event.currentTarget.getBoundingClientRect().top;
if (eventY >= stickyFadeSizeRef.current) return;
event.preventDefault();
event.stopPropagation();
}, []);
React.useLayoutEffect(() => {
if (stickyHeaders && scrollRef.current) syncStickyFade(scrollRef.current);
}, [stickyHeaders, syncStickyFade, visibleSectionKeys]);
const flatModelList = React.useMemo(() => {
const items: ModelPickerEntry[] = [];
if (!collapsedSections.has('favorites')) filteredFavorites.forEach((entry) => items.push(entry));
@@ -571,7 +641,7 @@ export const ModelPickerList: React.FC<ModelPickerListProps> = ({
const headerClassName = cn(
'typography-micro font-semibold text-muted-foreground uppercase tracking-wider flex items-center gap-2 px-2 py-1.5',
stickyHeaders && 'sticky top-0 z-10 [background:linear-gradient(var(--surface-elevated),var(--surface-elevated)),linear-gradient(var(--surface-background),var(--surface-background))]',
stickyHeaders && 'sticky top-0 z-20',
sectionHeaderClassName,
);
@@ -678,6 +748,15 @@ export const ModelPickerList: React.FC<ModelPickerListProps> = ({
const isSectionCollapsed = (key: string) => collapsedSections.has(key);
const toggleSectionCollapsed = (key: string) => toggleSection(key);
const renderSectionSentinel = (key: string) => stickyHeaders ? (
<div
ref={(element) => { sectionHeaderSentinelRefs.current.set(key, element); }}
data-model-section-key={key}
className="pointer-events-none absolute top-0 h-px w-full"
aria-hidden="true"
/>
) : null;
const renderSectionHeader = (key: string, icon: React.ReactNode, label: React.ReactNode, headerDragProps?: SortableFavoriteHandleProps) => {
const collapsed = isSectionCollapsed(key);
const toggleKeyDown = (event: React.KeyboardEvent) => {
@@ -703,6 +782,7 @@ export const ModelPickerList: React.FC<ModelPickerListProps> = ({
tabIndex={0}
aria-expanded={!collapsed}
title={reorderProviderTitle}
data-model-picker-sticky-header={stickyHeaders ? 'true' : undefined}
className={cn(headerClassName, 'w-full text-left cursor-grab select-none active:cursor-grabbing')}
onClick={() => toggleSectionCollapsed(key)}
onKeyDown={toggleKeyDown}
@@ -720,6 +800,7 @@ export const ModelPickerList: React.FC<ModelPickerListProps> = ({
return (
<button
type="button"
data-model-picker-sticky-header={stickyHeaders ? 'true' : undefined}
className={cn(headerClassName, 'w-full text-left cursor-pointer')}
onClick={() => toggleSectionCollapsed(key)}
aria-expanded={!collapsed}
@@ -737,15 +818,43 @@ export const ModelPickerList: React.FC<ModelPickerListProps> = ({
provider: (typeof filteredProviders)[number],
providerIndex: number,
headerDragProps?: SortableFavoriteHandleProps,
) => (
<>
{providerIndex > 0 ? <div className="h-px bg-border/40 my-1" /> : null}
{renderSectionHeader(`provider:${provider.id}`, <ProviderLogo providerId={provider.id} className="h-4 w-4 flex-shrink-0" />, provider.name || provider.id, headerDragProps)}
{!isSectionCollapsed(`provider:${provider.id}`)
? provider.models.map((model) => renderRow({ model, providerID: provider.id, modelID: model.id as string }, 'provider', false, currentFlatIndex++))
: null}
</>
);
) => {
const sectionKey = `provider:${provider.id}`;
return (
<>
{providerIndex > 0 ? <div className="h-px bg-border/40 my-1" /> : null}
<div className="relative">
{renderSectionSentinel(sectionKey)}
{renderSectionHeader(sectionKey, <ProviderLogo providerId={provider.id} className="h-4 w-4 flex-shrink-0" />, provider.name || provider.id, headerDragProps)}
{!isSectionCollapsed(sectionKey)
? provider.models.map((model) => renderRow({ model, providerID: provider.id, modelID: model.id as string }, 'provider', false, currentFlatIndex++))
: null}
</div>
</>
);
};
let stuckSectionKey: string | null = null;
for (const sectionKey of visibleSectionKeys) {
if (stuckSectionHeaders.has(sectionKey)) stuckSectionKey = sectionKey;
}
// The sidebar can seed its overlay with the first section because its first
// header starts flush with the scroller. `Not selected` may precede the
// first model section here, so wait for that section's sentinel rather than
// showing its identity while the leading action is still visible.
const leadingSectionKey = stuckSectionKey ?? (!includeNotSelected ? visibleSectionKeys[0] ?? null : null);
const renderSectionIdentity = (sectionKey: string): React.ReactNode => {
if (sectionKey === 'favorites') {
return <><Icon name="star-fill" className="h-4 w-4 flex-shrink-0 text-primary" /><span className="min-w-0 truncate">{labels.favorites}</span></>;
}
if (sectionKey === 'recent') {
return <><Icon name="time" className="h-4 w-4 flex-shrink-0" /><span className="min-w-0 truncate">{labels.recent}</span></>;
}
const providerId = sectionKey.startsWith('provider:') ? sectionKey.slice('provider:'.length) : '';
const provider = providerById.get(providerId);
if (!provider) return null;
return <><ProviderLogo providerId={providerId} className="h-4 w-4 flex-shrink-0" /><span className="min-w-0 truncate">{provider.name || provider.id}</span></>;
};
return (
<>
@@ -764,7 +873,25 @@ export const ModelPickerList: React.FC<ModelPickerListProps> = ({
</div>
</div>
<ScrollableOverlay ref={scrollRef} outerClassName={maxHeightClassName} className="overlay-scrollbar-target--no-gutter" style={maxHeightStyle}>
<div
className="oc-sticky-fade-root relative flex min-h-0 flex-1"
onPointerDownCapture={stickyHeaders ? blockStickyFadeInteraction : undefined}
onClickCapture={stickyHeaders ? blockStickyFadeInteraction : undefined}
onContextMenuCapture={stickyHeaders ? blockStickyFadeInteraction : undefined}
>
<ScrollableOverlay
ref={scrollRef}
useScrollShadow={stickyHeaders}
hideTopScrollShadow={!stickyHeaders}
scrollShadowSize={96}
outerClassName={maxHeightClassName}
className="oc-sticky-fade-scroller overlay-scrollbar-target--no-gutter"
style={{
...(stickyHeaders ? { '--scroll-shadow-top-size': '0px' } as React.CSSProperties : {}),
...maxHeightStyle,
}}
onScroll={stickyHeaders ? (event) => syncStickyFade(event.currentTarget) : undefined}
>
<div className="px-1">
{includeNotSelected ? (
<>
@@ -786,7 +913,8 @@ export const ModelPickerList: React.FC<ModelPickerListProps> = ({
) : null}
{filteredFavorites.length > 0 ? (
<div>
<div className="relative">
{renderSectionSentinel('favorites')}
{renderSectionHeader('favorites', <Icon name="star-fill" className="h-4 w-4 text-primary" />, labels.favorites)}
{!isSectionCollapsed('favorites') && (favoriteSortingEnabled ? (
<DndContext sensors={favoriteRowSensors} collisionDetection={closestCenter} onDragEnd={handleFavoriteDragEnd}>
@@ -806,11 +934,14 @@ export const ModelPickerList: React.FC<ModelPickerListProps> = ({
) : null}
{filteredRecents.length > 0 ? (
<div>
<>
{filteredFavorites.length > 0 ? <div className="h-px bg-border/40 my-1" /> : null}
{renderSectionHeader('recent', <Icon name="time" className="h-4 w-4" />, labels.recent)}
{!isSectionCollapsed('recent') ? filteredRecents.map((entry) => renderRow(entry, 'recent', true, currentFlatIndex++)) : null}
</div>
<div className="relative">
{renderSectionSentinel('recent')}
{renderSectionHeader('recent', <Icon name="time" className="h-4 w-4" />, labels.recent)}
{!isSectionCollapsed('recent') ? filteredRecents.map((entry) => renderRow(entry, 'recent', true, currentFlatIndex++)) : null}
</div>
</>
) : null}
{(filteredFavorites.length > 0 || filteredRecents.length > 0) && filteredProviders.length > 0 ? <div className="h-px bg-border/40 my-1" /> : null}
@@ -834,6 +965,15 @@ export const ModelPickerList: React.FC<ModelPickerListProps> = ({
)}
</div>
</ScrollableOverlay>
{stickyHeaders && leadingSectionKey ? (
<div
className="oc-sticky-fade-overlay pointer-events-none absolute inset-x-0 top-0 z-30 flex items-center gap-2 px-3 py-1.5 typography-micro font-semibold uppercase tracking-wider text-muted-foreground"
aria-hidden="true"
>
{renderSectionIdentity(leadingSectionKey)}
</div>
) : null}
</div>
<div className="px-3 pt-1 pb-1.5 border-t border-border/40 typography-micro text-muted-foreground">
<ModelPickerFooter store={selectionStore} flatModelList={flatModelList} footerContent={footerContent} fallback={labels.keyboardHint} />
@@ -18,7 +18,6 @@ import {
} from '@/components/ui/select';
import { Icon } from "@/components/icon/Icon";
import {
invokeDesktop,
isDesktopShell,
isVSCodeRuntime,
isWebRuntime,
@@ -410,16 +409,6 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
const [themesReloading, setThemesReloading] = React.useState(false);
// macOS-desktop-only vibrancy toggle. Changing it needs a full relaunch
// (vibrancy is a window-creation option), so we persist + restart on save.
const macVibrancySupported = React.useMemo(
() => isDesktopShell() && typeof window !== 'undefined' && window.__OPENCHAMBER_ELECTRON__?.macVibrancySupported === true,
[],
);
const macVibrancyEnabled = typeof window !== 'undefined' && window.__OPENCHAMBER_ELECTRON__?.macVibrancy === true;
const [vibrancyChecked, setVibrancyChecked] = React.useState(macVibrancyEnabled);
const [vibrancyRestarting, setVibrancyRestarting] = React.useState(false);
// macOS-desktop-only dock badge that counts chats with unseen activity.
// The tray sync (mac-only) pumps the count to the main process, so the
// toggle is offered only where it actually has an effect. No relaunch needed.
@@ -979,36 +968,6 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
</div>
</SettingsTwoColumn>
{macVibrancySupported && (
<SettingsInset settingsItem="appearance.window-transparency" className="flex flex-col gap-1.5">
<SettingsCheckboxRow
checked={vibrancyChecked}
onChange={setVibrancyChecked}
disabled={vibrancyRestarting}
label={t('settings.openchamber.visual.field.macVibrancy')}
info={t('settings.openchamber.visual.field.macVibrancyHint')}
ariaLabel={t('settings.openchamber.visual.field.macVibrancy')}
/>
{vibrancyChecked !== macVibrancyEnabled && (
<div className="pl-6">
<Button
variant="outline"
size="sm"
disabled={vibrancyRestarting}
onClick={() => {
setVibrancyRestarting(true);
void invokeDesktop('desktop_set_vibrancy', { enabled: vibrancyChecked });
}}
>
{vibrancyRestarting
? t('settings.openchamber.visual.actions.restarting')
: t('settings.openchamber.visual.actions.saveAndRestart')}
</Button>
</div>
)}
</SettingsInset>
)}
{dockBadgeSupported && (
<SettingsInset settingsItem="appearance.dock-badge">
<SettingsCheckboxRow
@@ -1143,7 +1143,6 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
data-slot="dropdown-menu-content"
finalFocus={() => (renamingFolderId || editingIdRef.current) ? false : true}
style={{
backgroundColor: 'var(--surface-elevated)',
color: 'var(--surface-elevated-foreground)',
}}
className={cn(dropdownMenuPopupClass, 'min-w-[180px]')}
@@ -218,7 +218,7 @@ function SidebarProjectsListComponent(props: Props): React.ReactNode {
hideTopScrollShadow={!enableStickyFade}
scrollShadowSize={96}
outerClassName="flex-1 min-h-0"
className={cn('oc-sidebar-scroller space-y-1.5 pb-1 pl-2.5 pr-2 [overflow-anchor:none]', props.mobileVariant ? '' : '')}
className={cn('oc-sidebar-scroller oc-sticky-fade-scroller space-y-1.5 pb-1 pl-2.5 pr-2 [overflow-anchor:none]', props.mobileVariant ? '' : '')}
style={enableStickyFade ? { '--scroll-shadow-top-size': '0px' } as React.CSSProperties : undefined}
onScroll={enableStickyFade ? (event) => syncTopFade(event.currentTarget) : undefined}
>
@@ -29,7 +29,6 @@ function ContextMenuContent({ className, positionerClassName, children, style, .
<BaseContextMenu.Popup
data-slot="dropdown-menu-content"
style={{
backgroundColor: "var(--surface-elevated)",
color: "var(--surface-elevated-foreground)",
...style,
}}
+2 -2
View File
@@ -61,7 +61,7 @@ const DialogOverlay = React.forwardRef<
ref={ref as React.Ref<HTMLDivElement>}
data-slot="dialog-overlay"
className={cn(
"fixed inset-0 z-50 bg-black/50 dark:bg-black/75",
"oc-glass-backdrop fixed inset-0 z-50 bg-black/25 dark:bg-black/40",
"transition-opacity duration-150 ease-out",
"data-[starting-style]:opacity-0 data-[ending-style]:opacity-0",
className
@@ -93,7 +93,7 @@ function DialogContent({
data-slot="dialog-content"
data-state-slot="dialog"
className={cn(
"relative pointer-events-auto bg-background text-foreground flex flex-col w-full max-w-lg max-h-full gap-4 rounded-xl border p-6 shadow-none overflow-y-auto pwa-dialog-content origin-center",
"oc-glass-dialog relative pointer-events-auto text-foreground flex flex-col w-full max-w-lg max-h-full gap-4 rounded-xl border p-6 shadow-none overflow-y-auto pwa-dialog-content origin-center",
"transition-all duration-150 ease-out",
"data-[starting-style]:opacity-0 data-[starting-style]:scale-[0.98]",
"data-[ending-style]:opacity-0 data-[ending-style]:scale-[0.98]",
@@ -1,4 +1,4 @@
export const dropdownMenuPopupClass = "app-region-no-drag transition-all duration-150 ease-out data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95 z-50 max-h-[var(--available-height)] min-w-[8rem] origin-[var(--transform-origin)] overflow-visible rounded-xl p-1 shadow-[inset_0_1px_0_0_rgba(255,255,255,0.8),inset_0_0_0_1px_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.10),0_1px_2px_-0.5px_rgba(0,0,0,0.08),0_4px_8px_-2px_rgba(0,0,0,0.08),0_12px_20px_-4px_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.12),inset_0_0_0_1px_rgba(255,255,255,0.08),0_0_0_1px_rgba(0,0,0,0.36),0_1px_1px_-0.5px_rgba(0,0,0,0.22),0_3px_3px_-1.5px_rgba(0,0,0,0.20),0_6px_6px_-3px_rgba(0,0,0,0.16)]";
export const dropdownMenuPopupClass = "oc-glass-popover oc-glass-floating app-region-no-drag transition-all duration-150 ease-out data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95 z-50 max-h-[var(--available-height)] min-w-[8rem] origin-[var(--transform-origin)] overflow-visible rounded-xl p-1";
export const dropdownMenuItemClass = "data-[highlighted]:bg-interactive-hover hover:bg-interactive-hover data-[variant=destructive]:text-destructive data-[variant=destructive]:hover:bg-destructive/10 dark:data-[variant=destructive]:hover:bg-destructive/20 data-[variant=destructive]:hover:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-pointer items-center gap-2 rounded-lg px-2 py-1 typography-ui-label outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5";
export const dropdownMenuSubTriggerClass = "data-[highlighted]:bg-interactive-hover hover:bg-interactive-hover [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-pointer items-center gap-2 rounded-lg px-2 py-1 typography-ui-label outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5";
export const dropdownMenuSeparatorClass = "bg-border -mx-1 my-0.5 h-px";
@@ -123,7 +123,6 @@ function DropdownMenuContent({
<BaseMenu.Popup
data-slot="dropdown-menu-content"
style={{
backgroundColor: 'var(--surface-elevated)',
color: 'var(--surface-elevated-foreground)',
...style,
}}
@@ -280,7 +279,6 @@ function DropdownMenuSubContent({
<BaseMenu.Popup
data-slot="dropdown-menu-sub-content"
style={{
backgroundColor: 'var(--surface-elevated)',
color: 'var(--surface-elevated-foreground)',
}}
className={cn(
+1 -2
View File
@@ -192,11 +192,10 @@ function SelectContent({
<BaseSelect.Popup
data-slot="select-content"
style={{
backgroundColor: 'var(--surface-elevated)',
color: 'var(--surface-elevated-foreground)',
}}
className={cn(
"pointer-events-auto transition-all duration-150 ease-out data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95 relative z-[120] max-h-[var(--available-height)] min-w-[8rem] origin-[var(--transform-origin)] overflow-x-hidden rounded-xl shadow-[inset_0_1px_0_0_rgba(255,255,255,0.8),inset_0_0_0_1px_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.10),0_1px_2px_-0.5px_rgba(0,0,0,0.08),0_4px_8px_-2px_rgba(0,0,0,0.08),0_12px_20px_-4px_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.12),inset_0_0_0_1px_rgba(255,255,255,0.08),0_0_0_1px_rgba(0,0,0,0.36),0_1px_1px_-0.5px_rgba(0,0,0,0.22),0_3px_3px_-1.5px_rgba(0,0,0,0.20),0_6px_6px_-3px_rgba(0,0,0,0.16)]",
"oc-glass-popover oc-glass-floating pointer-events-auto transition-all duration-150 ease-out data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95 relative z-[120] max-h-[var(--available-height)] min-w-[8rem] origin-[var(--transform-origin)] overflow-x-hidden rounded-xl",
!alignItemWithTrigger &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
fitContent && "w-max min-w-0",
+1 -1
View File
@@ -277,7 +277,7 @@ function TooltipContent({
// data-instant is set when moving between grouped tooltips
// (shared TooltipProvider): reposition without replaying the
// full exit/enter animation.
"bg-[var(--surface-elevated)] text-[var(--surface-elevated-foreground)] border border-border/60 transition-all duration-150 ease-out data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95 data-[instant]:transition-none data-[instant]:duration-0 z-50 w-fit origin-[var(--transform-origin)] rounded-xl px-3 py-1.5 typography-meta text-balance overflow-hidden",
"oc-glass-tooltip text-[var(--surface-elevated-foreground)] border border-border/60 transition-all duration-150 ease-out data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95 data-[instant]:transition-none data-[instant]:duration-0 z-50 w-fit origin-[var(--transform-origin)] rounded-xl px-3 py-1.5 typography-meta text-balance overflow-hidden",
className
)}
style={{ ...style }}
@@ -38,7 +38,7 @@ export const SettingsWindow: React.FC<SettingsWindowProps> = ({ open, onOpenChan
<Dialog.Portal>
<Dialog.Backdrop
className={cn(
'fixed inset-0 z-50 bg-black/50 dark:bg-black/75',
'oc-glass-backdrop fixed inset-0 z-50 bg-black/25 dark:bg-black/40',
'transition-opacity duration-150 ease-out',
'data-[starting-style]:opacity-0 data-[ending-style]:opacity-0',
)}
@@ -50,7 +50,7 @@ export const SettingsWindow: React.FC<SettingsWindowProps> = ({ open, onOpenChan
'relative pointer-events-auto',
'w-[90vw] max-w-[1200px] h-[85vh] max-h-[900px]',
'rounded-xl border shadow-none overflow-hidden origin-center',
'bg-background',
'oc-glass-dialog',
'transition-all duration-150 ease-out',
'data-[starting-style]:opacity-0 data-[starting-style]:scale-[0.98]',
'data-[ending-style]:opacity-0 data-[ending-style]:scale-[0.98]',