Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/tmux-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,10 @@ export function buildCodexCommand(config?: CodexConfig): string {
parts.push('--dangerously-bypass-approvals-and-sandbox');
}

if (config?.animations !== undefined) {
parts.push('--config', `tui.animations=${config.animations ? 'true' : 'false'}`);
}

if (config?.model) {
const safeModel = /^[a-zA-Z0-9._\-/]+$/.test(config.model) ? config.model : undefined;
if (safeModel) parts.push('--model', safeModel);
Expand Down
2 changes: 2 additions & 0 deletions src/types/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ export interface CodexConfig {
resumeSessionId?: string;
/** Bypass approval prompts (passes --dangerously-bypass-approvals-and-sandbox) */
dangerouslyBypassApprovals?: boolean;
/** Enable Codex's decorative TUI animations. Disable to reduce remote terminal redraws. */
animations?: boolean;
/** Browser rendering strategy for Codex sessions. Hybrid TUI is the only supported mode. */
renderMode?: CodexRenderMode;
}
Expand Down
8 changes: 8 additions & 0 deletions src/web/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1654,6 +1654,14 @@ <h3>App Settings</h3>
</label>
<span class="form-hint">Start new Codex sessions with --dangerously-bypass-approvals-and-sandbox</span>
</div>
<div class="form-row form-row-switch">
<label>Animated Status Effects</label>
<label class="switch">
<input type="checkbox" id="appSettingsCodexAnimations">
<span class="slider"></span>
</label>
<span class="form-hint">Decorative Codex TUI motion for new local sessions. Leave off to reduce remote and mobile redraws.</span>
</div>
</div>
<!-- Models Tab -->
<div class="modal-tab-content hidden" id="settings-models">
Expand Down
1 change: 1 addition & 0 deletions src/web/public/session-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,7 @@ Object.assign(CodemanApp.prototype, {
...(isRemote ? {} : {
codexConfig: {
dangerouslyBypassApprovals: globalSettings.codexDangerouslyBypassApprovals ?? false,
animations: globalSettings.codexAnimationsEnabled ?? false,
renderMode: 'hybrid',
},
...(Object.keys(envOverrides).length > 0 ? { envOverrides } : {}),
Expand Down
3 changes: 3 additions & 0 deletions src/web/public/settings-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ Object.assign(CodemanApp.prototype, {
// Codex CLI settings
document.getElementById('appSettingsCodexDangerouslyBypassApprovals').checked =
settings.codexDangerouslyBypassApprovals ?? false;
document.getElementById('appSettingsCodexAnimations').checked =
settings.codexAnimationsEnabled ?? false;
// Claude Permissions settings
document.getElementById('appSettingsAgentTeams').checked = settings.agentTeamsEnabled ?? false;
document.getElementById('appSettingsClaudeModel').value = settings.claudeModel ?? '';
Expand Down Expand Up @@ -1467,6 +1469,7 @@ Object.assign(CodemanApp.prototype, {
allowedTools: document.getElementById('appSettingsAllowedTools').value.trim(),
// Codex CLI settings
codexDangerouslyBypassApprovals: document.getElementById('appSettingsCodexDangerouslyBypassApprovals').checked,
codexAnimationsEnabled: document.getElementById('appSettingsCodexAnimations').checked,
// Claude Permissions settings
agentTeamsEnabled: document.getElementById('appSettingsAgentTeams').checked,
claudeModel: document.getElementById('appSettingsClaudeModel').value,
Expand Down
2 changes: 2 additions & 0 deletions src/web/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ const CodexConfigSchema = z
.regex(/^[a-zA-Z0-9_-]+$/)
.optional(),
dangerouslyBypassApprovals: z.boolean().optional(),
animations: z.boolean().optional(),
renderMode: z
.enum(['scrollback', 'hybrid'])
.optional()
Expand Down Expand Up @@ -756,6 +757,7 @@ export const SettingsUpdateSchema = z
allowedTools: z.string().max(2000).optional(),
// Codex CLI settings
codexDangerouslyBypassApprovals: z.boolean().optional(),
codexAnimationsEnabled: z.boolean().optional(),
// Terminal history and retention
terminalScrollbackLines: z
.number()
Expand Down
5 changes: 4 additions & 1 deletion test/run-mode-ui.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,13 @@ describe('Codex quick start settings', () => {
/<div class="modal-tab-content hidden" id="settings-claude">([\s\S]*?)<!-- Codex CLI Tab -->/
);
expect(claudeTab?.[1]).not.toContain('appSettingsCodexDangerouslyBypassApprovals');
expect(claudeTab?.[1]).not.toContain('appSettingsCodexAnimations');

const codexTab = html.match(
/<div class="modal-tab-content hidden" id="settings-codex">([\s\S]*?)<\/div>\s*<!-- Models Tab -->/
);
expect(codexTab?.[1]).toContain('appSettingsCodexDangerouslyBypassApprovals');
expect(codexTab?.[1]).toContain('appSettingsCodexAnimations');
expect(codexTab?.[1]).not.toContain('appSettingsCodexRenderMode');
});

Expand Down Expand Up @@ -222,6 +224,7 @@ describe('Codex quick start settings', () => {
app.terminal = { clear: () => {}, writeln: () => {}, focus: () => {} };
app.loadAppSettingsFromStorage = () => ({
codexDangerouslyBypassApprovals: true,
codexAnimationsEnabled: false,
});
app.getCaseSettings = () => ({});
app.buildEnvOverrides = () => ({});
Expand All @@ -240,7 +243,7 @@ describe('Codex quick start settings', () => {
mode: 'codex',
// tabs follow the w<n>-<case> naming convention (quick-start would otherwise auto-name codeman-<id>)
sessionName: 'w1-codex-case',
codexConfig: { dangerouslyBypassApprovals: true, renderMode: 'hybrid' },
codexConfig: { dangerouslyBypassApprovals: true, animations: false, renderMode: 'hybrid' },
});
expect(selected).toEqual(['sess-1']);
});
Expand Down
9 changes: 9 additions & 0 deletions test/tmux-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import {
TmuxManager,
buildCodexCommand,
buildRemoteKillCommand,
buildRemoteLaunchCommand,
formatPaneSnapshot,
Expand Down Expand Up @@ -98,6 +99,14 @@ describe('TmuxManager (unit)', () => {
});
});

describe('Codex command builder', () => {
it('controls decorative TUI animation through Codex config', () => {
expect(buildCodexCommand({ animations: false })).toBe('codex --config tui.animations=false');
expect(buildCodexCommand({ animations: true })).toBe('codex --config tui.animations=true');
expect(buildCodexCommand()).toBe('codex');
});
});

describe('remote launch command builder', () => {
it('wraps codex command overrides in ssh with remote tmux launch', () => {
const command = buildRemoteLaunchCommand({
Expand Down