fix(ui): restore scrolling in bounded code editors

This commit is contained in:
Tam Nhu Tran
2026-03-26 15:10:41 -04:00
parent a94a529201
commit 44d4df1254
7 changed files with 53 additions and 32 deletions
@@ -32,7 +32,7 @@ export function RawEditorSection({
</div> </div>
} }
> >
<div className="h-full flex flex-col"> <div className="flex h-full min-h-0 flex-col">
{!isRawJsonValid && rawJsonEdits !== null && ( {!isRawJsonValid && rawJsonEdits !== null && (
<div className="mb-2 px-3 py-2 bg-destructive/10 text-destructive text-sm rounded-md flex items-center gap-2 mx-6 mt-4 shrink-0"> <div className="mb-2 px-3 py-2 bg-destructive/10 text-destructive text-sm rounded-md flex items-center gap-2 mx-6 mt-4 shrink-0">
<X className="w-4 h-4" /> <X className="w-4 h-4" />
@@ -56,13 +56,14 @@ export function RawEditorSection({
</div> </div>
</div> </div>
)} )}
<div className="flex-1 overflow-hidden px-6 pb-4 pt-4"> <div className="min-h-0 flex-1 overflow-hidden px-6 pb-4 pt-4">
<div className="h-full border rounded-md overflow-hidden bg-background"> <div className="h-full border rounded-md overflow-hidden bg-background">
<CodeEditor <CodeEditor
value={rawJsonContent} value={rawJsonContent}
onChange={onRawJsonChange} onChange={onRawJsonChange}
language="json" language="json"
minHeight="100%" minHeight="100%"
heightMode="fill-parent"
/> />
</div> </div>
</div> </div>
@@ -85,15 +85,21 @@ export function RawJsonSettingsEditorPanel({
Loading settings.json... Loading settings.json...
</div> </div>
) : ( ) : (
<div className="h-full flex flex-col"> <div className="flex h-full min-h-0 flex-col">
{parseWarning && ( {parseWarning && (
<div className="mx-4 mt-4 rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-sm text-amber-800 dark:bg-amber-950/20 dark:text-amber-300"> <div className="mx-4 mt-4 rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-sm text-amber-800 dark:bg-amber-950/20 dark:text-amber-300">
Parse warning: {parseWarning} Parse warning: {parseWarning}
</div> </div>
)} )}
<div className="flex-1 p-4 pt-3"> <div className="min-h-0 flex-1 p-4 pt-3">
<div className="h-full rounded-md border overflow-hidden bg-background"> <div className="h-full rounded-md border overflow-hidden bg-background">
<CodeEditor value={value} onChange={onChange} language="json" minHeight="100%" /> <CodeEditor
value={value}
onChange={onChange}
language="json"
minHeight="100%"
heightMode="fill-parent"
/>
</div> </div>
</div> </div>
</div> </div>
@@ -40,7 +40,7 @@ export function RawEditorSection({
</div> </div>
} }
> >
<div className="h-full flex flex-col"> <div className="flex h-full min-h-0 flex-col">
{!isRawJsonValid && rawJsonEdits !== null && ( {!isRawJsonValid && rawJsonEdits !== null && (
<div className="mb-2 px-3 py-2 bg-destructive/10 text-destructive text-sm rounded-md flex items-center gap-2 mx-6 mt-4 shrink-0"> <div className="mb-2 px-3 py-2 bg-destructive/10 text-destructive text-sm rounded-md flex items-center gap-2 mx-6 mt-4 shrink-0">
<X className="w-4 h-4" /> <X className="w-4 h-4" />
@@ -63,13 +63,14 @@ export function RawEditorSection({
</div> </div>
</div> </div>
)} )}
<div className="flex-1 overflow-hidden px-6 pb-4 pt-4"> <div className="min-h-0 flex-1 overflow-hidden px-6 pb-4 pt-4">
<div className="h-full border rounded-md overflow-hidden bg-background"> <div className="h-full border rounded-md overflow-hidden bg-background">
<CodeEditor <CodeEditor
value={rawJsonContent} value={rawJsonContent}
onChange={onChange} onChange={onChange}
language="json" language="json"
minHeight="100%" minHeight="100%"
heightMode="fill-parent"
/> />
</div> </div>
</div> </div>
@@ -41,7 +41,7 @@ export function RawEditorSection({
</div> </div>
} }
> >
<div className="h-full flex flex-col"> <div className="flex h-full min-h-0 flex-col">
{!isRawJsonValid && rawJsonEdits !== null && ( {!isRawJsonValid && rawJsonEdits !== null && (
<div className="mb-2 px-3 py-2 bg-destructive/10 text-destructive text-sm rounded-md flex items-center gap-2 mx-6 mt-4 shrink-0"> <div className="mb-2 px-3 py-2 bg-destructive/10 text-destructive text-sm rounded-md flex items-center gap-2 mx-6 mt-4 shrink-0">
<X className="w-4 h-4" /> <X className="w-4 h-4" />
@@ -64,13 +64,14 @@ export function RawEditorSection({
</div> </div>
</div> </div>
)} )}
<div className="flex-1 overflow-hidden px-6 pb-4 pt-4"> <div className="min-h-0 flex-1 overflow-hidden px-6 pb-4 pt-4">
<div className="h-full border rounded-md overflow-hidden bg-background"> <div className="h-full border rounded-md overflow-hidden bg-background">
<CodeEditor <CodeEditor
value={rawJsonContent} value={rawJsonContent}
onChange={onChange} onChange={onChange}
language="json" language="json"
minHeight="100%" minHeight="100%"
heightMode="fill-parent"
/> />
</div> </div>
</div> </div>
+31 -22
View File
@@ -20,6 +20,7 @@ interface CodeEditorProps {
readonly?: boolean; readonly?: boolean;
className?: string; className?: string;
minHeight?: string; minHeight?: string;
heightMode?: 'content' | 'fill-parent';
} }
interface ValidationResult { interface ValidationResult {
@@ -70,10 +71,12 @@ export function CodeEditor({
readonly = false, readonly = false,
className, className,
minHeight = '300px', minHeight = '300px',
heightMode = 'content',
}: CodeEditorProps) { }: CodeEditorProps) {
const { isDark } = useTheme(); const { isDark } = useTheme();
const [isFocused, setIsFocused] = useState(false); const [isFocused, setIsFocused] = useState(false);
const [isMasked, setIsMasked] = useState(true); const [isMasked, setIsMasked] = useState(true);
const isFillParent = heightMode === 'fill-parent';
// Validate on every change for JSON // Validate on every change for JSON
const validation = useMemo(() => { const validation = useMemo(() => {
@@ -153,7 +156,7 @@ export function CodeEditor({
); );
return ( return (
<div className={cn('flex flex-col', className)}> <div className={cn('flex min-h-0 flex-col', className)}>
{/* Editor container */} {/* Editor container */}
<div <div
className={cn( className={cn(
@@ -163,28 +166,34 @@ export function CodeEditor({
readonly && 'opacity-70 cursor-not-allowed', readonly && 'opacity-70 cursor-not-allowed',
!validation.valid && 'border-destructive' !validation.valid && 'border-destructive'
)} )}
style={{ minHeight }} data-slot="code-editor-surface"
> >
<Editor <div
value={value} className={cn(isFillParent && 'min-h-0 overflow-auto')}
onValueChange={readonly ? () => {} : onChange} style={isFillParent ? { height: minHeight === 'auto' ? '100%' : minHeight } : undefined}
highlight={highlightCode} data-slot={isFillParent ? 'code-editor-viewport' : undefined}
key={isDark ? 'dark-editor' : 'light-editor'} >
padding={12} <Editor
disabled={readonly} value={value}
onFocus={() => setIsFocused(true)} onValueChange={readonly ? () => {} : onChange}
onBlur={() => setIsFocused(false)} highlight={highlightCode}
textareaClassName={cn( key={isDark ? 'dark-editor' : 'light-editor'}
'focus:outline-none font-mono text-sm', padding={12}
readonly && 'cursor-not-allowed' disabled={readonly}
)} onFocus={() => setIsFocused(true)}
preClassName="font-mono text-sm" onBlur={() => setIsFocused(false)}
style={{ textareaClassName={cn(
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace', 'focus:outline-none font-mono text-sm',
fontSize: '0.875rem', readonly && 'cursor-not-allowed'
minHeight, )}
}} preClassName="font-mono text-sm"
/> style={{
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
fontSize: '0.875rem',
minHeight,
}}
/>
</div>
{/* Secrets Toggle Overlay */} {/* Secrets Toggle Overlay */}
<div className="absolute top-2 right-2 z-10 opacity-50 hover:opacity-100 transition-opacity"> <div className="absolute top-2 right-2 z-10 opacity-50 hover:opacity-100 transition-opacity">
+2 -1
View File
@@ -279,7 +279,7 @@ function SettingsDialogContent({
</ScrollArea> </ScrollArea>
</TabsContent> </TabsContent>
<TabsContent value="raw" className="flex-1 overflow-hidden p-4 pt-4 m-0"> <TabsContent value="raw" className="m-0 min-h-0 flex-1 overflow-hidden p-4 pt-4">
<Suspense <Suspense
fallback={ fallback={
<div className="flex items-center justify-center h-full"> <div className="flex items-center justify-center h-full">
@@ -293,6 +293,7 @@ function SettingsDialogContent({
onChange={handleRawJsonChange} onChange={handleRawJsonChange}
language="json" language="json"
minHeight="calc(60vh - 120px)" minHeight="calc(60vh - 120px)"
heightMode="fill-parent"
/> />
</Suspense> </Suspense>
</TabsContent> </TabsContent>
+2
View File
@@ -1248,6 +1248,7 @@ function EntryInspector({
onChange={handleRawJsonChange} onChange={handleRawJsonChange}
language="json" language="json"
minHeight="100%" minHeight="100%"
heightMode="fill-parent"
/> />
</div> </div>
</div> </div>
@@ -1271,6 +1272,7 @@ function EntryInspector({
language="json" language="json"
readonly readonly
minHeight="100%" minHeight="100%"
heightMode="fill-parent"
/> />
</div> </div>
</div> </div>