diff --git a/ui/tests/unit/components/ui/code-editor-usage-contract.test.ts b/ui/tests/unit/components/ui/code-editor-usage-contract.test.ts
new file mode 100644
index 00000000..7175b8d8
--- /dev/null
+++ b/ui/tests/unit/components/ui/code-editor-usage-contract.test.ts
@@ -0,0 +1,42 @@
+import { readFileSync } from 'node:fs';
+import { resolve } from 'node:path';
+import { describe, expect, it } from 'vitest';
+
+const boundedConsumers = [
+ {
+ file: 'src/pages/cliproxy-ai-providers.tsx',
+ expectedCount: 2,
+ },
+ {
+ file: 'src/components/cliproxy/provider-editor/raw-editor-section.tsx',
+ expectedCount: 1,
+ },
+ {
+ file: 'src/components/profiles/editor/raw-editor-section.tsx',
+ expectedCount: 1,
+ },
+ {
+ file: 'src/components/copilot/config-form/raw-editor-section.tsx',
+ expectedCount: 1,
+ },
+ {
+ file: 'src/components/compatible-cli/raw-json-settings-editor-panel.tsx',
+ expectedCount: 1,
+ },
+ {
+ file: 'src/components/shared/settings-dialog.tsx',
+ expectedCount: 1,
+ },
+] as const;
+
+describe('bounded CodeEditor consumers', () => {
+ it.each(boundedConsumers)('$file opts into fill-parent mode for every bounded editor', ({
+ file,
+ expectedCount,
+ }) => {
+ const source = readFileSync(resolve(process.cwd(), file), 'utf8');
+ const matches = source.match(/heightMode="fill-parent"/g) ?? [];
+
+ expect(matches).toHaveLength(expectedCount);
+ });
+});
diff --git a/ui/tests/unit/components/ui/code-editor.test.tsx b/ui/tests/unit/components/ui/code-editor.test.tsx
new file mode 100644
index 00000000..3a8d71cc
--- /dev/null
+++ b/ui/tests/unit/components/ui/code-editor.test.tsx
@@ -0,0 +1,60 @@
+import { describe, expect, it, vi } from 'vitest';
+import { render, screen } from '@tests/setup/test-utils';
+
+import { CodeEditor } from '@/components/shared/code-editor';
+
+vi.mock('@/hooks/use-theme', () => ({
+ useTheme: () => ({ isDark: false }),
+}));
+
+describe('CodeEditor', () => {
+ it('creates an internal scroll viewport in fill-parent mode and keeps status outside it', () => {
+ const { container } = render(
+
+ );
+
+ const viewport = container.querySelector('[data-slot="code-editor-viewport"]');
+
+ expect(viewport).toBeInTheDocument();
+ expect(viewport).toHaveStyle({ height: '100%' });
+ expect(viewport).not.toContainElement(screen.getByText('Valid JSON'));
+ });
+
+ it('keeps readonly status outside the scroll viewport for bounded editors', () => {
+ const { container } = render(
+
+ );
+
+ const viewport = container.querySelector('[data-slot="code-editor-viewport"]');
+ const textarea = container.querySelector('textarea');
+
+ expect(viewport).toHaveStyle({ height: 'calc(60vh - 120px)' });
+ expect(textarea).toBeDisabled();
+ expect(viewport).not.toContainElement(screen.getByText('(Read-only)'));
+ });
+
+ it('preserves content mode as the default layout contract', () => {
+ const { container } = render(
+
+ );
+
+ expect(container.querySelector('[data-slot="code-editor-viewport"]')).not.toBeInTheDocument();
+ });
+});