feat(mobile): add keyboard avoidance for input area
This commit is contained in:
@@ -209,7 +209,7 @@ export const SessionAuthGate: React.FC<SessionAuthGateProps> = ({ children }) =>
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="w-full space-y-2">
|
<form onSubmit={handleSubmit} className="w-full space-y-2" data-keyboard-avoid="true">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="relative flex-1">
|
<div className="relative flex-1">
|
||||||
<RiLockLine className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground/60" />
|
<RiLockLine className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground/60" />
|
||||||
|
|||||||
@@ -1166,7 +1166,12 @@ export const ChatInput: React.FC<ChatInputProps> = ({ onOpenSettings, scrollToBo
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="pt-0 pb-2 md:pb-4 bottom-safe-area" style={isMobile && inputBarOffset > 0 && !isKeyboardOpen ? { marginBottom: `${inputBarOffset}px` } : undefined}>
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className="pt-0 pb-2 md:pb-4 bottom-safe-area"
|
||||||
|
data-keyboard-avoid="true"
|
||||||
|
style={isMobile && inputBarOffset > 0 && !isKeyboardOpen ? { marginBottom: `${inputBarOffset}px` } : undefined}
|
||||||
|
>
|
||||||
<StatusRow
|
<StatusRow
|
||||||
isWorking={working.isWorking}
|
isWorking={working.isWorking}
|
||||||
statusText={workingStatusText}
|
statusText={workingStatusText}
|
||||||
|
|||||||
@@ -102,9 +102,36 @@ export const MainLayout: React.FC = () => {
|
|||||||
let stickyKeyboardInset = 0;
|
let stickyKeyboardInset = 0;
|
||||||
let ignoreOpenUntilZero = false;
|
let ignoreOpenUntilZero = false;
|
||||||
let previousHeight = 0;
|
let previousHeight = 0;
|
||||||
|
let keyboardAvoidTarget: HTMLElement | null = null;
|
||||||
|
|
||||||
const setKeyboardOpen = useUIStore.getState().setKeyboardOpen;
|
const setKeyboardOpen = useUIStore.getState().setKeyboardOpen;
|
||||||
|
|
||||||
|
const clearKeyboardAvoidTarget = () => {
|
||||||
|
if (!keyboardAvoidTarget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
keyboardAvoidTarget.style.setProperty('--oc-keyboard-avoid-offset', '0px');
|
||||||
|
keyboardAvoidTarget.removeAttribute('data-keyboard-avoid-active');
|
||||||
|
keyboardAvoidTarget = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveKeyboardAvoidTarget = (active: HTMLElement | null) => {
|
||||||
|
if (!active) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const markedTarget = active.closest('[data-keyboard-avoid]') as HTMLElement | null;
|
||||||
|
if (markedTarget) {
|
||||||
|
return markedTarget;
|
||||||
|
}
|
||||||
|
if (active.classList.contains('overlay-scrollbar-container')) {
|
||||||
|
const parent = active.parentElement;
|
||||||
|
if (parent instanceof HTMLElement) {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return active;
|
||||||
|
};
|
||||||
|
|
||||||
const forceKeyboardClosed = () => {
|
const forceKeyboardClosed = () => {
|
||||||
stickyKeyboardInset = 0;
|
stickyKeyboardInset = 0;
|
||||||
ignoreOpenUntilZero = true;
|
ignoreOpenUntilZero = true;
|
||||||
@@ -155,7 +182,7 @@ export const MainLayout: React.FC = () => {
|
|||||||
// (prevents false positives during Android keyboard animation)
|
// (prevents false positives during Android keyboard animation)
|
||||||
const closingByHeight = !isTextTarget && height > previousHeight + 6;
|
const closingByHeight = !isTextTarget && height > previousHeight + 6;
|
||||||
|
|
||||||
if (measuredInset === 0) {
|
if (measuredInset === 0) {
|
||||||
stickyKeyboardInset = 0;
|
stickyKeyboardInset = 0;
|
||||||
setKeyboardOpen(false);
|
setKeyboardOpen(false);
|
||||||
} else if (closingByHeight) {
|
} else if (closingByHeight) {
|
||||||
@@ -174,6 +201,30 @@ if (measuredInset === 0) {
|
|||||||
root.style.setProperty('--oc-keyboard-inset', `${stickyKeyboardInset}px`);
|
root.style.setProperty('--oc-keyboard-inset', `${stickyKeyboardInset}px`);
|
||||||
previousHeight = height;
|
previousHeight = height;
|
||||||
|
|
||||||
|
const avoidTarget = isTextTarget ? resolveKeyboardAvoidTarget(active) : null;
|
||||||
|
|
||||||
|
if (!isMobile || !avoidTarget || !active) {
|
||||||
|
clearKeyboardAvoidTarget();
|
||||||
|
} else {
|
||||||
|
if (avoidTarget !== keyboardAvoidTarget) {
|
||||||
|
clearKeyboardAvoidTarget();
|
||||||
|
keyboardAvoidTarget = avoidTarget;
|
||||||
|
}
|
||||||
|
const viewportBottom = offsetTop + height;
|
||||||
|
const rect = active.getBoundingClientRect();
|
||||||
|
const overlap = rect.bottom - viewportBottom;
|
||||||
|
const clearance = 8;
|
||||||
|
const keyboardInset = Math.max(stickyKeyboardInset, measuredInset);
|
||||||
|
const avoidOffset = overlap > clearance && keyboardInset > 0
|
||||||
|
? Math.min(overlap, keyboardInset)
|
||||||
|
: 0;
|
||||||
|
const target = keyboardAvoidTarget;
|
||||||
|
if (target) {
|
||||||
|
target.style.setProperty('--oc-keyboard-avoid-offset', `${avoidOffset}px`);
|
||||||
|
target.setAttribute('data-keyboard-avoid-active', 'true');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Only force-scroll lock while an input is focused.
|
// Only force-scroll lock while an input is focused.
|
||||||
if (isMobile && isTextTarget) {
|
if (isMobile && isTextTarget) {
|
||||||
const scroller = document.scrollingElement;
|
const scroller = document.scrollingElement;
|
||||||
@@ -239,6 +290,7 @@ if (measuredInset === 0) {
|
|||||||
window.removeEventListener('orientationchange', updateVisualViewport);
|
window.removeEventListener('orientationchange', updateVisualViewport);
|
||||||
document.removeEventListener('focusin', handleFocusIn, true);
|
document.removeEventListener('focusin', handleFocusIn, true);
|
||||||
document.removeEventListener('focusout', handleFocusOut, true);
|
document.removeEventListener('focusout', handleFocusOut, true);
|
||||||
|
clearKeyboardAvoidTarget();
|
||||||
};
|
};
|
||||||
}, [isMobile]);
|
}, [isMobile]);
|
||||||
|
|
||||||
|
|||||||
@@ -422,7 +422,7 @@ export const MultiRunLauncher: React.FC<MultiRunLauncherProps> = ({
|
|||||||
{/* Content with chat-column max-width */}
|
{/* Content with chat-column max-width */}
|
||||||
<div className="flex-1 overflow-auto">
|
<div className="flex-1 overflow-auto">
|
||||||
<div className="chat-column py-6">
|
<div className="chat-column py-6">
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6" data-keyboard-avoid="true">
|
||||||
{/* Group name (required) */}
|
{/* Group name (required) */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label htmlFor="group-name" className="typography-ui-label font-medium text-foreground">
|
<label htmlFor="group-name" className="typography-ui-label font-medium text-foreground">
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ export const AgentsPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollableOverlay outerClassName="h-full" className="w-full">
|
<ScrollableOverlay keyboardAvoid outerClassName="h-full" className="w-full">
|
||||||
<div className="mx-auto max-w-3xl space-y-6 p-6">
|
<div className="mx-auto max-w-3xl space-y-6 p-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
|
|||||||
@@ -122,8 +122,8 @@ export const CommandsPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollableOverlay outerClassName="h-full" className="w-full">
|
<ScrollableOverlay keyboardAvoid outerClassName="h-full" className="w-full">
|
||||||
<div className="mx-auto max-w-3xl space-y-6 p-6">
|
<div className="mx-auto max-w-3xl space-y-6 p-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<h1 className="typography-ui-header font-semibold text-lg">
|
<h1 className="typography-ui-header font-semibold text-lg">
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export const OpenChamberPage: React.FC<OpenChamberPageProps> = ({ section }) =>
|
|||||||
if (!section) {
|
if (!section) {
|
||||||
return (
|
return (
|
||||||
<ScrollableOverlay
|
<ScrollableOverlay
|
||||||
|
keyboardAvoid
|
||||||
outerClassName="h-full"
|
outerClassName="h-full"
|
||||||
className="openchamber-page-body mx-auto max-w-3xl space-y-3 p-3 sm:space-y-6 sm:p-6"
|
className="openchamber-page-body mx-auto max-w-3xl space-y-3 p-3 sm:space-y-6 sm:p-6"
|
||||||
>
|
>
|
||||||
@@ -56,6 +57,7 @@ export const OpenChamberPage: React.FC<OpenChamberPageProps> = ({ section }) =>
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollableOverlay
|
<ScrollableOverlay
|
||||||
|
keyboardAvoid
|
||||||
outerClassName="h-full"
|
outerClassName="h-full"
|
||||||
className="openchamber-page-body mx-auto max-w-3xl space-y-6 p-3 sm:p-6"
|
className="openchamber-page-body mx-auto max-w-3xl space-y-6 p-3 sm:p-6"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -732,7 +732,7 @@ export const ProvidersPage: React.FC = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollableOverlay outerClassName="h-full" className="w-full">
|
<ScrollableOverlay keyboardAvoid outerClassName="h-full" className="w-full">
|
||||||
<div className="mx-auto max-w-3xl space-y-6 p-6">
|
<div className="mx-auto max-w-3xl space-y-6 p-6">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
@@ -363,7 +363,7 @@ export const SkillsPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollableOverlay outerClassName="h-full" className="w-full">
|
<ScrollableOverlay keyboardAvoid outerClassName="h-full" className="w-full">
|
||||||
<div className="mx-auto max-w-3xl space-y-6 p-6">
|
<div className="mx-auto max-w-3xl space-y-6 p-6">
|
||||||
{isNewSkill ? modeTabs : null}
|
{isNewSkill ? modeTabs : null}
|
||||||
|
|
||||||
@@ -558,7 +558,7 @@ export const SkillsPage: React.FC = () => {
|
|||||||
setIsFileDialogOpen(open);
|
setIsFileDialogOpen(open);
|
||||||
if (!open) setEditingFilePath(null);
|
if (!open) setEditingFilePath(null);
|
||||||
}}>
|
}}>
|
||||||
<DialogContent className="max-w-2xl max-h-[85vh] flex flex-col">
|
<DialogContent className="max-w-2xl max-h-[85vh] flex flex-col" keyboardAvoid>
|
||||||
<DialogHeader className="flex-shrink-0">
|
<DialogHeader className="flex-shrink-0">
|
||||||
<DialogTitle>{editingFilePath ? 'Edit Supporting File' : 'Add Supporting File'}</DialogTitle>
|
<DialogTitle>{editingFilePath ? 'Edit Supporting File' : 'Add Supporting File'}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ export const AddCatalogDialog: React.FC<AddCatalogDialogProps> = ({ open, onOpen
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="max-w-xl">
|
<DialogContent className="max-w-xl" keyboardAvoid>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Add skills catalog</DialogTitle>
|
<DialogTitle>Add skills catalog</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export const InstallSkillDialog: React.FC<InstallSkillDialogProps> = ({ open, on
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="max-w-lg">
|
<DialogContent className="max-w-lg" keyboardAvoid>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Install skill</DialogTitle>
|
<DialogTitle>Install skill</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollableOverlay outerClassName="h-full" className="w-full">
|
<ScrollableOverlay keyboardAvoid outerClassName="h-full" className="w-full">
|
||||||
<div className="mx-auto max-w-3xl space-y-6 p-6">
|
<div className="mx-auto max-w-3xl space-y-6 p-6">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<AnimatedTabs
|
<AnimatedTabs
|
||||||
|
|||||||
@@ -730,6 +730,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
|||||||
<div className="flex min-w-0 flex-1 flex-col gap-0">
|
<div className="flex min-w-0 flex-1 flex-col gap-0">
|
||||||
<form
|
<form
|
||||||
className="flex w-full items-center gap-2"
|
className="flex w-full items-center gap-2"
|
||||||
|
data-keyboard-avoid="true"
|
||||||
onSubmit={(event) => {
|
onSubmit={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
handleSaveEdit();
|
handleSaveEdit();
|
||||||
|
|||||||
@@ -75,14 +75,15 @@ export const MobileOverlayPanel: React.FC<MobileOverlayPanelProps> = ({
|
|||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'mt-auto w-full rounded-t-xl border border-border/50 bg-background shadow-none pwa-overlay-panel',
|
'mt-auto w-full rounded-t-xl border border-border/50 bg-background shadow-none pwa-overlay-panel',
|
||||||
'mx-auto max-w-lg',
|
'mx-auto max-w-lg',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
onClick={(event) => event.stopPropagation()}
|
data-keyboard-avoid="true"
|
||||||
>
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
>
|
||||||
{(() => {
|
{(() => {
|
||||||
const closeButton = (
|
const closeButton = (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ type ScrollableOverlayProps = React.HTMLAttributes<HTMLElement> & {
|
|||||||
scrollbarClassName?: string;
|
scrollbarClassName?: string;
|
||||||
disableHorizontal?: boolean;
|
disableHorizontal?: boolean;
|
||||||
fillContainer?: boolean;
|
fillContainer?: boolean;
|
||||||
|
keyboardAvoid?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ScrollableOverlay = React.forwardRef<HTMLElement, ScrollableOverlayProps>(
|
export const ScrollableOverlay = React.forwardRef<HTMLElement, ScrollableOverlayProps>(
|
||||||
@@ -24,6 +25,7 @@ export const ScrollableOverlay = React.forwardRef<HTMLElement, ScrollableOverlay
|
|||||||
scrollbarClassName,
|
scrollbarClassName,
|
||||||
disableHorizontal = false,
|
disableHorizontal = false,
|
||||||
fillContainer = true,
|
fillContainer = true,
|
||||||
|
keyboardAvoid = false,
|
||||||
...rest
|
...rest
|
||||||
}, ref) => {
|
}, ref) => {
|
||||||
const containerRef = React.useRef<HTMLElement | null>(null);
|
const containerRef = React.useRef<HTMLElement | null>(null);
|
||||||
@@ -31,7 +33,10 @@ export const ScrollableOverlay = React.forwardRef<HTMLElement, ScrollableOverlay
|
|||||||
React.useImperativeHandle(ref, () => containerRef.current as HTMLElement, []);
|
React.useImperativeHandle(ref, () => containerRef.current as HTMLElement, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("relative flex flex-col min-h-0 w-full overflow-hidden", outerClassName)}>
|
<div
|
||||||
|
className={cn("relative flex flex-col min-h-0 w-full overflow-hidden", outerClassName)}
|
||||||
|
data-keyboard-avoid={keyboardAvoid ? "true" : undefined}
|
||||||
|
>
|
||||||
<Component
|
<Component
|
||||||
ref={containerRef as React.Ref<HTMLElement>}
|
ref={containerRef as React.Ref<HTMLElement>}
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@@ -63,15 +63,18 @@ function DialogContent({
|
|||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
showCloseButton = true,
|
showCloseButton = true,
|
||||||
|
keyboardAvoid = false,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
||||||
showCloseButton?: boolean
|
showCloseButton?: boolean
|
||||||
|
keyboardAvoid?: boolean
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<DialogPortal data-slot="dialog-portal">
|
<DialogPortal data-slot="dialog-portal">
|
||||||
<DialogOverlay className="rounded-none" />
|
<DialogOverlay className="rounded-none" />
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
data-slot="dialog-content"
|
data-slot="dialog-content"
|
||||||
|
data-keyboard-avoid={keyboardAvoid ? "true" : undefined}
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-background text-foreground fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 rounded-xl border p-6 shadow-none overflow-hidden pwa-dialog-content",
|
"bg-background text-foreground fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 rounded-xl border p-6 shadow-none overflow-hidden pwa-dialog-content",
|
||||||
className
|
className
|
||||||
|
|||||||
@@ -640,7 +640,7 @@ export const GitView: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col overflow-hidden bg-background">
|
<div className="flex h-full flex-col overflow-hidden bg-background" data-keyboard-avoid="true">
|
||||||
<GitHeader
|
<GitHeader
|
||||||
status={status}
|
status={status}
|
||||||
localBranches={localBranches}
|
localBranches={localBranches}
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
|||||||
{/* Mobile: show sidebar OR page content based on drill-down state */}
|
{/* Mobile: show sidebar OR page content based on drill-down state */}
|
||||||
{isMobile ? (
|
{isMobile ? (
|
||||||
showMobilePageContent ? (
|
showMobilePageContent ? (
|
||||||
<div className="flex-1 overflow-hidden bg-background">
|
<div className="flex-1 overflow-hidden bg-background" data-keyboard-avoid="true">
|
||||||
<ErrorBoundary>{renderPageContent()}</ErrorBoundary>
|
<ErrorBoundary>{renderPageContent()}</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -804,6 +804,7 @@ export const TerminalView: React.FC = () => {
|
|||||||
<div
|
<div
|
||||||
className="relative flex-1 overflow-hidden"
|
className="relative flex-1 overflow-hidden"
|
||||||
style={{ backgroundColor: xtermTheme.background }}
|
style={{ backgroundColor: xtermTheme.background }}
|
||||||
|
data-keyboard-avoid="true"
|
||||||
>
|
>
|
||||||
<div className="h-full w-full box-border px-3 pt-3 pb-4">
|
<div className="h-full w-full box-border px-3 pt-3 pb-4">
|
||||||
{isTerminalActive ? (
|
{isTerminalActive ? (
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export const CommitSection: React.FC<CommitSectionProps> = ({
|
|||||||
<Collapsible
|
<Collapsible
|
||||||
open={hasSelectedFiles}
|
open={hasSelectedFiles}
|
||||||
className="rounded-xl border border-border/60 bg-background/70 overflow-hidden"
|
className="rounded-xl border border-border/60 bg-background/70 overflow-hidden"
|
||||||
|
data-keyboard-avoid="true"
|
||||||
>
|
>
|
||||||
<div className="flex w-full items-center justify-between px-3 py-2">
|
<div className="flex w-full items-center justify-between px-3 py-2">
|
||||||
<h3 className="typography-ui-header font-semibold text-foreground">Commit</h3>
|
<h3 className="typography-ui-header font-semibold text-foreground">Commit</h3>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
--oc-header-height: 56px;
|
--oc-header-height: 56px;
|
||||||
--oc-visual-viewport-offset-top: 0px;
|
--oc-visual-viewport-offset-top: 0px;
|
||||||
--oc-keyboard-inset: 0px;
|
--oc-keyboard-inset: 0px;
|
||||||
|
--oc-keyboard-avoid-offset: 0px;
|
||||||
--ui-regular-font-weight: 400;
|
--ui-regular-font-weight: 400;
|
||||||
--oc-scrollbar-thumb: oklch(0.32 0.03 50 / 0.4);
|
--oc-scrollbar-thumb: oklch(0.32 0.03 50 / 0.4);
|
||||||
--oc-scrollbar-thumb-hover: oklch(0.32 0.03 50 / 0.6);
|
--oc-scrollbar-thumb-hover: oklch(0.32 0.03 50 / 0.6);
|
||||||
@@ -56,6 +57,10 @@ textarea[data-chat-input="true"]:hover {
|
|||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-keyboard-avoid-active="true"] {
|
||||||
|
transform: translateY(calc(-1 * var(--oc-keyboard-avoid-offset, 0px)));
|
||||||
|
}
|
||||||
|
|
||||||
/* Scroll shadow (HeroUI) fallback styling to ensure visible gradients without the Tailwind plugin */
|
/* Scroll shadow (HeroUI) fallback styling to ensure visible gradients without the Tailwind plugin */
|
||||||
[data-scroll-shadow="true"] {
|
[data-scroll-shadow="true"] {
|
||||||
--scroll-shadow-size: var(--scroll-shadow-size, 48px);
|
--scroll-shadow-size: var(--scroll-shadow-size, 48px);
|
||||||
|
|||||||
Reference in New Issue
Block a user