Ultra-wide ChatGPT layout with Canvas/Split View support, scoped selectors, adaptive width profiles, settings UI, import/export, SPA-safe reinjection, and safer mutation handling.
// ==UserScript==
// @name UltraWide ChatGPT
// @namespace https://www.instagram.com/jsm.ig/
// @version 2026.05.28.6
// @author jsmdev
// @description Ultra-wide ChatGPT layout with Canvas/Split View support, scoped selectors, adaptive width profiles, settings UI, import/export, SPA-safe reinjection, and safer mutation handling.
// @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
// ==/UserScript==
/*
UltraWide ChatGPT
Version: 2026.05.28.6
Shortcuts
- Alt+O Toggle script
- Alt+U Toggle ultra-wide mode
- Alt+L Toggle left alignment
- Alt+M Cycle max-width cap
- Alt+A Toggle auto mode
- Alt+C Toggle Canvas/Split View tuning
- Alt+S Open settings
- Alt+R Reset settings
Console API
- window.__mlUltraWide.state()
- window.__mlUltraWide.apply()
- window.__mlUltraWide.restart()
- window.__mlUltraWide.openSettings()
- window.__mlUltraWide.set({ cap: '2400', auto: false })
- window.__mlUltraWide.reset()
*/
(() => {
'use strict';
const VERSION = '2026.05.28.6';
const STYLE_ID = 'uwc-style';
const UI_STYLE_ID = 'uwc-ui-style';
const TOAST_ID = 'uwc-toast';
const MODAL_ID = 'uwc-settings-modal';
const STORAGE_KEY = 'uwc.settings.v6';
const LEGACY_STORAGE_KEYS = Object.freeze([
'uwc.settings.v5',
'uwc.settings.v4',
'uwc.settings.v3',
'uwc.settings.v2',
'uwc.settings.v1'
]);
const CAPS = Object.freeze([
'none',
'1200',
'1400',
'1600',
'1800',
'2000',
'2200',
'2400',
'2800',
'3200',
'3600',
'4200'
]);
const ATTR = Object.freeze({
on: 'data-uwc-on',
wide: 'data-uwc-wide',
left: 'data-uwc-left',
cap: 'data-uwc-cap',
canvas: 'data-uwc-canvas',
mounted: 'data-uwc-mounted'
});
const DEFAULTS = Object.freeze({
enabled: true,
wide: true,
left: true,
cap: 'none',
auto: true,
autoMinWidth: 1180,
disableOnTouch: true,
disableBelowHeight: 620,
showToast: true,
composerWide: true,
codeWide: true,
mediaSafe: true,
canvasTuning: true,
balancedSplitView: true,
suppressCanvasMutations: true,
gutterMin: 12,
gutterVw: 1.6,
gutterMax: 28,
toastMs: 1400,
repairIntervalMs: 2600,
mutationDebounceMs: 180,
routePollMs: 900,
optionsCloseOnBackdrop: true
});
const LIMITS = Object.freeze({
autoMinWidth: [640, 10000],
disableBelowHeight: [0, 4000],
gutterMin: [0, 80],
gutterVw: [0, 12],
gutterMax: [0, 160],
toastMs: [300, 10000],
repairIntervalMs: [1000, 15000],
mutationDebounceMs: [0, 2500],
routePollMs: [300, 10000]
});
const KEYS = Object.freeze({
toggleEnabled: { alt: true, ctrl: false, shift: false, key: 'o' },
toggleWide: { alt: true, ctrl: false, shift: false, key: 'u' },
toggleLeft: { alt: true, ctrl: false, shift: false, key: 'l' },
cycleCap: { alt: true, ctrl: false, shift: false, key: 'm' },
toggleAuto: { alt: true, ctrl: false, shift: false, key: 'a' },
toggleCanvas: { alt: true, ctrl: false, shift: false, key: 'c' },
openSettings: { alt: true, ctrl: false, shift: false, key: 's' },
reset: { alt: true, ctrl: false, shift: false, key: 'r' }
});
const CFG = Object.freeze({
maxMutationRecordsToInspect: 40,
maxRemovedNodesToInspect: 20,
canvasProbeIntervalMs: 1200
});
const SEL = Object.freeze({
appRoots: [
'body',
'#__next',
'#root',
'main',
'[role="main"]',
'[data-testid="stretched-app"]',
'[data-testid="main-app"]',
'[data-testid="chat-layout"]'
].join(','),
conversationRoots: [
'[data-testid="conversation-view"]',
'[data-testid="conversation-container"]',
'[data-testid="conversation-turns"]',
'[data-testid="thread"]',
'main [class*="conversation"]',
'main [class*="thread"]'
].join(','),
turns: [
'[data-testid="conversation-turn"]',
'[data-testid^="conversation-turn"]',
'[data-message-author-role]',
'[data-turn]',
'article[data-testid]',
'article[class*="group"]'
].join(','),
turnInner: [
'[data-testid="conversation-turn"] > div',
'[data-testid^="conversation-turn"] > div',
'[data-message-author-role] > div',
'[data-turn] > div',
'article[data-testid] > div',
'article[class*="group"] > div'
].join(','),
markdown: [
'.markdown',
'[class*="markdown"]',
'[class*="prose"]',
'[data-message-author-role] .markdown',
'[data-message-author-role] [class*="prose"]',
'[data-testid="conversation-turn"] .markdown',
'[data-testid="conversation-turn"] [class*="prose"]'
].join(','),
composer: [
'[data-testid="composer"]',
'[data-testid*="composer"]',
'[class*="composer"]',
'footer form',
'footer'
].join(','),
input: [
'[data-testid="composer:input"]',
'[data-testid="composer-input"]',
'[data-testid*="composer"] textarea',
'[data-testid*="composer"] [contenteditable="true"]',
'textarea',
'[contenteditable="true"][role="textbox"]',
'[role="textbox"][contenteditable="true"]'
].join(','),
canvasRoots: [
'[class*="canvas"]',
'[class*="artifact"]',
'[data-testid*="canvas"]',
'[data-testid*="artifact"]',
'[aria-label*="canvas" i]',
'[aria-label*="artifact" i]'
].join(','),
editorRoots: [
'[class*="monaco"]',
'[class*="cm-editor"]',
'[class*="CodeMirror"]',
'[data-language]',
'[data-testid*="code"]',
'[data-testid*="editor"]'
].join(',')
});
const runtime = {
started: false,
href: '',
lastCssKey: '',
lastUiCssKey: '',
lastEnvKey: '',
lastCanvasState: false,
lastCanvasProbe: 0,
rafId: 0,
debounceTimer: 0,
repairTimer: 0,
routeTimer: 0,
toastTimer: 0,
bodyObserver: null,
headObserver: null,
bootstrapObserver: null,
origPushState: null,
origReplaceState: null,
handlers: Object.create(null),
menuRegistered: false,
suppressModalRefresh: false
};
const settings = { ...DEFAULTS };
function hasGmValueApi() {
return typeof GM_getValue === 'function' && typeof GM_setValue === 'function';
}
function readRawStorage(key) {
try {
if (hasGmValueApi()) return GM_getValue(key, null);
} catch (_) {}
try {
const raw = localStorage.getItem(key);
return raw ? JSON.parse(raw) : null;
} catch (_) {
return null;
}
}
function writeRawStorage(key, value) {
try {
if (hasGmValueApi()) {
GM_setValue(key, value);
return true;
}
} catch (_) {}
try {
localStorage.setItem(key, JSON.stringify(value));
return true;
} catch (_) {
return false;
}
}
function clampNumber(value, min, max, fallback) {
const n = Number(value);
if (!Number.isFinite(n)) return fallback;
return Math.min(max, Math.max(min, n));
}
function clampInt(value, min, max, fallback) {
return Math.round(clampNumber(value, min, max, fallback));
}
function normalizeBool(value, fallback) {
return typeof value === 'boolean' ? value : fallback;
}
function normalizeSettings(input) {
const out = { ...DEFAULTS };
if (!input || typeof input !== 'object') return out;
out.enabled = normalizeBool(input.enabled, out.enabled);
out.wide = normalizeBool(input.wide, out.wide);
out.left = normalizeBool(input.left, out.left);
out.cap = CAPS.includes(String(input.cap)) ? String(input.cap) : out.cap;
out.auto = normalizeBool(input.auto, out.auto);
out.autoMinWidth = clampInt(input.autoMinWidth, ...LIMITS.autoMinWidth, out.autoMinWidth);
out.disableOnTouch = normalizeBool(input.disableOnTouch, out.disableOnTouch);
out.disableBelowHeight = clampInt(input.disableBelowHeight, ...LIMITS.disableBelowHeight, out.disableBelowHeight);
out.showToast = normalizeBool(input.showToast, out.showToast);
out.composerWide = normalizeBool(input.composerWide, out.composerWide);
out.codeWide = normalizeBool(input.codeWide, out.codeWide);
out.mediaSafe = normalizeBool(input.mediaSafe, out.mediaSafe);
out.canvasTuning = normalizeBool(input.canvasTuning, out.canvasTuning);
out.balancedSplitView = normalizeBool(input.balancedSplitView, out.balancedSplitView);
out.suppressCanvasMutations = normalizeBool(input.suppressCanvasMutations, out.suppressCanvasMutations);
out.gutterMin = clampInt(input.gutterMin, ...LIMITS.gutterMin, out.gutterMin);
out.gutterVw = clampNumber(input.gutterVw, ...LIMITS.gutterVw, out.gutterVw);
out.gutterMax = clampInt(input.gutterMax, ...LIMITS.gutterMax, out.gutterMax);
out.toastMs = clampInt(input.toastMs, ...LIMITS.toastMs, out.toastMs);
out.repairIntervalMs = clampInt(input.repairIntervalMs, ...LIMITS.repairIntervalMs, out.repairIntervalMs);
out.mutationDebounceMs = clampInt(input.mutationDebounceMs, ...LIMITS.mutationDebounceMs, out.mutationDebounceMs);
out.routePollMs = clampInt(input.routePollMs, ...LIMITS.routePollMs, out.routePollMs);
out.optionsCloseOnBackdrop = normalizeBool(input.optionsCloseOnBackdrop, out.optionsCloseOnBackdrop);
if (out.gutterMax < out.gutterMin) out.gutterMax = out.gutterMin;
return out;
}
function loadSettings() {
const current = readRawStorage(STORAGE_KEY);
if (current) {
Object.assign(settings, normalizeSettings(current));
return;
}
for (const key of LEGACY_STORAGE_KEYS) {
const legacy = readRawStorage(key);
if (legacy) {
Object.assign(settings, normalizeSettings(legacy));
saveSettings();
return;
}
}
}
function saveSettings() {
writeRawStorage(STORAGE_KEY, { ...settings });
}
function getHead() {
return document.head || document.getElementsByTagName('head')[0] || null;
}
function getRoot() {
return document.documentElement || null;
}
function isTouchLike() {
try {
return (
(typeof matchMedia === 'function' && matchMedia('(pointer: coarse)').matches) ||
Number(navigator.maxTouchPoints || 0) > 0
);
} catch (_) {
return false;
}
}
function hasCanvasLayout() {
const now = Date.now();
if (now - runtime.lastCanvasProbe < CFG.canvasProbeIntervalMs) return runtime.lastCanvasState;
runtime.lastCanvasProbe = now;
try {
const canvasNode = document.querySelector(SEL.canvasRoots);
const editorNode = document.querySelector(SEL.editorRoots);
const bodyText = document.body?.innerText || '';
runtime.lastCanvasState = !!(
canvasNode ||
editorNode ||
bodyText.includes('Copy') && bodyText.includes('Download') && bodyText.includes('Share')
);
return runtime.lastCanvasState;
} catch (_) {
runtime.lastCanvasState = false;
return false;
}
}
function wideActive() {
if (!settings.enabled || !settings.wide) return false;
if (!settings.auto) return true;
if (settings.disableOnTouch && isTouchLike()) return false;
if (settings.disableBelowHeight > 0 && window.innerHeight > 0 && window.innerHeight < settings.disableBelowHeight) return false;
return (window.innerWidth || 0) >= settings.autoMinWidth;
}
function apiStateCore() {
return {
enabled: settings.enabled ? 1 : 0,
wide: settings.wide ? 1 : 0,
left: settings.left ? 1 : 0,
cap: settings.cap,
auto: settings.auto ? 1 : 0,
autoMinWidth: settings.autoMinWidth,
disableOnTouch: settings.disableOnTouch ? 1 : 0,
disableBelowHeight: settings.disableBelowHeight,
showToast: settings.showToast ? 1 : 0,
composerWide: settings.composerWide ? 1 : 0,
codeWide: settings.codeWide ? 1 : 0,
mediaSafe: settings.mediaSafe ? 1 : 0,
canvasTuning: settings.canvasTuning ? 1 : 0,
balancedSplitView: settings.balancedSplitView ? 1 : 0,
suppressCanvasMutations: settings.suppressCanvasMutations ? 1 : 0,
gutterMin: settings.gutterMin,
gutterVw: settings.gutterVw,
gutterMax: settings.gutterMax
};
}
function envKey() {
return [
window.innerWidth || 0,
window.innerHeight || 0,
window.devicePixelRatio || 1,
document.fullscreenElement ? 1 : 0,
isTouchLike() ? 1 : 0,
hasCanvasLayout() ? 1 : 0,
...Object.values(apiStateCore())
].join('|');
}
function cssKey() {
return [VERSION, ...Object.values(apiStateCore())].join('|');
}
function uiCssKey() {
return [VERSION, settings.toastMs].join('|');
}
function buildCapRules() {
return CAPS.map((cap) => {
const value = cap === 'none' ? 'none' : `${cap}px`;
return `:root[${ATTR.on}="1"][${ATTR.cap}="${cap}"]{--uwc-cap:${value}}`;
}).join('\n');
}
function buildCodeCss() {
if (!settings.codeWide) return '';
return `
:root[${ATTR.wide}="1"] pre,
:root[${ATTR.wide}="1"] .markdown pre,
:root[${ATTR.wide}="1"] [class*="prose"] pre{
width:100%!important;
max-width:100%!important;
overflow-x:auto!important;
overflow-y:visible!important;
white-space:pre!important;
box-sizing:border-box!important;
}
:root[${ATTR.wide}="1"] code,
:root[${ATTR.wide}="1"] .markdown code,
:root[${ATTR.wide}="1"] [class*="prose"] code{
max-width:100%!important;
}
:root[${ATTR.wide}="1"] table{
display:block!important;
width:100%!important;
max-width:100%!important;
overflow-x:auto!important;
box-sizing:border-box!important;
}`;
}
function buildMediaCss() {
if (!settings.mediaSafe) return '';
return `
:root[${ATTR.wide}="1"] img,
:root[${ATTR.wide}="1"] svg,
:root[${ATTR.wide}="1"] video,
:root[${ATTR.wide}="1"] canvas,
:root[${ATTR.wide}="1"] iframe,
:root[${ATTR.wide}="1"] figure{
max-width:100%!important;
height:auto!important;
}`;
}
function buildComposerCss() {
if (!settings.composerWide) return '';
return `
:root[${ATTR.wide}="1"] ${SEL.composer}{
width:min(100%,var(--uwc-content-width))!important;
max-width:var(--uwc-content-width)!important;
margin-left:auto!important;
margin-right:auto!important;
box-sizing:border-box!important;
}
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] ${SEL.composer}{
width:100%!important;
max-width:100%!important;
}`;
}
function buildCanvasCss() {
if (!settings.canvasTuning) return '';
const balanced = settings.balancedSplitView ? `
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] main > div,
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] [role="main"] > div{
min-width:0!important;
}
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] main > div:has(${SEL.conversationRoots}),
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] [role="main"] > div:has(${SEL.conversationRoots}){
flex:1 1 48%!important;
min-width:360px!important;
max-width:none!important;
}
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] main > div:has(${SEL.canvasRoots}),
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] [role="main"] > div:has(${SEL.canvasRoots}),
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] main > div:has(${SEL.editorRoots}),
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] [role="main"] > div:has(${SEL.editorRoots}){
flex:1 1 52%!important;
min-width:420px!important;
max-width:none!important;
}` : '';
return `
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] body{
overflow-x:hidden!important;
}
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] main,
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] [role="main"],
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] [data-testid="chat-layout"]{
width:100%!important;
max-width:none!important;
min-width:0!important;
}
${balanced}
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] ${SEL.canvasRoots},
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] ${SEL.editorRoots}{
min-width:0!important;
max-width:none!important;
box-sizing:border-box!important;
}
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] [class*="monaco"],
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] [class*="cm-editor"],
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] [class*="CodeMirror"]{
width:100%!important;
max-width:none!important;
}`;
}
function buildCss() {
return `
:root[${ATTR.on}="1"]{
--uwc-gutter:clamp(${settings.gutterMin}px,${settings.gutterVw}vw,${settings.gutterMax}px);
--uwc-cap:none;
--uwc-content-width:calc(100vw - (var(--uwc-gutter) * 2));
}
${buildCapRules()}
:root[${ATTR.on}="1"]:not([${ATTR.cap}="none"]){
--uwc-content-width:min(calc(100vw - (var(--uwc-gutter) * 2)),var(--uwc-cap));
}
:root[${ATTR.wide}="1"] body{
overflow-x:clip!important;
}
@supports not (overflow-x:clip){
:root[${ATTR.wide}="1"] body{
overflow-x:hidden!important;
}
}
:root[${ATTR.wide}="1"] ${SEL.appRoots}{
box-sizing:border-box!important;
min-width:0!important;
}
:root[${ATTR.wide}="1"] ${SEL.conversationRoots}{
width:100%!important;
max-width:none!important;
min-width:0!important;
padding-left:var(--uwc-gutter)!important;
padding-right:var(--uwc-gutter)!important;
box-sizing:border-box!important;
}
:root[${ATTR.wide}="1"] ${SEL.turns}{
width:100%!important;
max-width:100%!important;
min-width:0!important;
margin-left:0!important;
margin-right:0!important;
box-sizing:border-box!important;
}
:root[${ATTR.wide}="1"] ${SEL.turnInner}{
width:min(100%,var(--uwc-content-width))!important;
max-width:var(--uwc-content-width)!important;
min-width:0!important;
margin-left:auto!important;
margin-right:auto!important;
box-sizing:border-box!important;
}
:root[${ATTR.wide}="1"] ${SEL.markdown}{
width:100%!important;
max-width:none!important;
min-width:0!important;
box-sizing:border-box!important;
}
:root[${ATTR.wide}="1"] ${SEL.markdown} > *{
max-width:100%!important;
}
${buildComposerCss()}
${buildCodeCss()}
${buildMediaCss()}
${buildCanvasCss()}
:root[${ATTR.left}="1"] ${SEL.input},
:root[${ATTR.left}="1"] textarea,
:root[${ATTR.left}="1"] [contenteditable="true"][role="textbox"],
:root[${ATTR.left}="1"] ${SEL.markdown}{
text-align:left!important;
direction:ltr!important;
}
:root[${ATTR.left}="1"] textarea::placeholder{
text-align:left!important;
}
@media (max-width:1179px), (max-height:619px){
:root[${ATTR.wide}="1"]{
--uwc-gutter:12px;
}
:root[${ATTR.wide}="1"] ${SEL.conversationRoots}{
padding-left:12px!important;
padding-right:12px!important;
}
}
@media (max-width:900px){
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] main > div,
:root[${ATTR.wide}="1"][${ATTR.canvas}="1"] [role="main"] > div{
flex:1 1 auto!important;
min-width:0!important;
}
}
@media print{
:root[${ATTR.wide}="1"] ${SEL.appRoots},
:root[${ATTR.wide}="1"] ${SEL.conversationRoots}{
width:100%!important;
max-width:none!important;
}
}`.trim();
}
function buildUiCss() {
return `
#${TOAST_ID}{
position:fixed!important;
z-index:2147483647!important;
right:16px!important;
bottom:16px!important;
max-width:min(440px,calc(100vw - 32px))!important;
padding:10px 12px!important;
border-radius:12px!important;
color:CanvasText!important;
background:Canvas!important;
border:1px solid color-mix(in srgb, CanvasText 18%, transparent)!important;
box-shadow:0 8px 28px rgba(0,0,0,.18)!important;
font:12px/1.35 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif!important;
opacity:.96!important;
pointer-events:none!important;
}
#${MODAL_ID}{
position:fixed!important;
inset:0!important;
z-index:2147483646!important;
display:flex!important;
align-items:center!important;
justify-content:center!important;
padding:18px!important;
background:rgba(0,0,0,.42)!important;
color:CanvasText!important;
font:13px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif!important;
}
#${MODAL_ID} *{box-sizing:border-box!important;}
#${MODAL_ID} .uwc-panel{
width:min(760px,100%)!important;
max-height:min(86vh,820px)!important;
overflow:auto!important;
border-radius:18px!important;
background:Canvas!important;
color:CanvasText!important;
border:1px solid color-mix(in srgb, CanvasText 16%, transparent)!important;
box-shadow:0 20px 70px rgba(0,0,0,.35)!important;
}
#${MODAL_ID} .uwc-header{
position:sticky!important;
top:0!important;
z-index:1!important;
display:flex!important;
align-items:flex-start!important;
justify-content:space-between!important;
gap:16px!important;
padding:18px 18px 12px!important;
background:Canvas!important;
border-bottom:1px solid color-mix(in srgb, CanvasText 12%, transparent)!important;
}
#${MODAL_ID} .uwc-title{
margin:0!important;
font-size:18px!important;
line-height:1.2!important;
font-weight:700!important;
}
#${MODAL_ID} .uwc-subtitle{
margin:5px 0 0!important;
opacity:.72!important;
font-size:12px!important;
}
#${MODAL_ID} .uwc-body{padding:16px 18px 18px!important;}
#${MODAL_ID} .uwc-section{
margin:0 0 18px!important;
padding:14px!important;
border:1px solid color-mix(in srgb, CanvasText 12%, transparent)!important;
border-radius:14px!important;
}
#${MODAL_ID} .uwc-section h3{
margin:0 0 12px!important;
font-size:13px!important;
text-transform:uppercase!important;
letter-spacing:.06em!important;
opacity:.72!important;
}
#${MODAL_ID} .uwc-grid{
display:grid!important;
grid-template-columns:repeat(2,minmax(0,1fr))!important;
gap:12px!important;
}
#${MODAL_ID} .uwc-field{display:flex!important;flex-direction:column!important;gap:6px!important;}
#${MODAL_ID} .uwc-check{display:flex!important;align-items:center!important;gap:9px!important;min-height:34px!important;}
#${MODAL_ID} label{font-weight:600!important;}
#${MODAL_ID} .uwc-hint{opacity:.68!important;font-size:12px!important;font-weight:400!important;}
#${MODAL_ID} input,
#${MODAL_ID} select,
#${MODAL_ID} textarea{
width:100%!important;
min-height:34px!important;
padding:7px 9px!important;
border-radius:10px!important;
color:CanvasText!important;
background:Canvas!important;
border:1px solid color-mix(in srgb, CanvasText 18%, transparent)!important;
font:13px/1.35 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif!important;
}
#${MODAL_ID} input[type="checkbox"]{width:16px!important;min-height:16px!important;accent-color:CanvasText!important;}
#${MODAL_ID} textarea{min-height:115px!important;resize:vertical!important;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace!important;font-size:12px!important;}
#${MODAL_ID} .uwc-actions{display:flex!important;flex-wrap:wrap!important;gap:8px!important;justify-content:flex-end!important;padding-top:4px!important;}
#${MODAL_ID} button{
min-height:34px!important;
padding:7px 11px!important;
border-radius:10px!important;
cursor:pointer!important;
color:CanvasText!important;
background:Canvas!important;
border:1px solid color-mix(in srgb, CanvasText 18%, transparent)!important;
font:600 13px/1.35 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif!important;
}
#${MODAL_ID} button:hover{background:color-mix(in srgb, CanvasText 8%, Canvas)!important;}
#${MODAL_ID} .uwc-primary{color:Canvas!important;background:CanvasText!important;}
#${MODAL_ID} .uwc-danger{border-color:color-mix(in srgb, red 55%, CanvasText 12%)!important;}
#${MODAL_ID} .uwc-close{width:34px!important;min-width:34px!important;padding:0!important;font-size:18px!important;}
@media (max-width:680px){
#${MODAL_ID}{align-items:stretch!important;padding:8px!important;}
#${MODAL_ID} .uwc-panel{max-height:100%!important;}
#${MODAL_ID} .uwc-grid{grid-template-columns:1fr!important;}
}`.trim();
}
function mountStyleElement(id, css, keyProp, keyValue) {
const head = getHead();
if (!head) return false;
let el = document.getElementById(id);
if (!el) {
el = document.createElement('style');
el.id = id;
el.type = 'text/css';
head.appendChild(el);
}
el.setAttribute('data-version', VERSION);
if (el.textContent !== css) el.textContent = css;
runtime[keyProp] = keyValue;
return true;
}
function styleMounted() {
const el = document.getElementById(STYLE_ID);
const head = getHead();
return !!(el && head && head.contains(el));
}
function uiStyleMounted() {
const el = document.getElementById(UI_STYLE_ID);
const head = getHead();
return !!(el && head && head.contains(el));
}
function mountStyle() {
return mountStyleElement(STYLE_ID, buildCss(), 'lastCssKey', cssKey());
}
function mountUiStyle() {
return mountStyleElement(UI_STYLE_ID, buildUiCss(), 'lastUiCssKey', uiCssKey());
}
function unmountStyle() {
const el = document.getElementById(STYLE_ID);
if (el?.parentNode) el.parentNode.removeChild(el);
runtime.lastCssKey = '';
}
function clearAttrs() {
const root = getRoot();
if (!root) return;
root.removeAttribute(ATTR.on);
root.removeAttribute(ATTR.wide);
root.removeAttribute(ATTR.left);
root.removeAttribute(ATTR.cap);
root.removeAttribute(ATTR.canvas);
root.removeAttribute(ATTR.mounted);
}
function setAttrs() {
const root = getRoot();
if (!root) return;
root.setAttribute(ATTR.mounted, VERSION);
root.setAttribute(ATTR.cap, settings.cap);
if (settings.enabled) root.setAttribute(ATTR.on, '1');
else root.removeAttribute(ATTR.on);
if (wideActive()) root.setAttribute(ATTR.wide, '1');
else root.removeAttribute(ATTR.wide);
if (settings.enabled && settings.left) root.setAttribute(ATTR.left, '1');
else root.removeAttribute(ATTR.left);
if (settings.enabled && settings.canvasTuning && hasCanvasLayout()) root.setAttribute(ATTR.canvas, '1');
else root.removeAttribute(ATTR.canvas);
}
function apply() {
if (!getRoot()) return;
if (uiCssKey() !== runtime.lastUiCssKey || !uiStyleMounted()) mountUiStyle();
if (!settings.enabled) {
clearAttrs();
unmountStyle();
return;
}
if (cssKey() !== runtime.lastCssKey || !styleMounted()) {
if (!mountStyle()) return;
}
setAttrs();
}
function schedule(force = false) {
if (!force && runtime.rafId) return;
if (runtime.rafId) cancelAnimationFrame(runtime.rafId);
runtime.rafId = requestAnimationFrame(() => {
runtime.rafId = 0;
apply();
});
}
function showToast(message) {
if (!settings.showToast) return;
try {
mountUiStyle();
const parent = document.body || document.documentElement;
if (!parent) return;
let el = document.getElementById(TOAST_ID);
if (!el) {
el = document.createElement('div');
el.id = TOAST_ID;
parent.appendChild(el);
}
el.textContent = String(message || 'UltraWide updated');
if (runtime.toastTimer) clearTimeout(runtime.toastTimer);
runtime.toastTimer = window.setTimeout(() => {
runtime.toastTimer = 0;
const toast = document.getElementById(TOAST_ID);
if (toast?.parentNode) toast.parentNode.removeChild(toast);
}, settings.toastMs);
} catch (_) {}
}
function statusText() {
return `UltraWide ${settings.enabled ? 'on' : 'off'} | wide ${wideActive() ? 'active' : 'inactive'} | canvas ${hasCanvasLayout() ? 'detected' : 'none'} | cap ${settings.cap}`;
}
function restartTimers() {
if (!runtime.started) return;
if (runtime.repairTimer) clearInterval(runtime.repairTimer);
if (runtime.routeTimer) clearInterval(runtime.routeTimer);
runtime.routeTimer = window.setInterval(() => {
try { checkRouteChange(); } catch (_) {}
}, settings.routePollMs);
runtime.repairTimer = window.setInterval(() => {
try { repairTick(); } catch (error) { console.error('[UltraWide] Repair tick failed:', error); }
}, settings.repairIntervalMs);
}
function commit(message = statusText(), options = {}) {
const beforeRepair = settings.repairIntervalMs;
const beforeRoute = settings.routePollMs;
saveSettings();
runtime.lastCanvasProbe = 0;
schedule(true);
if (!runtime.suppressModalRefresh) refreshSettingsModal();
showToast(message);
if (options.forceTimerRestart || beforeRepair !== settings.repairIntervalMs || beforeRoute !== settings.routePollMs) {
restartTimers();
}
}
function cycleCap() {
const i = CAPS.indexOf(settings.cap);
settings.cap = CAPS[(i + 1 + CAPS.length) % CAPS.length] || 'none';
commit(`UltraWide cap: ${settings.cap}`);
}
function resetSettings() {
Object.assign(settings, DEFAULTS);
commit('UltraWide settings reset', { forceTimerRestart: true });
}
function keyMatch(rule, event) {
return (
!!rule.alt === !!event.altKey &&
!!rule.shift === !!event.shiftKey &&
!!rule.ctrl === !!(event.ctrlKey || event.metaKey) &&
String(rule.key).toLowerCase() === String(event.key || '').toLowerCase()
);
}
function typingTarget(target) {
if (!target) return false;
const tag = String(target.tagName || '').toLowerCase();
return tag === 'input' || tag === 'textarea' || !!target.isContentEditable;
}
function checkRouteChange() {
if (location.href !== runtime.href) {
runtime.href = location.href;
runtime.lastCanvasProbe = 0;
schedule(true);
}
}
function wrapHistoryMethod(fn) {
return function wrappedHistoryMethod(...args) {
const out = fn.apply(this, args);
queueMicrotask(checkRouteChange);
return out;
};
}
function installHistoryHooks() {
try {
if (!runtime.origPushState) runtime.origPushState = history.pushState;
if (!runtime.origReplaceState) runtime.origReplaceState = history.replaceState;
if (history.pushState === runtime.origPushState) history.pushState = wrapHistoryMethod(runtime.origPushState);
if (history.replaceState === runtime.origReplaceState) history.replaceState = wrapHistoryMethod(runtime.origReplaceState);
} catch (_) {}
}
function restoreHistoryHooks() {
try {
if (runtime.origPushState && history.pushState !== runtime.origPushState) history.pushState = runtime.origPushState;
if (runtime.origReplaceState && history.replaceState !== runtime.origReplaceState) history.replaceState = runtime.origReplaceState;
} catch (_) {}
}
function mutationRemovedManagedNode(records) {
const count = Math.min(records.length, CFG.maxMutationRecordsToInspect);
for (let i = 0; i < count; i += 1) {
const removed = records[i]?.removedNodes || [];
const removedCount = Math.min(removed.length, CFG.maxRemovedNodesToInspect);
for (let j = 0; j < removedCount; j += 1) {
const node = removed[j];
if (node?.id === STYLE_ID || node?.id === UI_STYLE_ID || node?.id === MODAL_ID || node?.id === TOAST_ID) return true;
}
}
return false;
}
function mutationIsMostlyCanvas(records) {
if (!settings.suppressCanvasMutations || !runtime.lastCanvasState) return false;
const list = Array.from(records || []).slice(0, CFG.maxMutationRecordsToInspect);
if (list.length === 0) return false;
let canvasHits = 0;
for (const record of list) {
const target = record?.target;
if (target instanceof HTMLElement && target.closest(SEL.canvasRoots)) canvasHits += 1;
else if (target instanceof HTMLElement && target.closest(SEL.editorRoots)) canvasHits += 1;
}
return canvasHits > 0 && canvasHits / list.length >= 0.75;
}
function onMutations(records) {
if (mutationRemovedManagedNode(records || [])) {
schedule(true);
return;
}
if (mutationIsMostlyCanvas(records || [])) return;
if (runtime.debounceTimer) clearTimeout(runtime.debounceTimer);
runtime.debounceTimer = window.setTimeout(() => {
runtime.debounceTimer = 0;
checkRouteChange();
schedule(false);
}, settings.mutationDebounceMs);
}
function attachHeadObserver() {
const head = getHead();
if (!head) return false;
try {
if (runtime.headObserver) runtime.headObserver.disconnect();
runtime.headObserver = new MutationObserver(() => {
if (!uiStyleMounted() || (settings.enabled && !styleMounted())) schedule(true);
});
runtime.headObserver.observe(head, { childList: true });
return true;
} catch (_) {
runtime.headObserver = null;
return false;
}
}
function installObservers() {
try {
const root = getRoot();
if (root) {
runtime.bodyObserver = new MutationObserver(onMutations);
runtime.bodyObserver.observe(root, { childList: true, subtree: true });
}
} catch (_) {}
if (!attachHeadObserver()) {
try {
const root = getRoot();
if (!root) return;
runtime.bootstrapObserver = new MutationObserver(() => {
if (!getHead()) return;
attachHeadObserver();
runtime.bootstrapObserver?.disconnect();
runtime.bootstrapObserver = null;
schedule(true);
});
runtime.bootstrapObserver.observe(root, { childList: true, subtree: true });
} catch (_) {
runtime.bootstrapObserver = null;
}
}
}
function disconnectObservers() {
try { runtime.bodyObserver?.disconnect(); } catch (_) {}
try { runtime.headObserver?.disconnect(); } catch (_) {}
try { runtime.bootstrapObserver?.disconnect(); } catch (_) {}
runtime.bodyObserver = null;
runtime.headObserver = null;
runtime.bootstrapObserver = null;
}
function onKeydown(event) {
if (!event || event.defaultPrevented) return;
const key = String(event.key || '').toLowerCase();
const ourCombo = event.altKey && ['o', 'u', 'l', 'm', 'a', 'c', 's', 'r'].includes(key);
if (typingTarget(event.target) && !ourCombo) return;
if (keyMatch(KEYS.toggleEnabled, event)) {
event.preventDefault();
settings.enabled = !settings.enabled;
commit(`UltraWide script: ${settings.enabled ? 'on' : 'off'}`);
return;
}
if (keyMatch(KEYS.toggleWide, event)) {
event.preventDefault();
settings.wide = !settings.wide;
commit(`UltraWide mode: ${settings.wide ? 'on' : 'off'}`);
return;
}
if (keyMatch(KEYS.toggleLeft, event)) {
event.preventDefault();
settings.left = !settings.left;
commit(`UltraWide left align: ${settings.left ? 'on' : 'off'}`);
return;
}
if (keyMatch(KEYS.cycleCap, event)) {
event.preventDefault();
cycleCap();
return;
}
if (keyMatch(KEYS.toggleAuto, event)) {
event.preventDefault();
settings.auto = !settings.auto;
commit(`UltraWide auto mode: ${settings.auto ? 'on' : 'off'}`);
return;
}
if (keyMatch(KEYS.toggleCanvas, event)) {
event.preventDefault();
settings.canvasTuning = !settings.canvasTuning;
commit(`UltraWide Canvas tuning: ${settings.canvasTuning ? 'on' : 'off'}`);
return;
}
if (keyMatch(KEYS.openSettings, event)) {
event.preventDefault();
openSettingsModal();
return;
}
if (keyMatch(KEYS.reset, event)) {
event.preventDefault();
resetSettings();
}
}
function onResizeLike() {
const next = envKey();
if (next !== runtime.lastEnvKey) {
runtime.lastEnvKey = next;
schedule(true);
}
}
function onPageShow() { schedule(true); }
function onFocus() { schedule(true); }
function onVisibilityChange() { if (!document.hidden) schedule(true); }
function bind() {
runtime.handlers.keydown = onKeydown;
runtime.handlers.resize = onResizeLike;
runtime.handlers.orientationchange = onResizeLike;
runtime.handlers.pageshow = onPageShow;
runtime.handlers.focus = onFocus;
runtime.handlers.popstate = checkRouteChange;
runtime.handlers.visibilitychange = onVisibilityChange;
runtime.handlers.fullscreenchange = onResizeLike;
window.addEventListener('keydown', runtime.handlers.keydown, true);
window.addEventListener('resize', runtime.handlers.resize, { passive: true });
window.addEventListener('orientationchange', runtime.handlers.orientationchange, { passive: true });
window.addEventListener('pageshow', runtime.handlers.pageshow, { passive: true });
window.addEventListener('focus', runtime.handlers.focus, { passive: true });
window.addEventListener('popstate', runtime.handlers.popstate, { passive: true });
document.addEventListener('visibilitychange', runtime.handlers.visibilitychange, { passive: true });
document.addEventListener('fullscreenchange', runtime.handlers.fullscreenchange, { passive: true });
}
function unbind() {
if (runtime.handlers.keydown) window.removeEventListener('keydown', runtime.handlers.keydown, true);
if (runtime.handlers.resize) window.removeEventListener('resize', runtime.handlers.resize);
if (runtime.handlers.orientationchange) window.removeEventListener('orientationchange', runtime.handlers.orientationchange);
if (runtime.handlers.pageshow) window.removeEventListener('pageshow', runtime.handlers.pageshow);
if (runtime.handlers.focus) window.removeEventListener('focus', runtime.handlers.focus);
if (runtime.handlers.popstate) window.removeEventListener('popstate', runtime.handlers.popstate);
if (runtime.handlers.visibilitychange) document.removeEventListener('visibilitychange', runtime.handlers.visibilitychange);
if (runtime.handlers.fullscreenchange) document.removeEventListener('fullscreenchange', runtime.handlers.fullscreenchange);
runtime.handlers = Object.create(null);
}
function registerMenu(label, callback) {
try {
if (typeof GM_registerMenuCommand === 'function') GM_registerMenuCommand(label, callback);
} catch (_) {}
}
function registerMenus() {
if (runtime.menuRegistered) return;
runtime.menuRegistered = true;
registerMenu('⚙️ Settings / Options', openSettingsModal);
registerMenu('🟢 Enable Script', () => {
settings.enabled = !settings.enabled;
commit(`Script ${settings.enabled ? 'enabled' : 'disabled'}`);
});
registerMenu('🖥️ Toggle Ultra-Wide Mode', () => {
settings.wide = !settings.wide;
commit(`Ultra-wide mode ${settings.wide ? 'enabled' : 'disabled'}`);
});
registerMenu('↔️ Toggle Left Alignment', () => {
settings.left = !settings.left;
commit(`Left alignment ${settings.left ? 'enabled' : 'disabled'}`);
});
registerMenu('🎨 Toggle Canvas Optimization', () => {
settings.canvasTuning = !settings.canvasTuning;
commit(`Canvas optimization ${settings.canvasTuning ? 'enabled' : 'disabled'}`);
});
registerMenu('📏 Cycle Width Limit', cycleCap);
registerMenu('⚡ Toggle Adaptive Mode', () => {
settings.auto = !settings.auto;
commit(`Adaptive mode ${settings.auto ? 'enabled' : 'disabled'}`);
});
registerMenu('📤 Export Configuration', exportSettingsToClipboard);
registerMenu('♻️ Reset Configuration', resetSettings);
}
function setOptions(patch) {
if (!patch || typeof patch !== 'object') return false;
if (Object.prototype.hasOwnProperty.call(patch, 'cap') && !CAPS.includes(String(patch.cap))) {
throw new Error(`Invalid cap. Allowed values: ${CAPS.join(', ')}`);
}
const beforeRepair = settings.repairIntervalMs;
const beforeRoute = settings.routePollMs;
const next = normalizeSettings({ ...settings, ...patch });
Object.assign(settings, next);
commit(statusText(), {
forceTimerRestart: beforeRepair !== settings.repairIntervalMs || beforeRoute !== settings.routePollMs
});
return true;
}
function removeToast() {
const toast = document.getElementById(TOAST_ID);
if (toast?.parentNode) toast.parentNode.removeChild(toast);
}
function makeEl(tag, attrs = {}, children = []) {
const el = document.createElement(tag);
for (const [key, value] of Object.entries(attrs)) {
if (key === 'className') el.className = value;
else if (key === 'text') el.textContent = value;
else if (key === 'dataset') Object.assign(el.dataset, value);
else if (key.startsWith('on') && typeof value === 'function') el.addEventListener(key.slice(2).toLowerCase(), value);
else if (value !== false && value !== null && value !== undefined) el.setAttribute(key, String(value));
}
for (const child of children) {
if (child === null || child === undefined) continue;
el.appendChild(typeof child === 'string' ? document.createTextNode(child) : child);
}
return el;
}
function settingInput(key, label, type = 'checkbox', hint = '') {
const id = `uwc-field-${key}`;
if (type === 'checkbox') {
const input = makeEl('input', { id, type: 'checkbox' });
input.checked = !!settings[key];
input.addEventListener('change', () => setOptions({ [key]: input.checked }));
return makeEl('label', { className: 'uwc-check', for: id }, [
input,
makeEl('span', {}, [label, hint ? makeEl('span', { className: 'uwc-hint', text: ` ${hint}` }) : null])
]);
}
const inputAttrs = {
id,
type,
value: settings[key],
min: LIMITS[key]?.[0],
max: LIMITS[key]?.[1],
step: key === 'gutterVw' ? '0.1' : '1'
};
const input = makeEl('input', inputAttrs);
input.addEventListener('change', () => setOptions({ [key]: input.value }));
return makeEl('div', { className: 'uwc-field' }, [
makeEl('label', { for: id, text: label }),
input,
hint ? makeEl('div', { className: 'uwc-hint', text: hint }) : null
]);
}
function capSelect() {
const select = makeEl('select', { id: 'uwc-field-cap' });
for (const cap of CAPS) {
const label = cap === 'none' ? 'None' : `${cap}px`;
const option = makeEl('option', { value: cap, text: label });
if (settings.cap === cap) option.selected = true;
select.appendChild(option);
}
select.addEventListener('change', () => setOptions({ cap: select.value }));
return makeEl('div', { className: 'uwc-field' }, [
makeEl('label', { for: 'uwc-field-cap', text: 'Max-width cap' }),
select,
makeEl('div', { className: 'uwc-hint', text: 'None means full available width.' })
]);
}
function section(title, children) {
return makeEl('section', { className: 'uwc-section' }, [
makeEl('h3', { text: title }),
...children
]);
}
function currentSettingsJson() {
return JSON.stringify({ version: VERSION, settings: { ...settings } }, null, 2);
}
async function copyText(text) {
try {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
return true;
}
} catch (_) {}
try {
const ta = makeEl('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.left = '-9999px';
ta.style.top = '0';
document.body.appendChild(ta);
ta.focus();
ta.select();
const ok = document.execCommand('copy');
ta.remove();
return ok;
} catch (_) {
return false;
}
}
async function exportSettingsToClipboard() {
const ok = await copyText(currentSettingsJson());
showToast(ok ? 'UltraWide settings copied' : 'Copy failed. Open settings and copy manually.');
}
function parseImportedSettings(raw) {
const parsed = JSON.parse(raw);
const payload = parsed?.settings && typeof parsed.settings === 'object' ? parsed.settings : parsed;
return normalizeSettings(payload);
}
function importSettingsFromText(raw) {
const next = parseImportedSettings(raw);
Object.assign(settings, next);
commit('UltraWide settings imported', { forceTimerRestart: true });
}
function closeSettingsModal() {
const modal = document.getElementById(MODAL_ID);
if (modal?.parentNode) modal.parentNode.removeChild(modal);
}
function refreshSettingsModal() {
const modal = document.getElementById(MODAL_ID);
if (!modal) return;
openSettingsModal(true);
}
function openSettingsModal(replace = false) {
try {
mountUiStyle();
const existing = document.getElementById(MODAL_ID);
if (existing && !replace) return;
if (existing?.parentNode) existing.parentNode.removeChild(existing);
const importBox = makeEl('textarea', {
id: 'uwc-import-box',
spellcheck: 'false',
placeholder: 'Paste exported JSON settings here...'
});
const exportBox = makeEl('textarea', {
id: 'uwc-export-box',
spellcheck: 'false'
});
exportBox.value = currentSettingsJson();
const modal = makeEl('div', { id: MODAL_ID, role: 'dialog', 'aria-modal': 'true' }, [
makeEl('div', { className: 'uwc-panel' }, [
makeEl('div', { className: 'uwc-header' }, [
makeEl('div', {}, [
makeEl('h2', { className: 'uwc-title', text: 'UltraWide ChatGPT settings' }),
makeEl('p', { className: 'uwc-subtitle', text: `Version ${VERSION} | Alt+S opens this panel` })
]),
makeEl('button', { className: 'uwc-close', type: 'button', text: '×', title: 'Close', onclick: closeSettingsModal })
]),
makeEl('div', { className: 'uwc-body' }, [
section('Layout', [
makeEl('div', { className: 'uwc-grid' }, [
settingInput('enabled', 'Enable script'),
settingInput('wide', 'Enable ultra-wide mode'),
settingInput('left', 'Left-align text'),
capSelect(),
settingInput('composerWide', 'Widen composer'),
settingInput('codeWide', 'Widen code blocks and tables'),
settingInput('mediaSafe', 'Constrain media safely'),
settingInput('showToast', 'Show toast messages')
])
]),
section('Canvas / Split View', [
makeEl('div', { className: 'uwc-grid' }, [
settingInput('canvasTuning', 'Enable Canvas tuning'),
settingInput('balancedSplitView', 'Balance split view'),
settingInput('suppressCanvasMutations', 'Reduce Canvas mutation noise')
])
]),
section('Adaptive mode', [
makeEl('div', { className: 'uwc-grid' }, [
settingInput('auto', 'Enable auto mode'),
settingInput('disableOnTouch', 'Disable on touch devices'),
settingInput('autoMinWidth', 'Auto min viewport width', 'number', `${LIMITS.autoMinWidth[0]}-${LIMITS.autoMinWidth[1]} px`),
settingInput('disableBelowHeight', 'Disable below height', 'number', '0 disables this guard')
])
]),
section('Spacing', [
makeEl('div', { className: 'uwc-grid' }, [
settingInput('gutterMin', 'Gutter min px', 'number'),
settingInput('gutterVw', 'Gutter vw', 'number'),
settingInput('gutterMax', 'Gutter max px', 'number')
])
]),
section('Runtime tuning', [
makeEl('div', { className: 'uwc-grid' }, [
settingInput('toastMs', 'Toast duration ms', 'number'),
settingInput('mutationDebounceMs', 'Mutation debounce ms', 'number'),
settingInput('repairIntervalMs', 'Repair interval ms', 'number'),
settingInput('routePollMs', 'Route poll ms', 'number'),
settingInput('optionsCloseOnBackdrop', 'Close settings on backdrop')
])
]),
section('Import / Export', [
makeEl('div', { className: 'uwc-grid' }, [
makeEl('div', { className: 'uwc-field' }, [
makeEl('label', { for: 'uwc-export-box', text: 'Export JSON' }),
exportBox,
makeEl('button', { type: 'button', text: 'Copy export JSON', onclick: exportSettingsToClipboard })
]),
makeEl('div', { className: 'uwc-field' }, [
makeEl('label', { for: 'uwc-import-box', text: 'Import JSON' }),
importBox,
makeEl('button', {
type: 'button',
text: 'Import settings',
onclick: () => {
try { importSettingsFromText(importBox.value); }
catch (error) { showToast(`Import failed: ${error?.message || 'invalid JSON'}`); }
}
})
])
])
]),
makeEl('div', { className: 'uwc-actions' }, [
makeEl('button', { type: 'button', text: 'Apply now', onclick: () => commit('UltraWide settings applied', { forceTimerRestart: true }) }),
makeEl('button', { type: 'button', text: 'Copy status', onclick: () => copyText(JSON.stringify(apiState(), null, 2)).then((ok) => showToast(ok ? 'UltraWide status copied' : 'Copy failed')) }),
makeEl('button', { className: 'uwc-danger', type: 'button', text: 'Reset', onclick: resetSettings }),
makeEl('button', { className: 'uwc-primary', type: 'button', text: 'Close', onclick: closeSettingsModal })
])
])
])
]);
modal.addEventListener('click', (event) => {
if (settings.optionsCloseOnBackdrop && event.target === modal) closeSettingsModal();
});
modal.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
event.preventDefault();
event.stopPropagation();
closeSettingsModal();
}
});
const parent = document.body || document.documentElement;
if (!parent) {
showToast('Unable to mount settings UI');
return;
}
runtime.suppressModalRefresh = true;
parent.appendChild(modal);
modal.tabIndex = -1;
modal.focus({ preventScroll: true });
runtime.suppressModalRefresh = false;
} catch (error) {
runtime.suppressModalRefresh = false;
showToast(`Settings UI failed: ${error?.message || 'unknown error'}`);
}
}
function repairTick() {
if (!settings.enabled) {
if (styleMounted()) unmountStyle();
return;
}
checkRouteChange();
if (!uiStyleMounted() || !styleMounted()) {
schedule(true);
return;
}
const nextEnv = envKey();
if (nextEnv !== runtime.lastEnvKey) {
runtime.lastEnvKey = nextEnv;
schedule(true);
}
if (!runtime.headObserver && getHead()) attachHeadObserver();
}
function start() {
if (runtime.started) return;
loadSettings();
runtime.started = true;
runtime.href = location.href;
runtime.lastCanvasProbe = 0;
runtime.lastEnvKey = envKey();
installHistoryHooks();
installObservers();
bind();
registerMenus();
runtime.routeTimer = window.setInterval(checkRouteChange, settings.routePollMs);
runtime.repairTimer = window.setInterval(repairTick, settings.repairIntervalMs);
schedule(true);
}
function stop() {
if (!runtime.started) return;
runtime.started = false;
if (runtime.repairTimer) clearInterval(runtime.repairTimer);
if (runtime.routeTimer) clearInterval(runtime.routeTimer);
if (runtime.debounceTimer) clearTimeout(runtime.debounceTimer);
if (runtime.toastTimer) clearTimeout(runtime.toastTimer);
if (runtime.rafId) cancelAnimationFrame(runtime.rafId);
runtime.repairTimer = 0;
runtime.routeTimer = 0;
runtime.debounceTimer = 0;
runtime.toastTimer = 0;
runtime.rafId = 0;
unbind();
disconnectObservers();
restoreHistoryHooks();
clearAttrs();
unmountStyle();
removeToast();
closeSettingsModal();
}
function restart() {
stop();
start();
}
function apiState() {
return {
version: VERSION,
started: runtime.started,
enabled: settings.enabled,
wide: settings.wide,
left: settings.left,
cap: settings.cap,
auto: settings.auto,
autoMinWidth: settings.autoMinWidth,
disableOnTouch: settings.disableOnTouch,
disableBelowHeight: settings.disableBelowHeight,
showToast: settings.showToast,
composerWide: settings.composerWide,
codeWide: settings.codeWide,
mediaSafe: settings.mediaSafe,
canvasTuning: settings.canvasTuning,
balancedSplitView: settings.balancedSplitView,
suppressCanvasMutations: settings.suppressCanvasMutations,
gutterMin: settings.gutterMin,
gutterVw: settings.gutterVw,
gutterMax: settings.gutterMax,
toastMs: settings.toastMs,
mutationDebounceMs: settings.mutationDebounceMs,
repairIntervalMs: settings.repairIntervalMs,
routePollMs: settings.routePollMs,
optionsCloseOnBackdrop: settings.optionsCloseOnBackdrop,
activeWide: wideActive(),
canvasDetected: hasCanvasLayout(),
href: runtime.href,
env: runtime.lastEnvKey
};
}
function installApi() {
try {
Object.defineProperty(window, '__mlUltraWide', {
configurable: true,
value: Object.freeze({
version: VERSION,
start,
stop,
restart,
apply: () => schedule(true),
set: setOptions,
reset: resetSettings,
state: apiState,
caps: () => [...CAPS],
openSettings: openSettingsModal,
closeSettings: closeSettingsModal,
exportSettings: currentSettingsJson,
importSettings: importSettingsFromText
})
});
} catch (_) {}
}
installApi();
start();
})();