[FR][Desktop] Add option to remove white margins

Resolved 💬 5 comments Opened Feb 3, 2026 by NightMachinery Closed May 10, 2026
💡 Likely answer: A maintainer (etraut-openai, contributor) responded on this thread — see the highlighted reply below.

What variant of Codex are you using?

App

What feature would you like to see?

<img width="1280" height="832" alt="Image" src="https://github.com/user-attachments/assets/b8595c64-5fc4-4599-8b11-c0f91a968b05" />
I want to remove the side white margins from the desktop app.

Additional information

_No response_

View original on GitHub ↗

5 Comments

shrinidhi4u · 5 months ago

I agree.. No way to increase the width of central pane. Please add this feature.

AbdelrahmanHafez · 5 months ago

I'd also like this.

The current desktop app hard caps the thread content width, so on a large monitor the main conversation pane ends up using a pretty small slice of the available window. In practice it can look like the app is only using something like a quarter of the monitor width even when the app window itself is much wider.

An official fix would be much better than patching the app bundle locally. I have a temporary workaround that rewrites the desktop CSS after updates by changing the thread max-width variable in Codex.app, which does make the app use the available space, but it also means modifying and re-signing the app bundle. That leads to extra macOS friction like Gatekeeper and Keychain prompts, so it's clearly not something users should need to do just to get a reasonable layout.

It'd be great to have either:

  • a Use full width or Adaptive width setting for the thread view
  • a width slider or a few presets like comfortable, wide, and full
  • or just a less aggressive default cap on desktop, especially on large monitors

Even a simple built-in option here would be a big improvement.

My temporary workaround would only work for MacOS, and needs to be re-applied after each update, but should be easy to give to Codex and ask it to tailor it to your own setup:

~/bin/update-codex-default-width.sh

#!/usr/bin/env bash
# vi: ft=bash
#
# Re-applies a wider default content width to Codex Desktop after updates.
# Usage:
#   update-codex-default-width
#   update-codex-default-width 72rem
#   update-codex-default-width --dry-run
#
# Environment:
#   CODEX_APP_PATH=/Applications/Codex.app
#   CODEX_CONTENT_MAX_WIDTH=100%
#   CODEX_SKIP_UNQUARANTINE=1
#   CODEX_SKIP_CODESIGN=1
#   CODEX_KEEP_TMP=1

if [[ -t 1 ]]; then
  RED='\033[31m' GREEN='\033[32m' YELLOW='\033[33m' CYAN='\033[36m' DIM='\033[2m' NC='\033[0m'
else
  RED='' GREEN='' YELLOW='' CYAN='' DIM='' NC=''
fi

set -euo pipefail

DEFAULT_WIDTH="100%"
DESIRED_WIDTH="${CODEX_CONTENT_MAX_WIDTH:-$DEFAULT_WIDTH}"
DRY_RUN=false
APP_PATH="${CODEX_APP_PATH:-}"
TMP_DIR=""

usage() {
  cat <<EOF
Usage: $(basename "$0") [width] [--dry-run] [--app /path/to/Codex.app]

Examples:
  $(basename "$0")
  $(basename "$0") 72rem
  $(basename "$0") --dry-run

Environment:
  CODEX_APP_PATH=/Applications/Codex.app
  CODEX_CONTENT_MAX_WIDTH=100%
  Width may be any valid CSS max-width value, for example:
    100%
    72rem
    min(100%, 1600px)
  CODEX_SKIP_UNQUARANTINE=1
  CODEX_SKIP_CODESIGN=1
  CODEX_KEEP_TMP=1
EOF
}

die() {
  echo -e "${RED}Error:${NC} $*" >&2
  exit 1
}

cleanup() {
  if [[ -n "$TMP_DIR" && -d "$TMP_DIR" && "${CODEX_KEEP_TMP:-0}" != "1" ]]; then
    rm -rf "$TMP_DIR"
  fi
}

find_codex_app_path() {
  local candidate
  for candidate in /Applications/Codex.app "$HOME/Applications/Codex.app"; do
    if [[ -d "$candidate" ]]; then
      printf '%s\n' "$candidate"
      return 0
    fi
  done
  return 1
}

