ChatGPT — UltraWide Pro

Expands ChatGPT’s conversation layout to fully utilize wide screens by removing restrictive width clamps and enabling an ultra-wide, full-viewport reading experience. Includes adaptive auto-mode, adjustable maximum width caps, left-alignment normalization, resilient SPA navigation handling, self-healing stylesheet injection, and native Tampermonkey settings.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         ChatGPT — UltraWide Pro
// @namespace    https://www.instagram.com/jsm.ig/
// @version      2026.03.13
// @author       Jonas
// @description  Expands ChatGPT’s conversation layout to fully utilize wide screens by removing restrictive width clamps and enabling an ultra-wide, full-viewport reading experience. Includes adaptive auto-mode, adjustable maximum width caps, left-alignment normalization, resilient SPA navigation handling, self-healing stylesheet injection, and native Tampermonkey settings.
// @license      MIT
// @match        https://chatgpt.com/*
// @match        https://www.chatgpt.com/*
// @icon         https://cdn.jsdelivr.net/gh/simple-icons/simple-icons/icons/openai.svg
// @run-at       document-start
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_addValueChangeListener
// @grant        GM_removeValueChangeListener
// ==/UserScript==

(() => {
'use strict';

const VERSION = '2026.03.12-Settings.2';
const STYLE_ID = 'ml-ultrawide-pro-style';
const STORAGE_KEY = 'ml_ultrawide_pro_v7';

const ATTR = Object.freeze({
  active: 'data-ml-active',
  unclamp: 'data-ml-unclamp',
  left: 'data-ml-left',
  cap: 'data-ml-cap',
  auto: 'data-ml-auto',
  touch: 'data-ml-touch'
});

const CONFIG = Object.freeze({
  gutters: { vw: 2, px: 22 },
  throttleMs: 100,
  idleTimeoutMs: 220,
  pollMs: 2600,
  headGuardMs: 1800,
  moDebounceMs: 80,
  moBurstMax: 18,
  moBurstWindowMs: 1400,
  auto: {
    enabled: true,
    minWidth: 1180,
    disableOnTouch: true,
    disableOnSmallHeight: 620
  },
  caps: ['none', '1600', '2000', '2400', '3000', '3600'],
  keys: {
    toggleAll:     { alt: true, shift: false, ctrl: false, key: 'o' },
    toggleUnclamp: { alt: true, shift: false, ctrl: false, key: 'u' },
    toggleLeft:    { alt: true, shift: false, ctrl: false, key: 'l' },
    cycleCap:      { alt: true, shift: false, ctrl: false, key: 'm' },
    toggleAuto:    { alt: true, shift: false, ctrl: false, key: 'a' }
  }
});

const DEFAULT_PREFS = Object.freeze({
  active: true,
  unclamp: true,
  left: true,
  cap: 'none',
  auto: CONFIG.auto.enabled,
  autoMinWidth: CONFIG.auto.minWidth,
  autoDisableOnTouch: CONFIG.auto.disableOnTouch,
  autoDisableOnSmallHeight: CONFIG.auto.disableOnSmallHeight
});

const SELECTORS = Object.freeze({
  appShell: [
    'html',
    'body',
    '#__next',
    'main',
    '[role="main"]',
    '[data-testid="stretched-app"]',
    '[data-testid="main-app"]',
    '[data-testid="app-root"]',
    '[data-testid="chat-layout"]',
    '[data-testid="conversation-container"]',
    '[data-testid="conversation-view"]'
  ],
  unclampContainers: [
    'main [class*="max-w"]',
    'main .container',
    'main .prose',
    'main .markdown',
    'main .content',
    'main .layout',
    'main .chat',
    'main .conversation-turns',
    '[data-testid="conversation-turns"]',
    '[data-testid="conversation-view"]',
    '[data-testid="conversation-content"]',
    '[data-testid="conversation-container"]',
    '[class*="max-w-"]',
    '[class*="max-w-screen"]'
  ],
  conversationTurns: [
    '[data-testid="conversation-turn"]',
    '[data-message-author-role]',
    '.message',
    '.chat-message',
    '.chat-entry',
    '.bubble',
    '.text-message'
  ],
  conversationTurnInner: [
    '[data-testid="conversation-turn"] > div',
    '[data-message-author-role] > div'
  ],
  markdownBlocks: [
    '[data-message-author-role] .markdown',
    '[data-message-author-role] [class*="prose"]',
    '[data-testid="conversation-turn"] .markdown',
    '[data-testid="conversation-turn"] [class*="prose"]',
    '.chat-message .markdown'
  ],
  composerInputs: [
    '[data-testid="composer:input"]',
    'form textarea',
    'form [contenteditable="true"][role="textbox"]',
    '[aria-label*="message" i]',
    '[aria-label*="prompt" i]',
    '[aria-label*="send a message" i]'
  ],
  sidebarLike: [
    '[aria-label*="sidebar" i]',
    '[data-testid*="sidebar" i]'
  ]
});

const state = {
  started: false,
  ready: false,

  active: DEFAULT_PREFS.active,
  unclamp: DEFAULT_PREFS.unclamp,
  left: DEFAULT_PREFS.left,
  cap: DEFAULT_PREFS.cap,
  auto: DEFAULT_PREFS.auto,
  autoMinWidth: DEFAULT_PREFS.autoMinWidth,
  autoDisableOnTouch: DEFAULT_PREFS.autoDisableOnTouch,
  autoDisableOnSmallHeight: DEFAULT_PREFS.autoDisableOnSmallHeight,

  href: '',
  lastApplyTs: 0,
  lastCssKey: '',
  lastEnvKey: '',
  lastMountedTs: 0,

  scheduled: false,
  idleId: null,
  rafId: 0,
  resizeRaf: 0,
  moTimer: 0,
  moBurstStart: 0,
  moBurstCount: 0,

  sheet: null,
  docMo: null,
  headMo: null,
  bootstrapMo: null,

  pollTimer: 0,
  guardTimer: 0,

  originalPushState: null,
  originalReplaceState: null,

  handlers: Object.create(null),
  valueListeners: []
};

const now = () => performance.now();

function getStoreKey(key) {
  return `${STORAGE_KEY}.${key}`;
}

function getHead() {
  return document.head || document.getElementsByTagName('head')[0] || null;
}

function joinSelectors(list) {
  return list.join(',');
}

function isTouchLike() {
  try {
    const coarse = typeof matchMedia === 'function' && matchMedia('(pointer: coarse)').matches;
    const maxTouchPoints = typeof navigator !== 'undefined' ? Number(navigator.maxTouchPoints || 0) : 0;
    return coarse || maxTouchPoints > 0;
  } catch {
    return false;
  }
}

function envKey() {
  const w = window.innerWidth || 0;
  const h = window.innerHeight || 0;
  const dpr = window.devicePixelRatio || 1;
  const touch = isTouchLike() ? 't' : 'd';
  const full = document.fullscreenElement ? 'fs1' : 'fs0';
  return `${w}x${h}@${dpr}:${touch}:${full}:${state.auto ? 'a1' : 'a0'}:${state.autoMinWidth}:${state.autoDisableOnTouch ? 1 : 0}:${state.autoDisableOnSmallHeight}`;
}

function cssKey() {
  return [
    state.active ? 1 : 0,
    state.unclamp ? 1 : 0,
    state.left ? 1 : 0,
    state.cap,
    state.auto ? 1 : 0,
    state.autoMinWidth,
    state.autoDisableOnTouch ? 1 : 0,
    state.autoDisableOnSmallHeight
  ].join('|');
}

function normalizeCap(v) {
  const s = String(v ?? '').trim().toLowerCase();
  return CONFIG.caps.includes(s) ? s : DEFAULT_PREFS.cap;
}

function normalizeBool(v, fallback) {
  return typeof v === 'boolean' ? v : fallback;
}

function normalizeInt(v, fallback, min, max) {
  const n = Number(v);
  if (!Number.isFinite(n)) return fallback;
  const clamped = Math.min(Math.max(Math.round(n), min), max);
  return clamped;
}

function loadPrefs() {
  try {
    state.active = normalizeBool(GM_getValue(getStoreKey('active'), DEFAULT_PREFS.active), DEFAULT_PREFS.active);
    state.unclamp = normalizeBool(GM_getValue(getStoreKey('unclamp'), DEFAULT_PREFS.unclamp), DEFAULT_PREFS.unclamp);
    state.left = normalizeBool(GM_getValue(getStoreKey('left'), DEFAULT_PREFS.left), DEFAULT_PREFS.left);
    state.auto = normalizeBool(GM_getValue(getStoreKey('auto'), DEFAULT_PREFS.auto), DEFAULT_PREFS.auto);
    state.cap = normalizeCap(GM_getValue(getStoreKey('cap'), DEFAULT_PREFS.cap));
    state.autoMinWidth = normalizeInt(GM_getValue(getStoreKey('autoMinWidth'), DEFAULT_PREFS.autoMinWidth), DEFAULT_PREFS.autoMinWidth, 640, 5000);
    state.autoDisableOnTouch = normalizeBool(GM_getValue(getStoreKey('autoDisableOnTouch'), DEFAULT_PREFS.autoDisableOnTouch), DEFAULT_PREFS.autoDisableOnTouch);
    state.autoDisableOnSmallHeight = normalizeInt(GM_getValue(getStoreKey('autoDisableOnSmallHeight'), DEFAULT_PREFS.autoDisableOnSmallHeight), DEFAULT_PREFS.autoDisableOnSmallHeight, 0, 3000);
  } catch {}
}

function savePref(key, value) {
  try {
    GM_setValue(getStoreKey(key), value);
  } catch {}
}

function saveAllPrefs() {
  savePref('active', state.active);
  savePref('unclamp', state.unclamp);
  savePref('left', state.left);
  savePref('auto', state.auto);
  savePref('cap', state.cap);
  savePref('autoMinWidth', state.autoMinWidth);
  savePref('autoDisableOnTouch', state.autoDisableOnTouch);
  savePref('autoDisableOnSmallHeight', state.autoDisableOnSmallHeight);
}

function reloadPage() {
  try {
    window.location.reload();
  } catch {
    location.href = location.href;
  }
}

function resetPrefs() {
  state.active = DEFAULT_PREFS.active;
  state.unclamp = DEFAULT_PREFS.unclamp;
  state.left = DEFAULT_PREFS.left;
  state.cap = DEFAULT_PREFS.cap;
  state.auto = DEFAULT_PREFS.auto;
  state.autoMinWidth = DEFAULT_PREFS.autoMinWidth;
  state.autoDisableOnTouch = DEFAULT_PREFS.autoDisableOnTouch;
  state.autoDisableOnSmallHeight = DEFAULT_PREFS.autoDisableOnSmallHeight;
  saveAllPrefs();
}

function shouldEnableWide() {
  if (!state.active) return false;
  if (!state.unclamp) return false;
  if (!state.auto) return true;

  const w = window.innerWidth || 0;
  const h = window.innerHeight || 0;

  if (state.autoDisableOnTouch && isTouchLike()) return false;
  if (state.autoDisableOnSmallHeight > 0 && h > 0 && h < state.autoDisableOnSmallHeight) return false;

  return w >= state.autoMinWidth;
}

function clearRootFlags() {
  const root = document.documentElement;
  if (!root) return;
  root.removeAttribute(ATTR.active);
  root.removeAttribute(ATTR.unclamp);
  root.removeAttribute(ATTR.left);
  root.removeAttribute(ATTR.cap);
  root.removeAttribute(ATTR.auto);
  root.removeAttribute(ATTR.touch);
}

function setRootFlags() {
  const root = document.documentElement;
  if (!root) return;

  if (state.active) root.setAttribute(ATTR.active, '1');
  else root.removeAttribute(ATTR.active);

  if (state.active) root.setAttribute(ATTR.cap, state.cap);
  else root.removeAttribute(ATTR.cap);

  if (state.active && state.left) root.setAttribute(ATTR.left, '1');
  else root.removeAttribute(ATTR.left);

  if (state.auto) root.setAttribute(ATTR.auto, '1');
  else root.removeAttribute(ATTR.auto);

  if (isTouchLike()) root.setAttribute(ATTR.touch, '1');
  else root.removeAttribute(ATTR.touch);

  if (shouldEnableWide()) root.setAttribute(ATTR.unclamp, '1');
  else root.removeAttribute(ATTR.unclamp);
}

function buildCss() {
  const appShell = joinSelectors(SELECTORS.appShell);
  const unclampContainers = joinSelectors(SELECTORS.unclampContainers);
  const conversationTurns = joinSelectors(SELECTORS.conversationTurns);
  const conversationTurnInner = joinSelectors(SELECTORS.conversationTurnInner);
  const markdownBlocks = joinSelectors(SELECTORS.markdownBlocks);
  const composerInputs = joinSelectors(SELECTORS.composerInputs);
  const sidebarLike = joinSelectors(SELECTORS.sidebarLike);

  return String.raw`
:root[${ATTR.active}="1"]{
  --ml-gutter:min(${CONFIG.gutters.vw}vw,${CONFIG.gutters.px}px);
  --ml-cap:none;
}

:root[${ATTR.active}="1"][${ATTR.cap}="none"]{--ml-cap:none}
:root[${ATTR.active}="1"][${ATTR.cap}="1600"]{--ml-cap:1600px}
:root[${ATTR.active}="1"][${ATTR.cap}="2000"]{--ml-cap:2000px}
:root[${ATTR.active}="1"][${ATTR.cap}="2400"]{--ml-cap:2400px}
:root[${ATTR.active}="1"][${ATTR.cap}="3000"]{--ml-cap:3000px}
:root[${ATTR.active}="1"][${ATTR.cap}="3600"]{--ml-cap:3600px}

:root[${ATTR.unclamp}="1"] html,
:root[${ATTR.unclamp}="1"] body{
  width:100% !important;
  max-width:none !important;
  overflow-x:visible !important;
}

:root[${ATTR.unclamp}="1"] ${appShell}{
  max-width:none !important;
}

:root[${ATTR.unclamp}="1"] #__next,
:root[${ATTR.unclamp}="1"] main,
:root[${ATTR.unclamp}="1"] [role="main"],
:root[${ATTR.unclamp}="1"] [data-testid="stretched-app"],
:root[${ATTR.unclamp}="1"] [data-testid="main-app"],
:root[${ATTR.unclamp}="1"] [data-testid="app-root"],
:root[${ATTR.unclamp}="1"] [data-testid="chat-layout"],
:root[${ATTR.unclamp}="1"] [data-testid="conversation-container"],
:root[${ATTR.unclamp}="1"] [data-testid="conversation-view"]{
  width:100% !important;
  max-width:100vw !important;
  margin:0 !important;
  padding-left:var(--ml-gutter) !important;
  padding-right:var(--ml-gutter) !important;
  box-sizing:border-box !important;
}

:root[${ATTR.unclamp}="1"] ${unclampContainers}{
  width:100% !important;
  max-width:none !important;
  margin-left:0 !important;
  margin-right:0 !important;
  box-sizing:border-box !important;
}

:root[${ATTR.unclamp}="1"] ${conversationTurns}{
  width:100% !important;
  max-width:100% !important;
  margin-left:0 !important;
  margin-right:0 !important;
  box-sizing:border-box !important;
}

:root[${ATTR.unclamp}="1"] ${conversationTurnInner}{
  width:100% !important;
  max-width:var(--ml-cap) !important;
  margin-left:auto !important;
  margin-right:auto !important;
  box-sizing:border-box !important;
}

:root[${ATTR.unclamp}="1"][${ATTR.cap}="none"] ${conversationTurnInner}{
  max-width:none !important;
}

:root[${ATTR.unclamp}="1"] ${markdownBlocks}{
  max-width:none !important;
}

:root[${ATTR.unclamp}="1"] pre,
:root[${ATTR.unclamp}="1"] .markdown pre{
  width:100% !important;
  max-width:100% !important;
  max-height:none !important;
  overflow-x:auto !important;
  overflow-y:visible !important;
  white-space:pre !important;
  box-sizing:border-box !important;
}

:root[${ATTR.unclamp}="1"] code,
:root[${ATTR.unclamp}="1"] .markdown code{
  white-space:pre !important;
}

:root[${ATTR.unclamp}="1"] table{
  display:block !important;
  width:100% !important;
  max-width:100% !important;
  overflow-x:auto !important;
  box-sizing:border-box !important;
}

:root[${ATTR.unclamp}="1"] img,
:root[${ATTR.unclamp}="1"] svg,
:root[${ATTR.unclamp}="1"] video,
:root[${ATTR.unclamp}="1"] canvas,
:root[${ATTR.unclamp}="1"] figure,
:root[${ATTR.unclamp}="1"] iframe{
  max-width:100% !important;
  height:auto !important;
}

:root[${ATTR.unclamp}="1"] ${sidebarLike}{
  max-width:none !important;
}

:root[${ATTR.left}="1"] ${composerInputs}{
  text-align:left !important;
  direction:ltr !important;
  justify-content:flex-start !important;
  align-items:flex-start !important;
}

:root[${ATTR.left}="1"] textarea,
:root[${ATTR.left}="1"] [contenteditable="true"][role="textbox"]{
  text-align:left !important;
}

:root[${ATTR.left}="1"] textarea::placeholder{
  text-align:left !important;
}

:root[${ATTR.left}="1"] ${markdownBlocks}{
  text-align:left !important;
}

@media print{
  :root[${ATTR.unclamp}="1"] html,
  :root[${ATTR.unclamp}="1"] body,
  :root[${ATTR.unclamp}="1"] #__next,
  :root[${ATTR.unclamp}="1"] main,
  :root[${ATTR.unclamp}="1"] [role="main"]{
    width:100% !important;
    max-width:none !important;
    text-align:left !important;
  }
}
`.trim();
}

function isStyleMounted() {
  try {
    if (state.sheet && Array.isArray(document.adoptedStyleSheets) && document.adoptedStyleSheets.includes(state.sheet)) {
      return true;
    }
  } catch {}
  const head = getHead();
  const el = document.getElementById(STYLE_ID);
  return !!(head && el && head.contains(el));
}

function mountCss() {
  const head = getHead();
  if (!head) return false;

  const css = buildCss();

  try {
    if ('adoptedStyleSheets' in Document.prototype && 'replaceSync' in CSSStyleSheet.prototype) {
      if (!state.sheet) state.sheet = new CSSStyleSheet();
      state.sheet.replaceSync(css);

      const sheets = document.adoptedStyleSheets || [];
      if (!sheets.includes(state.sheet)) {
        document.adoptedStyleSheets = [...sheets, state.sheet];
      }

      const fallback = document.getElementById(STYLE_ID);
      if (fallback && fallback.parentNode) fallback.parentNode.removeChild(fallback);

      state.lastCssKey = cssKey();
      state.lastMountedTs = now();
      return true;
    }
  } catch {}

  let el = document.getElementById(STYLE_ID);
  if (!el) {
    el = document.createElement('style');
    el.id = STYLE_ID;
    el.type = 'text/css';
    head.appendChild(el);
  }
  if (el.textContent !== css) el.textContent = css;

  state.lastCssKey = cssKey();
  state.lastMountedTs = now();
  return true;
}

function unmountCss() {
  try {
    if (state.sheet && Array.isArray(document.adoptedStyleSheets) && document.adoptedStyleSheets.includes(state.sheet)) {
      document.adoptedStyleSheets = document.adoptedStyleSheets.filter(s => s !== state.sheet);
    }
  } catch {}

  const el = document.getElementById(STYLE_ID);
  if (el && el.parentNode) el.parentNode.removeChild(el);
}

function cleanupVisuals() {
  clearRootFlags();
  unmountCss();
}

function apply() {
  if (!document.documentElement) return;

  if (!state.active) {
    cleanupVisuals();
    state.lastApplyTs = now();
    state.ready = true;
    return;
  }

  const nextCssKey = cssKey();
  if (nextCssKey !== state.lastCssKey || !isStyleMounted()) {
    if (!mountCss()) return;
  }

  setRootFlags();

  state.lastApplyTs = now();
  state.ready = true;
}

function cancelScheduled() {
  state.scheduled = false;

  if (state.rafId) {
    cancelAnimationFrame(state.rafId);
    state.rafId = 0;
  }

  if (state.idleId != null && typeof cancelIdleCallback === 'function') {
    try { cancelIdleCallback(state.idleId); } catch {}
    state.idleId = null;
  }
}

function schedule(force = false) {
  const t = now();
  if (!force && t - state.lastApplyTs < CONFIG.throttleMs) return;
  if (state.scheduled) return;

  state.scheduled = true;

  const run = () => {
    state.scheduled = false;
    state.rafId = 0;
    state.idleId = null;
    apply();
  };

  if (typeof requestIdleCallback === 'function') {
    try {
      state.idleId = requestIdleCallback(run, { timeout: CONFIG.idleTimeoutMs });
      return;
    } catch {}
  }

  state.rafId = requestAnimationFrame(() => Promise.resolve().then(run));
}

function relevantMutation(mutations) {
  for (let i = 0; i < mutations.length; i++) {
    const m = mutations[i];
    if (!m) continue;
    if (m.type === 'childList') {
      if (m.addedNodes && m.addedNodes.length) return true;
      if (m.removedNodes && m.removedNodes.length) return true;
    }
    if (m.type === 'attributes') return true;
  }
  return false;
}

function burstGuardedSchedule() {
  const t = now();
  if (!state.moBurstStart || t - state.moBurstStart > CONFIG.moBurstWindowMs) {
    state.moBurstStart = t;
    state.moBurstCount = 0;
  }
  state.moBurstCount++;
  if (state.moBurstCount > CONFIG.moBurstMax) return;
  schedule(false);
}

function onMutations(mutations) {
  if (!relevantMutation(mutations)) return;
  if (state.moTimer) return;
  state.moTimer = window.setTimeout(() => {
    state.moTimer = 0;
    burstGuardedSchedule();
  }, CONFIG.moDebounceMs);
}

function onHrefMaybeChanged() {
  const href = location.href;
  if (href !== state.href) {
    state.href = href;
    schedule(true);
  } else {
    schedule(false);
  }
}

function wrapHistoryMethod(original) {
  return function wrappedHistoryMethod(...args) {
    const out = original.apply(this, args);
    Promise.resolve().then(onHrefMaybeChanged);
    return out;
  };
}

function connectHeadObserver() {
  const head = getHead();
  if (!head) return false;

  if (state.headMo) {
    try { state.headMo.disconnect(); } catch {}
  }

  try {
    state.headMo = new MutationObserver(() => {
      if (!isStyleMounted()) schedule(true);
    });
    state.headMo.observe(head, { childList: true, subtree: true, attributes: false });
    return true;
  } catch {
    state.headMo = null;
    return false;
  }
}

function initObservers() {
  try {
    if (document.documentElement) {
      state.docMo = new MutationObserver(onMutations);
      state.docMo.observe(document.documentElement, {
        childList: true,
        subtree: true,
        attributes: false
      });
    }
  } catch {}

  connectHeadObserver();

  if (!getHead() && document.documentElement) {
    try {
      state.bootstrapMo = new MutationObserver(() => {
        if (getHead()) {
          connectHeadObserver();
          if (state.bootstrapMo) {
            try { state.bootstrapMo.disconnect(); } catch {}
            state.bootstrapMo = null;
          }
          schedule(true);
        }
      });
      state.bootstrapMo.observe(document.documentElement, {
        childList: true,
        subtree: true,
        attributes: false
      });
    } catch {}
  }
}

function disconnectObservers() {
  try { if (state.docMo) state.docMo.disconnect(); } catch {}
  try { if (state.headMo) state.headMo.disconnect(); } catch {}
  try { if (state.bootstrapMo) state.bootstrapMo.disconnect(); } catch {}
  state.docMo = null;
  state.headMo = null;
  state.bootstrapMo = null;
}

function initNavHooks() {
  try {
    if (!state.originalPushState) state.originalPushState = history.pushState;
    if (!state.originalReplaceState) state.originalReplaceState = history.replaceState;

    if (history.pushState === state.originalPushState) {
      history.pushState = wrapHistoryMethod(state.originalPushState);
    }
    if (history.replaceState === state.originalReplaceState) {
      history.replaceState = wrapHistoryMethod(state.originalReplaceState);
    }
  } catch {}
}

function restoreNavHooks() {
  try {
    if (state.originalPushState && history.pushState !== state.originalPushState) {
      history.pushState = state.originalPushState;
    }
  } catch {}
  try {
    if (state.originalReplaceState && history.replaceState !== state.originalReplaceState) {
      history.replaceState = state.originalReplaceState;
    }
  } catch {}
}

function clearTimers() {
  if (state.pollTimer) {
    clearInterval(state.pollTimer);
    state.pollTimer = 0;
  }
  if (state.guardTimer) {
    clearInterval(state.guardTimer);
    state.guardTimer = 0;
  }
  if (state.moTimer) {
    clearTimeout(state.moTimer);
    state.moTimer = 0;
  }
  if (state.resizeRaf) {
    cancelAnimationFrame(state.resizeRaf);
    state.resizeRaf = 0;
  }
  cancelScheduled();
}

function isMatch(mods, e) {
  if (!!mods.alt !== !!e.altKey) return false;
  if (!!mods.shift !== !!e.shiftKey) return false;
  if (!!mods.ctrl !== !!(e.ctrlKey || e.metaKey)) return false;
  return String(e.key || '').toLowerCase() === String(mods.key || '').toLowerCase();
}

function toggleAll() {
  state.active = !state.active;
  savePref('active', state.active);
  schedule(true);
}

function toggleUnclamp() {
  state.unclamp = !state.unclamp;
  savePref('unclamp', state.unclamp);
  schedule(true);
}

function toggleLeft() {
  state.left = !state.left;
  savePref('left', state.left);
  schedule(true);
}

function toggleAuto() {
  state.auto = !state.auto;
  savePref('auto', state.auto);
  schedule(true);
}

function cycleCap() {
  const idx = CONFIG.caps.indexOf(state.cap);
  state.cap = CONFIG.caps[(idx + 1 + CONFIG.caps.length) % CONFIG.caps.length] || 'none';
  savePref('cap', state.cap);
  schedule(true);
}

function onKeydown(e) {
  if (!e || e.defaultPrevented) return;

  const tag = e.target && e.target.tagName ? String(e.target.tagName).toLowerCase() : '';
  const isTyping = tag === 'textarea' || tag === 'input' || !!(e.target && e.target.isContentEditable);
  const k = String(e.key || '').toLowerCase();
  const isOurCombo = e.altKey && (k === 'o' || k === 'u' || k === 'l' || k === 'm' || k === 'a');

  if (isTyping && !isOurCombo) return;

  if (isMatch(CONFIG.keys.toggleAll, e)) { e.preventDefault(); toggleAll(); return; }
  if (isMatch(CONFIG.keys.toggleUnclamp, e)) { e.preventDefault(); toggleUnclamp(); return; }
  if (isMatch(CONFIG.keys.toggleLeft, e)) { e.preventDefault(); toggleLeft(); return; }
  if (isMatch(CONFIG.keys.cycleCap, e)) { e.preventDefault(); cycleCap(); return; }
  if (isMatch(CONFIG.keys.toggleAuto, e)) { e.preventDefault(); toggleAuto(); return; }
}

function onResize() {
  if (state.resizeRaf) cancelAnimationFrame(state.resizeRaf);
  state.resizeRaf = requestAnimationFrame(() => {
    state.resizeRaf = 0;
    const k = envKey();
    if (k !== state.lastEnvKey) {
      state.lastEnvKey = k;
      schedule(true);
    }
  });
}

function syncFromStorage() {
  const before = `${state.active}|${state.unclamp}|${state.left}|${state.cap}|${state.auto}|${state.autoMinWidth}|${state.autoDisableOnTouch}|${state.autoDisableOnSmallHeight}`;
  loadPrefs();
  const after = `${state.active}|${state.unclamp}|${state.left}|${state.cap}|${state.auto}|${state.autoMinWidth}|${state.autoDisableOnTouch}|${state.autoDisableOnSmallHeight}`;
  if (before !== after) {
    state.lastEnvKey = envKey();
    schedule(true);
  }
}

function onVisibilityChange() {
  if (!document.hidden) schedule(true);
}

function onPageShow() {
  schedule(true);
}

function onPageHide() {
  if (state.moTimer) {
    clearTimeout(state.moTimer);
    state.moTimer = 0;
  }
  cancelScheduled();
}

function onFocus() {
  schedule(true);
}

function onPopState() {
  onHrefMaybeChanged();
}

function onOrientationChange() {
  schedule(true);
}

function onFullscreenChange() {
  const k = envKey();
  if (k !== state.lastEnvKey) state.lastEnvKey = k;
  schedule(true);
}

function bindListeners() {
  state.handlers.keydown = onKeydown;
  state.handlers.resize = onResize;
  state.handlers.visibilitychange = onVisibilityChange;
  state.handlers.pageshow = onPageShow;
  state.handlers.pagehide = onPageHide;
  state.handlers.focus = onFocus;
  state.handlers.popstate = onPopState;
  state.handlers.orientationchange = onOrientationChange;
  state.handlers.fullscreenchange = onFullscreenChange;

  window.addEventListener('keydown', state.handlers.keydown, true);
  window.addEventListener('resize', state.handlers.resize, { passive: true });
  document.addEventListener('visibilitychange', state.handlers.visibilitychange, { passive: true });
  window.addEventListener('pageshow', state.handlers.pageshow, { passive: true });
  window.addEventListener('pagehide', state.handlers.pagehide, { passive: true });
  window.addEventListener('focus', state.handlers.focus, { passive: true });
  window.addEventListener('popstate', state.handlers.popstate, { passive: true });
  window.addEventListener('orientationchange', state.handlers.orientationchange, { passive: true });
  document.addEventListener('fullscreenchange', state.handlers.fullscreenchange, { passive: true });
}

function unbindListeners() {
  if (state.handlers.keydown) window.removeEventListener('keydown', state.handlers.keydown, true);
  if (state.handlers.resize) window.removeEventListener('resize', state.handlers.resize, { passive: true });
  if (state.handlers.visibilitychange) document.removeEventListener('visibilitychange', state.handlers.visibilitychange, { passive: true });
  if (state.handlers.pageshow) window.removeEventListener('pageshow', state.handlers.pageshow, { passive: true });
  if (state.handlers.pagehide) window.removeEventListener('pagehide', state.handlers.pagehide, { passive: true });
  if (state.handlers.focus) window.removeEventListener('focus', state.handlers.focus, { passive: true });
  if (state.handlers.popstate) window.removeEventListener('popstate', state.handlers.popstate, { passive: true });
  if (state.handlers.orientationchange) window.removeEventListener('orientationchange', state.handlers.orientationchange, { passive: true });
  if (state.handlers.fullscreenchange) document.removeEventListener('fullscreenchange', state.handlers.fullscreenchange, { passive: true });

  state.handlers = Object.create(null);
}

function initGuards() {
  state.guardTimer = window.setInterval(() => {
    if (!state.started || !state.ready) return;

    const currentEnv = envKey();
    if (currentEnv !== state.lastEnvKey) {
      state.lastEnvKey = currentEnv;
      schedule(true);
      return;
    }

    if (state.active && !isStyleMounted()) {
      schedule(true);
      return;
    }

    if (!state.headMo && getHead()) {
      connectHeadObserver();
      schedule(true);
    }
  }, CONFIG.headGuardMs);
}

function menuLabelBool(label, value) {
  return `${value ? '✅' : '❌'} ${label}`;
}

function menuLabelValue(label, value) {
  return `⚙️ ${label}: ${value}`;
}

function promptCap() {
  const current = state.cap;
  const input = prompt(`Set max width cap.\nAllowed: ${CONFIG.caps.join(', ')}\n\nCurrent: ${current}`, current);
  if (input == null) return;
  state.cap = normalizeCap(input);
  savePref('cap', state.cap);
  reloadPage();
}

function promptAutoMinWidth() {
  const current = state.autoMinWidth;
  const input = prompt('Set auto-mode minimum viewport width in pixels.', String(current));
  if (input == null) return;
  state.autoMinWidth = normalizeInt(input, current, 640, 5000);
  savePref('autoMinWidth', state.autoMinWidth);
  state.lastEnvKey = envKey();
  reloadPage();
}

function promptSmallHeightCutoff() {
  const current = state.autoDisableOnSmallHeight;
  const input = prompt('Set auto-mode small-height cutoff in pixels.\nUse 0 to disable.', String(current));
  if (input == null) return;
  state.autoDisableOnSmallHeight = normalizeInt(input, current, 0, 3000);
  savePref('autoDisableOnSmallHeight', state.autoDisableOnSmallHeight);
  state.lastEnvKey = envKey();
  reloadPage();
}

function registerMenu() {
  GM_registerMenuCommand(menuLabelBool('Script enabled', state.active), () => {
    state.active = !state.active;
    savePref('active', state.active);
    reloadPage();
  });

  GM_registerMenuCommand(menuLabelBool('Unclamp enabled', state.unclamp), () => {
    state.unclamp = !state.unclamp;
    savePref('unclamp', state.unclamp);
    reloadPage();
  });

  GM_registerMenuCommand(menuLabelBool('Left alignment enabled', state.left), () => {
    state.left = !state.left;
    savePref('left', state.left);
    reloadPage();
  });

  GM_registerMenuCommand(menuLabelBool('Auto mode enabled', state.auto), () => {
    state.auto = !state.auto;
    savePref('auto', state.auto);
    reloadPage();
  });

  GM_registerMenuCommand(menuLabelValue('Max width cap', state.cap), () => {
    promptCap();
  });

  GM_registerMenuCommand(menuLabelValue('Auto min width', `${state.autoMinWidth}px`), () => {
    promptAutoMinWidth();
  });

  GM_registerMenuCommand(menuLabelBool('Disable on touch', state.autoDisableOnTouch), () => {
    state.autoDisableOnTouch = !state.autoDisableOnTouch;
    savePref('autoDisableOnTouch', state.autoDisableOnTouch);
    state.lastEnvKey = envKey();
    reloadPage();
  });

  GM_registerMenuCommand(menuLabelValue('Small-height cutoff', `${state.autoDisableOnSmallHeight}px`), () => {
    promptSmallHeightCutoff();
  });

  GM_registerMenuCommand('🔄 Cycle max width cap', () => {
    const idx = CONFIG.caps.indexOf(state.cap);
    state.cap = CONFIG.caps[(idx + 1 + CONFIG.caps.length) % CONFIG.caps.length] || 'none';
    savePref('cap', state.cap);
    reloadPage();
  });

  GM_registerMenuCommand('♻️ Reset all settings', () => {
    const ok = confirm('Reset all ChatGPT — UltraWide Pro settings to defaults?');
    if (!ok) return;
    resetPrefs();
    state.lastEnvKey = envKey();
    reloadPage();
  });
}

function initValueSync() {
  const keys = [
    'active',
    'unclamp',
    'left',
    'auto',
    'cap',
    'autoMinWidth',
    'autoDisableOnTouch',
    'autoDisableOnSmallHeight'
  ];

  for (const key of keys) {
    try {
      const id = GM_addValueChangeListener(getStoreKey(key), () => {
        syncFromStorage();
      });
      state.valueListeners.push(id);
    } catch {}
  }
}

function removeValueSync() {
  for (const id of state.valueListeners) {
    try {
      GM_removeValueChangeListener(id);
    } catch {}
  }
  state.valueListeners = [];
}

function initSettings() {
  loadPrefs();
  registerMenu();
  initValueSync();
}

function start() {
  if (state.started) return;

  initSettings();
  state.href = location.href;
  state.lastEnvKey = envKey();

  initNavHooks();
  initObservers();
  bindListeners();
  initGuards();

  state.pollTimer = window.setInterval(onHrefMaybeChanged, CONFIG.pollMs);

  schedule(true);

  state.started = true;
}

function stop() {
  if (!state.started) return;

  clearTimers();
  unbindListeners();
  disconnectObservers();
  restoreNavHooks();
  removeValueSync();
  cleanupVisuals();

  state.started = false;
  state.ready = false;
}

function restart() {
  stop();
  start();
}

start();

Object.defineProperty(window, '__mlUltraWide', {
  value: Object.freeze({
    version: VERSION,
    start,
    stop,
    restart,
    apply: () => schedule(true),
    state: () => ({
      active: state.active,
      unclamp: state.unclamp,
      left: state.left,
      cap: state.cap,
      auto: state.auto,
      autoMinWidth: state.autoMinWidth,
      autoDisableOnTouch: state.autoDisableOnTouch,
      autoDisableOnSmallHeight: state.autoDisableOnSmallHeight,
      started: state.started,
      ready: state.ready,
      href: state.href,
      env: state.lastEnvKey
    }),
    defaults: () => ({ ...DEFAULT_PREFS })
  }),
  configurable: true
});

})();