mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-02 14:19:56 +00:00
fix(ui): restore scrolling in bounded code editors
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user