require_command() {
  command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"
}

append_unique() {
  local needle="$1"
  shift
  local item
  for item in "$@"; do
    if [[ "$item" == "$needle" ]]; then
      return 0
    fi
  done
  return 1
}

parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      -h|--help)
        usage
        exit 0
        ;;
      --dry-run)
        DRY_RUN=true
        ;;
      --app)
        [[ $# -ge 2 ]] || die "--app requires a path"
        APP_PATH="$2"
        shift
        ;;
      --*)
        die "Unknown option: $1"
        ;;
      *)
        DESIRED_WIDTH="$1"
        ;;
    esac
    shift
  done
}

extract_current_width() {
  local css_file="$1"
  perl -0ne 'print "$1\n" if /\[data-codex-window-type=electron\] body\{[^}]*?--thread-content-max-width:([^;]+);/s' "$css_file"
}

extract_plist_hash() {
  local plist_path="$1"
  /usr/libexec/PlistBuddy -c "Print :ElectronAsarIntegrity:Resources/app.asar:hash" "$plist_path"
}

compute_asar_header_hash() {
  local asar_path="$1"
  node - "$asar_path" <<'EOF'
const crypto = require('node:crypto');
const fs = require('node:fs');

const archivePath = process.argv[2];
const fd = fs.openSync(archivePath, 'r');
const sizeBuf = Buffer.alloc(8);
fs.readSync(fd, sizeBuf, 0, 8, 0);
const headerPickleLength = sizeBuf.readUInt32LE(4);
const headerBuf = Buffer.alloc(headerPickleLength);
fs.readSync(fd, headerBuf, 0, headerPickleLength, 8);
fs.closeSync(fd);

const headerStringLength = headerBuf.readUInt32LE(4);
const headerString = headerBuf.subarray(8, 8 + headerStringLength).toString('utf8');
process.stdout.write(crypto.createHash('sha256').update(headerString).digest('hex'));
EOF
  printf '\n'
}

patch_css_width() {
  local css_file="$1"
  CODEX_PATCH_WIDTH="$DESIRED_WIDTH" perl -0pi -e '
    my $width = $ENV{CODEX_PATCH_WIDTH};
    my $count = s~(\[data-codex-window-type=electron\] body\{[^}]*?--thread-content-max-width:)[^;]+(;--thread-composer-max-width:)[^;]+;~
                  $1.$width.$2."min(100%, calc(var(--thread-content-max-width) + 1rem));"~se;
    die "failed to patch Codex width block\n" if $count != 1;
  ' "$css_file"
}

refresh_integrity_digest_if_needed() {
  local app_path="$1"
  local framework_binary="$app_path/Contents/Frameworks/Electron Framework.framework/Electron Framework"
  local verify_output

  [[ -f "$framework_binary" ]] || return 0

  verify_output="$(npx -y @electron/asar integrity-digest verify "$app_path" 2>&1 || true)"

  case "$verify_output" in
    *"Integrity digest verification PASSED"*|*"Integrity digest is off, verification SKIPPED"*|*"No integrity digest found in the binary"*)
      return 0
      ;;
    *"Integrity digest verification FAILED"*)
      echo -e "${DIM}Refreshing embedded Electron integrity digest...${NC}"
      npx -y @electron/asar integrity-digest on "$app_path" >/dev/null
      verify_output="$(npx -y @electron/asar integrity-digest verify "$app_path" 2>&1 || true)"
      [[ "$verify_output" == *"Integrity digest verification PASSED"* ]] || die "Electron integrity digest verification did not pass after refresh"
      ;;
    *)
      echo "$verify_output" >&2
      die "Unexpected response while verifying Electron integrity digest"
      ;;
  esac
}

