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.
// ==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
});
})();