build_unpack_pattern() {
  local unpacked_dir="$1"
  local rel_path first second root
  local -a roots=()

  while IFS= read -r rel_path; do
    [[ -n "$rel_path" ]] || continue
    first="${rel_path%%/*}"
    second=""
    if [[ "$rel_path" == */* ]]; then
      second="${rel_path#*/}"
      second="${second%%/*}"
    fi

    if [[ "$first" == "node_modules" && -n "$second" ]]; then
      root="$first/$second"
    else
      root="$first"
    fi

    if ! append_unique "$root" "${roots[@]}"; then
      roots+=("$root")
    fi
  done < <(find "$unpacked_dir" \( -type f -o -type l \) -print | sed "s|^$unpacked_dir/||")

  [[ "${#roots[@]}" -gt 0 ]] || die "Could not derive unpacked asset roots from $unpacked_dir"

  local joined
  joined="$(IFS=,; printf '%s' "${roots[*]}")"
  printf '**/{%s}/**/*\n' "$joined"
}

main() {
  trap cleanup EXIT
  parse_args "$@"

  require_command npx
  require_command node
  require_command perl
  [[ -x /usr/libexec/PlistBuddy ]] || die "Missing required command: /usr/libexec/PlistBuddy"

  if [[ -z "$APP_PATH" ]]; then
    APP_PATH="$(find_codex_app_path || true)"
  fi

  [[ -n "$APP_PATH" ]] || die "Could not find Codex.app in /Applications or ~/Applications"
  [[ -d "$APP_PATH" ]] || die "App path does not exist: $APP_PATH"

  local asar_path="$APP_PATH/Contents/Resources/app.asar"
  local asar_unpacked_path="$APP_PATH/Contents/Resources/app.asar.unpacked"
  local plist_path="$APP_PATH/Contents/Info.plist"
  local app_executable="$APP_PATH/Contents/MacOS/Codex"
  local live_asar_header_hash live_plist_hash

  [[ -f "$asar_path" ]] || die "Missing app archive: $asar_path"
  [[ -d "$asar_unpacked_path" ]] || die "Missing unpacked app resources: $asar_unpacked_path"
  [[ -f "$plist_path" ]] || die "Missing Info.plist: $plist_path"

  if ! $DRY_RUN && ps -axo command= | grep -Fqx -- "$app_executable"; then
    die "Codex is running. Quit it before patching."
  fi

  TMP_DIR="$(mktemp -d /tmp/codex-width.XXXXXX)"
  local extract_dir="$TMP_DIR/app"
  local new_asar="$TMP_DIR/app.asar"
  local unpack_pattern

  live_asar_header_hash="$(compute_asar_header_hash "$asar_path")"
  live_plist_hash="$(extract_plist_hash "$plist_path")"
  unpack_pattern="$(build_unpack_pattern "$asar_unpacked_path")"

  echo -e "Codex app:   ${CYAN}$APP_PATH${NC}"
  echo -e "Target width:${CYAN} ${DESIRED_WIDTH}${NC}"
  echo -e "Unpack glob: ${DIM}${unpack_pattern}${NC}"
  echo -e "${DIM}Extracting app bundle...${NC}"
  npx -y @electron/asar extract "$asar_path" "$extract_dir" >/dev/null

  local css_file
  css_file="$(find "$extract_dir/webview/assets" -maxdepth 1 -name 'index-*.css' | head -n1)"
  [[ -n "$css_file" ]] || die "Could not find Codex webview CSS inside app.asar"

  local current_width
  current_width="$(extract_current_width "$css_file")"
  [[ -n "$current_width" ]] || die "Could not read current Codex content width from $css_file"

  echo -e "Current width:${DIM} ${current_width}${NC}"
  if [[ "$live_asar_header_hash" == "$live_plist_hash" ]]; then
    echo -e "ASAR header:  ${DIM}in sync${NC}"
  else
    echo -e "ASAR header:  ${YELLOW}stale Info.plist entry detected${NC}"
  fi

  local new_hash
  local patched_width="$current_width"
  local needs_width_patch=false

  if [[ "$current_width" == "$DESIRED_WIDTH" ]]; then
    if [[ "$live_asar_header_hash" == "$live_plist_hash" ]]; then
      echo -e "${GREEN}Already patched.${NC}"
      return 0
    fi

    new_hash="$live_asar_header_hash"
    echo -e "${YELLOW}Width already patched, repairing Electron ASAR hash metadata only.${NC}"
    if $DRY_RUN; then
      echo -e "${GREEN}Dry run successful.${NC} Would repair the stale Electron ASAR hash metadata."
      return 0
    fi
  else
    needs_width_patch=true
    patch_css_width "$css_file"
    patched_width="$(extract_current_width "$css_file")"
    [[ "$patched_width" == "$DESIRED_WIDTH" ]] || die "Patch verification failed: expected $DESIRED_WIDTH, got ${patched_width:-<empty>}"

    if $DRY_RUN; then
      local suffix=""
      if [[ "$live_asar_header_hash" != "$live_plist_hash" ]]; then
        suffix=" and repair the stale Electron ASAR hash metadata"
      fi
      echo -e "${GREEN}Dry run successful.${NC} Would patch ${DIM}$current_width${NC} -> ${CYAN}$patched_width${NC}${suffix}."
      return 0
    fi

    echo -e "${DIM}Repacking app.asar...${NC}"
    npx -y @electron/asar pack --unpack="$unpack_pattern" "$extract_dir" "$new_asar" >/dev/null

    local new_asar_unpacked="$new_asar.unpacked"
    [[ -d "$new_asar_unpacked" ]] || die "Repacked app.asar.unpacked was not created"

    new_hash="$(compute_asar_header_hash "$new_asar")"
    [[ -n "$new_hash" ]] || die "Failed to compute new app.asar header hash"

    echo -e "${DIM}Replacing app.asar...${NC}"
    cp "$new_asar" "$asar_path"

    echo -e "${DIM}Replacing app.asar.unpacked...${NC}"
    rm -rf "$asar_unpacked_path"
    ditto "$new_asar_unpacked" "$asar_unpacked_path"
  fi

  echo -e "${DIM}Updating Electron ASAR integrity hash...${NC}"
  /usr/libexec/PlistBuddy -c "Set :ElectronAsarIntegrity:Resources/app.asar:hash $new_hash" "$plist_path"

  if [[ "${CODEX_SKIP_CODESIGN:-0}" == "1" ]]; then
    echo -e "${YELLOW}Skipping codesign because CODEX_SKIP_CODESIGN=1${NC}"
  else
    require_command codesign
    refresh_integrity_digest_if_needed "$APP_PATH"
    echo -e "${DIM}Re-signing Codex.app...${NC}"
    codesign --force --deep --sign - "$APP_PATH" >/dev/null
    echo -e "${DIM}Verifying code signature...${NC}"
    codesign --verify --deep --strict "$APP_PATH" >/dev/null
  fi

  if [[ "${CODEX_SKIP_UNQUARANTINE:-0}" == "1" ]]; then
    echo -e "${YELLOW}Skipping quarantine removal because CODEX_SKIP_UNQUARANTINE=1${NC}"
  else
    require_command xattr
    echo -e "${DIM}Removing quarantine attribute...${NC}"
    xattr -dr com.apple.quarantine "$APP_PATH" 2>/dev/null || true
  fi

  local actual_hash plist_hash
  actual_hash="$(compute_asar_header_hash "$asar_path")"
  plist_hash="$(extract_plist_hash "$plist_path")"

  [[ "$actual_hash" == "$new_hash" ]] || die "app.asar header hash mismatch after replacement"
  [[ "$plist_hash" == "$new_hash" ]] || die "Info.plist hash mismatch after replacement"

  echo -e "${GREEN}Patched Codex Desktop width.${NC}"
  echo -e "New width:   ${CYAN}$patched_width${NC}"
}

main "$@"
imajes · 5 months ago

that's pretty tight, but -- codex peeps -- can we have this as a setting please? :)

etraut-openai contributor · 3 months ago

This feature request hasn't received enough upvotes, so I'm going to close it.

AbdelrahmanHafez · 3 months ago

Ah, shame. I was really looking forward to it