Improves ChatGPT performance for long message chains with containment, inline edit boost, and experimental offscreen parking.
// ==UserScript==
// @name ChatGPT Performance Fix
// @namespace chatgpt-performance-fix
// @description Improves ChatGPT performance for long message chains with containment, inline edit boost, and experimental offscreen parking.
// @version 1.1.11
// @match *://*.chatgpt.com/*
// @supportURL https://greasyfork.org/en/scripts/526998/feedback
// @license MIT
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
"use strict";
// SECTION: constants, state, storage, utilities
const SCRIPT_ID = "cgpfp001";
const SCRIPT_VERSION = "1.1.11";
const STORAGE_KEY = `${SCRIPT_ID}-config`;
const HOST_ID = `${SCRIPT_ID}-host`;
const PANEL_ID = `${SCRIPT_ID}-panel`;
const PAGE_STYLE_ID = `${SCRIPT_ID}-page-style`;
const SHADOW_STYLE_ID = `${SCRIPT_ID}-shadow-style`;
const SUPPORT_URL = "https://greasyfork.org/en/scripts/526998/feedback";
const DONATION_URL = "https://buymeacoffee.com/funlovingdev";
const FRAME_GAP_THRESHOLD_MS = 50;
const SAMPLE_INTERVAL_MS = 3000;
const MAX_TIMING_SAMPLES = 40;
const PARKING_MIN_TURN_COUNT = 30;
const PARKING_TAIL_KEEP_TURNS = 10;
const PARKING_WORK_TIMEOUT_MS = 450;
const PARKING_SCROLL_SETTLE_MS = 180;
const PROXY_MIN_HEIGHT_PX = 44;
const ROOT_ATTRIBUTES = Object.freeze({
masterEnabled: `data-${SCRIPT_ID}-enabled`,
foundationEnabled: `data-${SCRIPT_ID}-foundation`,
heavyBlocksEnabled: `data-${SCRIPT_ID}-heavy-blocks`,
parkingEnabled: `data-${SCRIPT_ID}-parking`,
proxyActive: `data-${SCRIPT_ID}-proxy-active`,
});
const TURN_ATTRIBUTES = Object.freeze({
parkedShell: `data-${SCRIPT_ID}-parked-shell`,
proxyTurn: `data-${SCRIPT_ID}-proxy-turn`,
});
const PROXY_ATTRIBUTES = Object.freeze({
wrapper: `data-${SCRIPT_ID}-proxy-wrapper`,
gridHost: `data-${SCRIPT_ID}-proxy-grid`,
nativeHidden: `data-${SCRIPT_ID}-native-hidden`,
nativeGuide: `data-${SCRIPT_ID}-native-guide`,
nativeTextMuted: `data-${SCRIPT_ID}-native-text-muted`,
diagnosticsLabel: `data-${SCRIPT_ID}-diagnostics-label`,
diagnosticsRole: `data-${SCRIPT_ID}-diagnostics-role`,
});
const SELECTORS = Object.freeze({
turns: '[data-testid^="conversation-turn-"]',
userTurns: '[data-testid^="conversation-turn-"][data-turn="user"]',
composer:
'#prompt-textarea.ProseMirror, .ProseMirror#prompt-textarea, div[contenteditable="true"][role="textbox"]#prompt-textarea',
heavyBlocks:
'[data-testid^="conversation-turn-"] pre, [data-testid^="conversation-turn-"] table, [data-testid^="conversation-turn-"] [role="table"]',
});
const DEFAULT_CONFIG = Object.freeze({
masterEnabled: true,
panelCollapsed: true,
panelIconOnly: false,
foundationEnabled: true,
containHeavyBlocks: true,
intrinsicSizePx: 900,
editBoostEnabled: true,
syncDelayMs: 180,
editorDiagnosticsOverlay: false,
autoFocusProxy: true,
parkingEnabled: true,
keepMarginPx: 1200,
warmMarginPx: 2200,
pruneBatchSize: 2,
restoreBatchSize: 1,
});
const PARKING_PRESETS = Object.freeze({
minimal: Object.freeze({
label: "Minimal",
keepMarginPx: 2800,
warmMarginPx: 4200,
pruneBatchSize: 1,
restoreBatchSize: 1,
summary: "Parks only the farthest turns and keeps the page feeling closest to native.",
}),
conservative: Object.freeze({
label: "Conservative",
keepMarginPx: 1800,
warmMarginPx: 3000,
pruneBatchSize: 1,
restoreBatchSize: 1,
summary: "A gentle middle ground that keeps more nearby DOM ready before parking starts.",
}),
balanced: Object.freeze({
label: "Balanced",
keepMarginPx: 1200,
warmMarginPx: 2200,
pruneBatchSize: 2,
restoreBatchSize: 1,
summary: "Matches the current advanced defaults and balances DOM savings against restore timing.",
}),
aggressive: Object.freeze({
label: "Aggressive",
keepMarginPx: 800,
warmMarginPx: 1500,
pruneBatchSize: 3,
restoreBatchSize: 2,
summary: "Cuts DOM pressure faster, but restore timing becomes more noticeable during long scrolls.",
}),
maximum: Object.freeze({
label: "Maximum",
keepMarginPx: 450,
warmMarginPx: 900,
pruneBatchSize: 4,
restoreBatchSize: 2,
summary: "The strongest parking profile, best for very long threads when DOM reduction matters most.",
}),
});
const TOOLTIPS = Object.freeze({
panelTitle:
"Keeps long ChatGPT threads responsive with render containment, faster old-message editing, and offscreen parking.",
panelVersion: "Installed release version.",
toggles: Object.freeze({
expand: "Expand the full control panel.",
compact: "Minimise the panel but keep live status visible.",
icon: "Reduce the panel to a single button.",
show: "Show ChatGPT Performance Fix.",
}),
summary: Object.freeze({
domNodes: "Current page-wide DOM node count. Lower values usually mean less rendering work.",
parkedTurns:
"Conversation turns currently parked offscreen. Parked turns restore automatically near the viewport.",
statusLabel:
"Current optimisation state. It changes while the script starts, scrolls, edits, or restores parked turns.",
}),
sections: Object.freeze({
foundation:
"Uses CSS containment and content visibility to reduce rendering work across long conversations.",
editBoost:
"Places a lightweight proxy editor over old-message edits so typing stays responsive while native save and cancel still apply the change.",
parking:
"Parks distant turns as lightweight placeholders and restores them before they are needed again.",
}),
controls: Object.freeze({
masterEnabled:
"Turns the whole script on or off. Turning it off restores native behaviour and parked turns.",
foundationEnabled:
"Turns the containment foundation on or off. Off restores full native rendering cost for conversation turns.",
containHeavyBlocks:
"Adds extra containment to large code blocks and tables. Turn it on to lower heavy-thread rendering cost; turn it off to keep full native rendering.",
intrinsicSizePx:
"Reserved height guess for contained turns. Increase it to reduce layout shifts but reserve more space; decrease it to free space but allow more shifting.",
editBoostEnabled:
"Turns the proxy editor on or off. Off uses ChatGPT's native inline editor only.",
syncDelayMs:
"Wait time before proxy text syncs back to the native editor. Increase it to reduce sync churn while typing; decrease it to push edits through sooner.",
editorDiagnosticsOverlay:
"Shows a live diagnostics overlay around the proxy editor, native editor, scroll host, and save or cancel buttons so layout issues are easier to debug without losing readable text.",
autoFocusProxy:
"Moves focus into the proxy editor automatically. Turn it on for faster editing; turn it off if page focus handling feels safer.",
parkingEnabled:
"Turns offscreen parking on or off. Turning it on lowers DOM pressure in long threads; turning it off keeps all turns fully loaded.",
parkingPreset:
"Applies a predefined offscreen parking profile by updating keep margin, warm margin, prune batch, and restore batch together. Gentler presets keep more nearby DOM ready; stronger presets reduce page weight more aggressively.",
keepMarginPx:
"Turns inside this distance stay fully loaded. Increase it to keep more nearby DOM ready; decrease it to unload more DOM and save work.",
warmMarginPx:
"Parked turns inside this wider distance start restoring before they reach the viewport. Increase it for earlier restores; decrease it to save more work but risk later restores.",
pruneBatchSize:
"Maximum number of turns to park in one background pass. Increase it to unload DOM faster; decrease it to spread work more gently.",
restoreBatchSize:
"Maximum number of parked turns to restore in one background pass. Increase it to reload content faster; decrease it to keep restore work lighter.",
}),
actions: Object.freeze({
restore:
"Restores all currently parked turns in small batches.",
copySupport:
"Copies a privacy-safe support snapshot with settings, runtime state, and performance metrics for bug reports.",
resetDefaults:
"Restores the shipped optimisation defaults while keeping the current panel view mode visible for quick verification.",
}),
metrics: Object.freeze({
domNodes: "Current page-wide DOM node count. Lower values usually mean less rendering work.",
turns: "Total conversation turns currently detected on the page.",
parkedTurns:
"Number of offscreen turns currently replaced with lightweight placeholders.",
hitchMax:
"Largest recent main-thread hitch seen in the last 10 seconds. Lower values usually feel smoother.",
}),
});
function createEmptyProxyRecord(key = "") {
return {
key,
wrapper: null,
textarea: null,
editor: null,
grid: null,
scrollHost: null,
turn: null,
submitButton: null,
cancelButton: null,
dirty: false,
syncTimer: 0,
lastAppliedText: "",
submitInFlight: false,
lastFocusedAt: 0,
};
}
const state = {
host: null,
shadowRoot: null,
panel: null,
noticeEl: null,
summaryFields: Object.create(null),
metricFields: Object.create(null),
buttons: Object.create(null),
inputs: Object.create(null),
fieldsets: Object.create(null),
routeUrl: location.href,
config: hydrateStoredConfig(),
statusMessage: "",
statusMessageExpiresAt: 0,
refreshQueued: false,
runtimeMaintenanceQueued: false,
sampleTimer: 0,
rootObserver: null,
frameMonitorStarted: false,
lastScrollY: window.scrollY || 0,
scrollSettledAt: performance.now(),
longTasks: [],
frameGaps: [],
prunedTurns: new Map(),
parkingWorkScheduled: false,
parkingWorkPending: false,
parkingWorkInProgress: false,
forceRestoreAll: false,
proxyEditors: new Map(),
turnKeys: new WeakMap(),
nextTurnKey: 1,
startedAt: performance.now(),
initComplete: false,
lastActivityReason: "starting",
lastActivityAt: performance.now(),
noticeHideTimer: 0,
};
function getProxyEditorList() {
return Array.from(state.proxyEditors.values()).filter(
(proxy) => proxy && (proxy.wrapper?.isConnected || proxy.editor?.isConnected)
);
}
function hasActiveProxyEditors() {
return getProxyEditorList().some((proxy) => proxy.wrapper?.isConnected);
}
function getPrimaryProxyEditor() {
const proxies = getProxyEditorList().filter((proxy) => proxy.wrapper?.isConnected);
if (!proxies.length) {
return null;
}
const activeElement = document.activeElement;
const focusedProxy = proxies.find(
(proxy) => proxy.textarea === activeElement || proxy.textarea?.contains?.(activeElement)
);
if (focusedProxy) {
return focusedProxy;
}
proxies.sort((a, b) => {
if (a.submitInFlight !== b.submitInFlight) {
return Number(b.submitInFlight) - Number(a.submitInFlight);
}
if (a.dirty !== b.dirty) {
return Number(b.dirty) - Number(a.dirty);
}
return b.lastFocusedAt - a.lastFocusedAt || compareNodeOrder(a.turn || a.editor, b.turn || b.editor);
});
return proxies[0];
}
function readJsonStorage(storage, key, fallback = null) {
try {
const raw = storage.getItem(key);
return raw == null ? fallback : JSON.parse(raw);
} catch {
return fallback;
}
}
function writeJsonStorage(storage, key, value) {
try {
storage.setItem(key, JSON.stringify(value));
return true;
} catch {
return false;
}
}
function clamp(value, min, max) {
return Math.min(max, Math.max(min, value));
}
function sanitizeNumericConfigValue(rawValue, min, max) {
if (rawValue === "" || rawValue == null) {
return undefined;
}
const numericValue = Number(rawValue);
if (!Number.isFinite(numericValue)) {
return undefined;
}
return clamp(Math.round(numericValue), min, max);
}
function sanitizeConfigValue(name, rawValue) {
switch (name) {
case "masterEnabled":
case "panelCollapsed":
case "panelIconOnly":
case "foundationEnabled":
case "containHeavyBlocks":
case "editBoostEnabled":
case "editorDiagnosticsOverlay":
case "autoFocusProxy":
case "parkingEnabled":
return Boolean(rawValue);
case "intrinsicSizePx":
return sanitizeNumericConfigValue(rawValue, 400, 2400);
case "syncDelayMs":
return sanitizeNumericConfigValue(rawValue, 50, 2000);
case "keepMarginPx":
return sanitizeNumericConfigValue(rawValue, 200, 5000);
case "warmMarginPx":
return sanitizeNumericConfigValue(rawValue, 300, 7000);
case "pruneBatchSize":
return sanitizeNumericConfigValue(rawValue, 1, 8);
case "restoreBatchSize":
return sanitizeNumericConfigValue(rawValue, 1, 8);
default:
return undefined;
}
}
function sanitizeStoredConfig(rawConfig) {
const nextConfig = { ...DEFAULT_CONFIG };
if (!rawConfig || typeof rawConfig !== "object") {
return nextConfig;
}
for (const key of Object.keys(DEFAULT_CONFIG)) {
const nextValue = sanitizeConfigValue(key, rawConfig[key]);
if (nextValue !== undefined) {
nextConfig[key] = nextValue;
}
}
if (rawConfig.editorDiagnosticsOverlay === undefined && "showNativeGuide" in rawConfig) {
nextConfig.editorDiagnosticsOverlay = Boolean(rawConfig.showNativeGuide);
}
if (nextConfig.warmMarginPx < nextConfig.keepMarginPx) {
nextConfig.warmMarginPx = nextConfig.keepMarginPx;
}
if (nextConfig.panelIconOnly) {
nextConfig.panelCollapsed = true;
}
return nextConfig;
}
function hydrateStoredConfig() {
return sanitizeStoredConfig(readJsonStorage(localStorage, STORAGE_KEY, null));
}
function persistConfig() {
writeJsonStorage(localStorage, STORAGE_KEY, state.config);
}
function getParkingPresetKey(config = state.config) {
for (const [key, preset] of Object.entries(PARKING_PRESETS)) {
if (
config.keepMarginPx === preset.keepMarginPx &&
config.warmMarginPx === preset.warmMarginPx &&
config.pruneBatchSize === preset.pruneBatchSize &&
config.restoreBatchSize === preset.restoreBatchSize
) {
return key;
}
}
return "custom";
}
function getParkingPresetTooltipText() {
const presetKey = getParkingPresetKey();
const preset = PARKING_PRESETS[presetKey];
if (!preset) {
return `${TOOLTIPS.controls.parkingPreset} Current values are custom.`;
}
return `${TOOLTIPS.controls.parkingPreset} Current preset: ${preset.label}. ${preset.summary}`;
}
function applyParkingPreset(presetKey) {
const preset = PARKING_PRESETS[presetKey];
if (!preset) {
syncPanelControls();
return;
}
let changed = false;
for (const [name, value] of Object.entries({
keepMarginPx: preset.keepMarginPx,
warmMarginPx: preset.warmMarginPx,
pruneBatchSize: preset.pruneBatchSize,
restoreBatchSize: preset.restoreBatchSize,
})) {
if (state.config[name] !== value) {
state.config[name] = value;
changed = true;
}
}
if (!changed) {
syncPanelControls();
queueRefresh();
return;
}
persistConfig();
syncRootRuntimeState();
syncPanelControls();
setStatus(`Offscreen parking preset set to ${preset.label}.`, {
reason: "parking-preset",
});
requestParkingWork("parking-preset");
queueRefresh();
}
function pushRolling(list, value, maxSize = MAX_TIMING_SAMPLES) {
list.push(value);
if (list.length > maxSize) {
list.splice(0, list.length - maxSize);
}
}
function formatInteger(value) {
if (value == null || Number.isNaN(value)) {
return "n/a";
}
return Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(value);
}
function formatMs(value) {
if (value == null || Number.isNaN(value)) {
return "n/a";
}
return `${Math.round(value)} ms`;
}
function applyTooltip(target, text) {
if (target && text) {
target.title = text;
}
return target;
}
function applyTooltipToMany(targets, text) {
for (const target of targets) {
applyTooltip(target, text);
}
}
function clearPanelNoticeTimer() {
if (!state.noticeHideTimer) {
return;
}
window.clearTimeout(state.noticeHideTimer);
state.noticeHideTimer = 0;
}
function showPanelNotice(message, options = {}) {
if (!state.panel || !state.noticeEl) {
return;
}
clearPanelNoticeTimer();
state.noticeEl.textContent = String(message || "").trim();
state.noticeEl.dataset.tone = options.tone || "success";
state.panel.dataset.noticeVisible = "true";
state.noticeHideTimer = window.setTimeout(() => {
if (state.panel) {
state.panel.dataset.noticeVisible = "false";
}
state.noticeHideTimer = 0;
}, options.durationMs ?? 3200);
}
function noteActivity(reason) {
state.lastActivityReason = String(reason || "runtime");
state.lastActivityAt = performance.now();
}
function hasRecentActivity(reasons, windowMs = 900) {
const activeReasons = Array.isArray(reasons) ? reasons : [reasons];
return (
activeReasons.includes(state.lastActivityReason) &&
performance.now() - state.lastActivityAt <= windowMs
);
}
function setStatus(message, options = {}) {
state.statusMessage = String(message || "").trim();
state.statusMessageExpiresAt = performance.now() + (options.durationMs ?? 2200);
if (options.reason) {
noteActivity(options.reason);
}
}
function getDefaultStatusMessage() {
return getCurrentStatusInfo().detail;
}
function getCurrentStatusInfo() {
const now = performance.now();
const hasConversation = getTurnElements().length > 0;
const primaryProxy = getPrimaryProxyEditor();
const activeProxyEditors = getProxyEditorList();
if (!state.config.masterEnabled) {
return {
label: "Paused",
detail: "ChatGPT Performance Fix is paused. Re-enable it to apply containment and edit boost again.",
};
}
if (!state.initComplete || now - state.startedAt < 900) {
return {
label: "Starting",
detail: hasConversation ? "Preparing page optimisation." : "Starting.",
};
}
if (!hasConversation) {
return {
label: "Ready",
detail: "Ready.",
};
}
let label = "Page Optimised";
let detail = "Page optimised.";
if (hasRecentActivity(["route", "route-change"], 1800)) {
label = "Starting";
detail = "Refreshing optimisation state for this conversation.";
} else if (state.forceRestoreAll || (state.parkingWorkInProgress && hasRecentActivity("restore-all", 1800))) {
label = "Optimising";
detail = "Reloading nearby conversation content.";
} else if (state.parkingWorkInProgress || state.parkingWorkPending) {
label = "Optimising";
detail = state.prunedTurns.size
? "Optimising while unloading or restoring offscreen turns."
: "Optimising offscreen parking.";
} else if (primaryProxy?.submitInFlight) {
label = "Optimising";
detail = "Optimising while saving an inline edit.";
} else if (activeProxyEditors.some((proxy) => proxy.dirty || proxy.syncTimer)) {
label = "Optimising";
detail = "Optimising while editing.";
} else if (activeProxyEditors.length) {
label = "Editing";
detail =
activeProxyEditors.length > 1
? "Inline edit boost is active across multiple open edits."
: "Inline edit boost is active. Native save and cancel remain authoritative.";
} else if (state.runtimeMaintenanceQueued) {
label = "Optimising";
detail = "Refreshing page optimisation.";
} else if (hasRecentActivity("scroll", 900)) {
label = "Optimising";
detail = "Optimising while scrolling.";
} else if (hasRecentActivity(["mutation", "resize"], 900)) {
label = "Optimising";
detail = "Refreshing page optimisation.";
} else if (hasRecentActivity(["focusin", "focusout"], 900)) {
label = "Optimising";
detail = "Refreshing edit state.";
} else if (state.config.parkingEnabled && state.prunedTurns.size > 0) {
label = "Page Optimised";
detail = "Page optimised. Offscreen turns are parked.";
} else if (state.config.parkingEnabled) {
label = "Page Optimised";
detail = "Page optimised. Experimental parking is active.";
} else {
label = "Page Optimised";
detail = "Page optimised. Containment and edit boost are active.";
}
if (state.statusMessage && now <= state.statusMessageExpiresAt) {
return {
label,
detail: state.statusMessage,
};
}
return { label, detail };
}
function queueRefresh() {
if (state.refreshQueued) {
return;
}
state.refreshQueued = true;
const run = () => {
state.refreshQueued = false;
renderSnapshot(getSnapshot());
};
if ("requestIdleCallback" in window) {
window.requestIdleCallback(run, { timeout: 400 });
} else {
setTimeout(run, 0);
}
}
function scheduleRuntimeMaintenance(reason = "runtime") {
if (state.runtimeMaintenanceQueued) {
return;
}
noteActivity(reason);
state.runtimeMaintenanceQueued = true;
requestAnimationFrame(() => {
state.runtimeMaintenanceQueued = false;
performRuntimeMaintenance(reason);
});
}
function getPanelMode() {
if (!state.config.panelCollapsed) {
return "expanded";
}
return state.config.panelIconOnly ? "icon" : "compact";
}
function setPanelMode(mode) {
const nextMode =
mode === "expanded" || mode === "compact" || mode === "icon" ? mode : "compact";
const nextCollapsed = nextMode !== "expanded";
const nextIconOnly = nextMode === "icon";
if (
state.config.panelCollapsed === nextCollapsed &&
state.config.panelIconOnly === nextIconOnly
) {
syncPanelControls();
return;
}
state.config.panelCollapsed = nextCollapsed;
state.config.panelIconOnly = nextIconOnly;
persistConfig();
syncPanelControls();
queueRefresh();
}
function getMountRoot() {
return document.documentElement || document.body || null;
}
function ensureHost() {
const mountRoot = getMountRoot();
if (!mountRoot) {
return null;
}
let host = document.getElementById(HOST_ID);
if (!host) {
host = document.createElement("div");
host.id = HOST_ID;
mountRoot.appendChild(host);
}
if (!host.shadowRoot) {
host.attachShadow({ mode: "open" });
}
state.host = host;
state.shadowRoot = host.shadowRoot;
return host;
}
function clearPanelReferences() {
clearPanelNoticeTimer();
state.panel = null;
state.noticeEl = null;
state.summaryFields = Object.create(null);
state.metricFields = Object.create(null);
state.buttons = Object.create(null);
state.inputs = Object.create(null);
state.fieldsets = Object.create(null);
}
function compareNodeOrder(a, b) {
if (a === b) {
return 0;
}
const position = a.compareDocumentPosition(b);
if (position & Node.DOCUMENT_POSITION_PRECEDING) {
return 1;
}
if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
return -1;
}
return 0;
}
function setRootAttribute(name, enabled) {
const root = document.documentElement;
if (!root) {
return;
}
if (enabled) {
root.setAttribute(name, "true");
} else {
root.removeAttribute(name);
}
}
function syncRootRuntimeState() {
const root = document.documentElement;
if (!root) {
return;
}
setRootAttribute(ROOT_ATTRIBUTES.masterEnabled, state.config.masterEnabled);
setRootAttribute(
ROOT_ATTRIBUTES.foundationEnabled,
state.config.masterEnabled && state.config.foundationEnabled
);
setRootAttribute(
ROOT_ATTRIBUTES.heavyBlocksEnabled,
state.config.masterEnabled && state.config.foundationEnabled && state.config.containHeavyBlocks
);
setRootAttribute(
ROOT_ATTRIBUTES.parkingEnabled,
state.config.masterEnabled && state.config.parkingEnabled
);
setRootAttribute(ROOT_ATTRIBUTES.proxyActive, hasActiveProxyEditors());
root.style.setProperty(`--${SCRIPT_ID}-intrinsic-size`, `${state.config.intrinsicSizePx}px`);
}
function injectPageStyles() {
if (document.getElementById(PAGE_STYLE_ID)) {
return;
}
const root = document.documentElement || document.head || document.body;
if (!root) {
return;
}
const style = document.createElement("style");
style.id = PAGE_STYLE_ID;
style.textContent = `
#${HOST_ID} {
position: static;
isolation: isolate;
}
html[${ROOT_ATTRIBUTES.foundationEnabled}="true"] ${SELECTORS.turns} {
content-visibility: auto !important;
contain-intrinsic-size: auto var(--${SCRIPT_ID}-intrinsic-size, 900px) !important;
}
html[${ROOT_ATTRIBUTES.heavyBlocksEnabled}="true"] ${SELECTORS.heavyBlocks} {
content-visibility: auto !important;
contain: layout paint !important;
contain-intrinsic-size: auto 320px !important;
}
[${PROXY_ATTRIBUTES.nativeHidden}="true"] {
visibility: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
caret-color: transparent !important;
user-select: none !important;
overflow: hidden !important;
}
[${PROXY_ATTRIBUTES.nativeGuide}="true"] {
visibility: visible !important;
opacity: 1 !important;
pointer-events: none !important;
caret-color: transparent !important;
user-select: none !important;
outline: 1px dashed rgba(73, 92, 87, 0.34) !important;
outline-offset: 2px !important;
background: rgba(244, 247, 239, 0.42) !important;
}
[${PROXY_ATTRIBUTES.nativeTextMuted}="true"],
[${PROXY_ATTRIBUTES.nativeTextMuted}="true"] * {
color: transparent !important;
-webkit-text-fill-color: transparent !important;
text-shadow: none !important;
caret-color: transparent !important;
}
[${PROXY_ATTRIBUTES.diagnosticsLabel}] {
position: relative !important;
}
[${PROXY_ATTRIBUTES.diagnosticsLabel}]::before {
content: attr(${PROXY_ATTRIBUTES.diagnosticsLabel});
position: absolute;
top: -11px;
left: 0;
z-index: 2;
max-width: calc(100% - 8px);
padding: 1px 6px;
border-radius: 999px;
background: rgba(22, 33, 24, 0.9);
color: #f7fbf8;
font: 600 10px/1.3 "Segoe UI", system-ui, sans-serif;
letter-spacing: 0.01em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
pointer-events: none;
}
[${PROXY_ATTRIBUTES.diagnosticsRole}~="grid"] {
outline: 1px dotted rgba(65, 99, 86, 0.76) !important;
outline-offset: 3px !important;
}
[${PROXY_ATTRIBUTES.diagnosticsRole}~="proxy"] {
outline: 2px solid rgba(42, 95, 168, 0.62) !important;
background: rgba(42, 95, 168, 0.05) !important;
}
[${PROXY_ATTRIBUTES.diagnosticsRole}~="native"] {
outline: 1px dashed rgba(181, 110, 22, 0.66) !important;
outline-offset: 2px !important;
background: rgba(255, 184, 107, 0.08) !important;
}
[${PROXY_ATTRIBUTES.diagnosticsRole}~="scroll"] {
outline: 1px solid rgba(35, 124, 102, 0.44) !important;
outline-offset: 1px !important;
}
[${PROXY_ATTRIBUTES.diagnosticsRole}~="save"] {
box-shadow: 0 0 0 2px rgba(35, 124, 102, 0.44) !important;
}
[${PROXY_ATTRIBUTES.diagnosticsRole}~="cancel"] {
box-shadow: 0 0 0 2px rgba(181, 110, 22, 0.42) !important;
}
[${PROXY_ATTRIBUTES.gridHost}="true"] {
position: relative !important;
}
.${SCRIPT_ID}-proxy-shell {
grid-column: 1 / -1;
grid-row: 1 / -1;
position: relative;
z-index: 1;
display: block;
align-self: stretch;
justify-self: stretch;
box-sizing: border-box;
width: 100%;
max-width: 100%;
padding: 0;
border: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
pointer-events: none;
}
.${SCRIPT_ID}-proxy-shell textarea {
display: block;
width: 100%;
max-width: 100%;
resize: none;
border: 0;
border-radius: 0;
background: transparent;
color: inherit;
padding: 0;
font: inherit;
line-height: inherit;
box-sizing: border-box;
box-shadow: none;
outline: none;
pointer-events: auto;
overflow: hidden;
white-space: pre-wrap;
}
.${SCRIPT_ID}-proxy-shell textarea:focus {
outline: none;
box-shadow: none;
}
html[${ROOT_ATTRIBUTES.parkingEnabled}="true"] ${SELECTORS.turns}[${TURN_ATTRIBUTES.parkedShell}="true"] {
contain: layout paint style !important;
overflow-anchor: none !important;
}
.${SCRIPT_ID}-placeholder {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
min-height: var(--${SCRIPT_ID}-reserved-height, 96px);
padding: 12px 14px;
border: 1px dashed rgba(73, 92, 87, 0.34);
border-radius: 14px;
background: rgba(244, 247, 239, 0.92);
color: #1e2d26;
cursor: pointer;
}
.${SCRIPT_ID}-placeholder:hover {
background: rgba(239, 245, 236, 0.96);
}
.${SCRIPT_ID}-placeholder:focus-visible {
outline: 2px solid rgba(48, 82, 69, 0.48);
outline-offset: 2px;
}
.${SCRIPT_ID}-placeholder strong {
display: block;
margin-bottom: 2px;
}
.${SCRIPT_ID}-placeholder span {
display: block;
color: #596d63;
}
.${SCRIPT_ID}-placeholder .${SCRIPT_ID}-placeholder-meta {
margin-top: 6px;
font-size: 11px;
}
`;
root.appendChild(style);
}
function ensureShadowStyles() {
const shadowRoot = state.shadowRoot;
if (!shadowRoot || shadowRoot.querySelector(`#${SHADOW_STYLE_ID}`)) {
return;
}
const style = document.createElement("style");
style.id = SHADOW_STYLE_ID;
style.textContent = `
:host {
all: initial;
}
#${PANEL_ID} {
position: fixed;
right: var(--${SCRIPT_ID}-panel-right, 16px);
bottom: var(--${SCRIPT_ID}-panel-bottom, 16px);
z-index: 2147483647;
width: min(var(--${SCRIPT_ID}-panel-width, 380px), calc(var(--${SCRIPT_ID}-vw, 100vw) - (var(--${SCRIPT_ID}-panel-right, 16px) * 2)));
max-height: calc(var(--${SCRIPT_ID}-vh, 100vh) - (var(--${SCRIPT_ID}-panel-bottom, 16px) * 2));
box-sizing: border-box;
display: flex;
flex-direction: column;
border: 1px solid rgba(57, 81, 68, 0.24);
border-radius: 16px;
background:
radial-gradient(circle at top right, rgba(244, 248, 238, 0.96), rgba(236, 244, 240, 0.96)),
linear-gradient(180deg, rgba(249, 251, 247, 0.98), rgba(241, 247, 242, 0.98));
color: #162118;
box-shadow: 0 24px 52px rgba(18, 25, 21, 0.22);
font: 12px/1.35 "Segoe UI", system-ui, sans-serif;
overflow: hidden;
}
#${PANEL_ID}[data-collapsed="true"] {
width: min(var(--${SCRIPT_ID}-panel-collapsed-width, 220px), calc(var(--${SCRIPT_ID}-vw, 100vw) - (var(--${SCRIPT_ID}-panel-right, 16px) * 2)));
}
#${PANEL_ID}[data-collapsed="true"] .${SCRIPT_ID}-body {
display: none;
}
#${PANEL_ID}[data-collapsed="true"] .${SCRIPT_ID}-switch--title {
display: none;
}
#${PANEL_ID}[data-collapsed="true"] .${SCRIPT_ID}-version {
display: none;
}
#${PANEL_ID}[data-icon-only="true"] {
width: 44px;
max-height: none;
border-radius: 12px;
}
#${PANEL_ID}[data-icon-only="true"] .${SCRIPT_ID}-title-block {
display: none;
}
#${PANEL_ID}[data-icon-only="true"] .${SCRIPT_ID}-body {
display: none;
}
#${PANEL_ID}[data-icon-only="true"] .${SCRIPT_ID}-header {
grid-template-columns: auto;
justify-content: end;
padding: 8px;
border-bottom: 0;
}
#${PANEL_ID}[data-icon-only="true"] .${SCRIPT_ID}-header-actions {
gap: 0;
}
#${PANEL_ID}[data-notice-visible="true"] .${SCRIPT_ID}-notice {
opacity: 1;
transform: translateY(0);
}
#${PANEL_ID} button,
#${PANEL_ID} input,
#${PANEL_ID} select,
#${PANEL_ID} a {
font: inherit;
}
#${PANEL_ID} button {
border: 0;
border-radius: 11px;
background: #305245;
color: #f7fbf8;
cursor: pointer;
transition: filter 120ms ease;
}
#${PANEL_ID} button:hover {
filter: brightness(1.06);
}
#${PANEL_ID} button:disabled {
opacity: 0.55;
cursor: default;
filter: none;
}
#${PANEL_ID} .${SCRIPT_ID}-header {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 8px;
padding: 9px 10px;
border-bottom: 1px solid rgba(57, 81, 68, 0.14);
}
#${PANEL_ID} .${SCRIPT_ID}-title-block {
min-width: 0;
}
#${PANEL_ID} .${SCRIPT_ID}-header-actions {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
}
#${PANEL_ID} .${SCRIPT_ID}-title-line {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
#${PANEL_ID} .${SCRIPT_ID}-title-line strong {
font-size: 12px;
}
#${PANEL_ID} .${SCRIPT_ID}-version {
color: #5d7266;
font-size: 11px;
}
#${PANEL_ID} .${SCRIPT_ID}-summary {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 4px;
margin-top: 6px;
}
#${PANEL_ID} .${SCRIPT_ID}-summary-item {
padding: 6px 7px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.72);
}
#${PANEL_ID} .${SCRIPT_ID}-summary-item span {
display: block;
color: #61756a;
margin-bottom: 2px;
font-size: 10px;
}
#${PANEL_ID} .${SCRIPT_ID}-summary-item strong {
display: block;
color: #18231a;
word-break: break-word;
font-size: 11px;
}
#${PANEL_ID} .${SCRIPT_ID}-summary-item--status {
grid-column: 1 / -1;
}
#${PANEL_ID} .${SCRIPT_ID}-summary-item--status strong {
white-space: normal;
}
#${PANEL_ID} .${SCRIPT_ID}-toggle {
inline-size: 28px;
block-size: 28px;
min-width: 28px;
min-height: 28px;
padding: 0;
border-radius: 8px;
background: rgba(222, 232, 223, 0.96);
color: #1b281d;
}
#${PANEL_ID} .${SCRIPT_ID}-toggle--quiet {
background: rgba(232, 238, 233, 0.96);
}
#${PANEL_ID} .${SCRIPT_ID}-body {
padding: 10px;
overflow: auto;
min-height: 0;
}
#${PANEL_ID} .${SCRIPT_ID}-intro,
#${PANEL_ID} .${SCRIPT_ID}-helper {
margin: 0 0 8px;
color: #4b6155;
}
#${PANEL_ID} .${SCRIPT_ID}-switch {
display: inline-flex;
align-items: center;
gap: 6px;
color: #243228;
}
#${PANEL_ID} .${SCRIPT_ID}-switch input {
inline-size: 14px;
block-size: 14px;
margin: 0;
}
#${PANEL_ID} .${SCRIPT_ID}-switch--title {
padding: 2px 6px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.72);
font-size: 11px;
margin-left: auto;
}
#${PANEL_ID} .${SCRIPT_ID}-section {
margin-bottom: 8px;
padding: 9px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.66);
}
#${PANEL_ID} .${SCRIPT_ID}-section[data-disabled="true"] {
opacity: 0.6;
}
#${PANEL_ID} .${SCRIPT_ID}-section-title {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin: 0 0 4px;
}
#${PANEL_ID} .${SCRIPT_ID}-section-title strong {
font-size: 11px;
}
#${PANEL_ID} .${SCRIPT_ID}-section-note {
margin: 0 0 7px;
color: #5d7266;
font-size: 11px;
}
#${PANEL_ID} fieldset {
border: 0;
padding: 0;
margin: 0;
min-width: 0;
}
#${PANEL_ID} .${SCRIPT_ID}-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 6px;
}
#${PANEL_ID} .${SCRIPT_ID}-control {
display: flex;
flex-direction: column;
gap: 4px;
padding: 7px 8px;
border-radius: 10px;
background: rgba(248, 250, 246, 0.94);
color: #172119;
}
#${PANEL_ID} .${SCRIPT_ID}-control--wide {
grid-column: 1 / -1;
}
#${PANEL_ID} .${SCRIPT_ID}-control[data-kind="checkbox"] {
flex-direction: row;
align-items: center;
justify-content: space-between;
}
#${PANEL_ID} .${SCRIPT_ID}-control[data-kind="select-inline"] {
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 8px;
flex-wrap: nowrap;
}
#${PANEL_ID} .${SCRIPT_ID}-control[data-kind="select-inline"] .${SCRIPT_ID}-control-label {
margin-right: 0;
white-space: nowrap;
}
#${PANEL_ID} .${SCRIPT_ID}-control[data-kind="select-inline"] select {
flex: 0 0 calc((100% - 6px) / 2 - 8px);
width: calc((100% - 6px) / 2 - 8px);
max-width: calc((100% - 6px) / 2 - 8px);
min-width: 0;
margin-left: auto;
}
#${PANEL_ID} .${SCRIPT_ID}-control-label {
color: #61756a;
font-size: 11px;
}
#${PANEL_ID} .${SCRIPT_ID}-control input[type="number"],
#${PANEL_ID} .${SCRIPT_ID}-control select {
min-height: 28px;
border: 1px solid rgba(57, 81, 68, 0.2);
border-radius: 8px;
background: rgba(255, 255, 255, 0.96);
color: #172119;
padding: 4px 6px;
outline: none;
}
#${PANEL_ID} .${SCRIPT_ID}-control select {
min-width: 168px;
}
#${PANEL_ID} .${SCRIPT_ID}-metrics {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 6px;
margin: 8px 0;
}
#${PANEL_ID} .${SCRIPT_ID}-metric {
padding: 7px 8px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.72);
}
#${PANEL_ID} .${SCRIPT_ID}-metric span {
display: block;
color: #61756a;
margin-bottom: 2px;
font-size: 10px;
}
#${PANEL_ID} .${SCRIPT_ID}-metric strong {
display: block;
color: #162118;
font-size: 11px;
}
#${PANEL_ID} .${SCRIPT_ID}-actions {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 6px;
margin-bottom: 8px;
}
#${PANEL_ID} .${SCRIPT_ID}-actions button {
min-height: 30px;
padding: 6px 8px;
}
#${PANEL_ID} .${SCRIPT_ID}-footer {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 8px;
padding-top: 2px;
color: #5d7266;
font-size: 11px;
}
#${PANEL_ID} .${SCRIPT_ID}-footer-separator {
opacity: 0.58;
}
#${PANEL_ID} .${SCRIPT_ID}-footer a {
color: inherit;
text-decoration: none;
}
#${PANEL_ID} .${SCRIPT_ID}-footer a:hover {
text-decoration: underline;
}
#${PANEL_ID} .${SCRIPT_ID}-notice {
position: absolute;
left: 10px;
right: 10px;
bottom: 10px;
z-index: 3;
padding: 10px 11px;
border-radius: 10px;
background: rgba(33, 69, 57, 0.98);
color: #f7fbf8;
box-shadow: 0 14px 28px rgba(18, 25, 21, 0.24);
opacity: 0;
transform: translateY(12px);
transition: opacity 140ms ease, transform 140ms ease;
pointer-events: none;
}
#${PANEL_ID} .${SCRIPT_ID}-notice[data-tone="error"] {
background: rgba(113, 46, 37, 0.98);
}
.${SCRIPT_ID}-proxy-shell {
grid-column: 1 / -1;
grid-row: 1 / -1;
position: relative;
z-index: 1;
display: block;
align-self: stretch;
justify-self: stretch;
box-sizing: border-box;
width: 100%;
max-width: 100%;
padding: 0;
border: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
pointer-events: none;
}
.${SCRIPT_ID}-proxy-shell textarea {
display: block;
width: 100%;
max-width: 100%;
resize: none;
border: 0;
border-radius: 0;
background: transparent;
color: inherit;
padding: 0;
font: inherit;
line-height: inherit;
box-sizing: border-box;
box-shadow: none;
outline: none;
pointer-events: auto;
overflow: hidden;
white-space: pre-wrap;
}
.${SCRIPT_ID}-proxy-shell textarea:focus {
outline: none;
box-shadow: none;
}
@media (max-width: 720px) {
#${PANEL_ID} .${SCRIPT_ID}-summary,
#${PANEL_ID} .${SCRIPT_ID}-grid,
#${PANEL_ID} .${SCRIPT_ID}-metrics,
#${PANEL_ID} .${SCRIPT_ID}-actions {
grid-template-columns: minmax(0, 1fr);
}
}
`;
shadowRoot.appendChild(style);
}
function syncViewportSizing() {
const host = ensureHost();
if (!host) {
return;
}
const viewport = window.visualViewport;
const viewportWidth = Math.max(320, Math.round(viewport?.width || window.innerWidth || 320));
const viewportHeight = Math.max(240, Math.round(viewport?.height || window.innerHeight || 240));
const compact = viewportWidth < 900;
const inset = compact ? 8 : 16;
const availableWidth = Math.max(180, viewportWidth - inset * 2);
const availableHeight = Math.max(180, viewportHeight - inset * 2);
const panelWidth = compact
? Math.max(288, Math.min(344, availableWidth))
: Math.max(332, Math.min(372, availableWidth));
const collapsedWidth = compact
? Math.max(182, Math.min(202, availableWidth))
: Math.max(188, Math.min(205, availableWidth));
host.style.setProperty(`--${SCRIPT_ID}-vw`, `${viewportWidth}px`);
host.style.setProperty(`--${SCRIPT_ID}-vh`, `${viewportHeight}px`);
host.style.setProperty(`--${SCRIPT_ID}-panel-right`, `${inset}px`);
host.style.setProperty(`--${SCRIPT_ID}-panel-bottom`, `${inset}px`);
host.style.setProperty(`--${SCRIPT_ID}-panel-width`, `${panelWidth}px`);
host.style.setProperty(`--${SCRIPT_ID}-panel-collapsed-width`, `${collapsedWidth}px`);
}
function createMetric(label, fieldName, tooltip, target = state.metricFields) {
const wrapper = document.createElement("div");
wrapper.className = `${SCRIPT_ID}-metric`;
const labelEl = document.createElement("span");
labelEl.textContent = label;
const valueEl = document.createElement("strong");
valueEl.textContent = "n/a";
applyTooltipToMany([wrapper, labelEl, valueEl], tooltip);
wrapper.append(labelEl, valueEl);
target[fieldName] = valueEl;
return wrapper;
}
function createSummaryItem(label, fieldName, tooltip) {
const wrapper = document.createElement("div");
wrapper.className = `${SCRIPT_ID}-summary-item`;
const labelEl = document.createElement("span");
labelEl.textContent = label;
const valueEl = document.createElement("strong");
valueEl.textContent = "n/a";
applyTooltipToMany([wrapper, labelEl, valueEl], tooltip);
wrapper.append(labelEl, valueEl);
state.summaryFields[fieldName] = valueEl;
return wrapper;
}
// SECTION: UI
function createCheckboxControl(name, label, tooltip, target = state.inputs) {
const wrapper = document.createElement("label");
wrapper.className = `${SCRIPT_ID}-control`;
wrapper.dataset.kind = "checkbox";
const labelEl = document.createElement("span");
labelEl.className = `${SCRIPT_ID}-control-label`;
labelEl.textContent = label;
const input = document.createElement("input");
input.type = "checkbox";
input.addEventListener("change", () => {
updateConfig(name, input.checked);
});
applyTooltipToMany([wrapper, labelEl, input], tooltip);
wrapper.append(labelEl, input);
target[name] = input;
return wrapper;
}
function createNumberControl(name, label, options, tooltip, target = state.inputs) {
const wrapper = document.createElement("label");
wrapper.className = `${SCRIPT_ID}-control`;
wrapper.dataset.kind = "number";
const labelEl = document.createElement("span");
labelEl.className = `${SCRIPT_ID}-control-label`;
labelEl.textContent = label;
const input = document.createElement("input");
input.type = "number";
input.min = String(options.min);
input.max = String(options.max);
input.step = String(options.step || 1);
input.addEventListener("change", () => {
updateConfig(name, input.value);
});
input.addEventListener("blur", () => {
syncPanelControls();
});
applyTooltipToMany([wrapper, labelEl, input], tooltip);
wrapper.append(labelEl, input);
target[name] = input;
return wrapper;
}
function createSelectControl(name, label, choices, tooltip, options = {}, target = state.inputs) {
const wrapper = document.createElement("label");
wrapper.className = `${SCRIPT_ID}-control${options.wide ? ` ${SCRIPT_ID}-control--wide` : ""}`;
wrapper.dataset.kind = options.compact ? "select-inline" : "select";
const labelEl = document.createElement("span");
labelEl.className = `${SCRIPT_ID}-control-label`;
labelEl.textContent = label;
const select = document.createElement("select");
for (const choice of choices) {
const option = document.createElement("option");
option.value = choice.value;
option.textContent = choice.label;
select.appendChild(option);
}
select.addEventListener("change", () => {
if (typeof options.onChange === "function") {
options.onChange(select.value);
return;
}
updateConfig(name, select.value);
});
applyTooltipToMany([wrapper, labelEl, select], tooltip);
wrapper.append(labelEl, select);
target[name] = select;
return wrapper;
}
function syncNumberInputValue(input, value) {
if (!(input instanceof HTMLInputElement)) {
return;
}
if (document.activeElement === input) {
return;
}
const nextValue = String(value);
if (input.value !== nextValue) {
input.value = nextValue;
}
}
function syncSelectInputValue(input, value) {
if (!(input instanceof HTMLSelectElement)) {
return;
}
if (document.activeElement === input) {
return;
}
if (input.value !== value) {
input.value = value;
}
}
function createSectionHeading(text, tooltip) {
const title = document.createElement("strong");
title.textContent = text;
applyTooltip(title, tooltip);
return title;
}
function createInlineSwitch(name, label, className = "", tooltip = "") {
const switchLabel = document.createElement("label");
switchLabel.className = `${SCRIPT_ID}-switch${className ? ` ${className}` : ""}`;
const input = document.createElement("input");
input.type = "checkbox";
input.addEventListener("change", () => {
updateConfig(name, input.checked);
});
state.inputs[name] = input;
const text = document.createElement("span");
text.textContent = label;
applyTooltipToMany([switchLabel, input, text], tooltip);
switchLabel.append(input, text);
return switchLabel;
}
function createPanel() {
const mountRoot = state.shadowRoot;
if (!mountRoot) {
return null;
}
clearPanelReferences();
const panel = document.createElement("section");
panel.id = PANEL_ID;
const header = document.createElement("div");
header.className = `${SCRIPT_ID}-header`;
const titleBlock = document.createElement("div");
titleBlock.className = `${SCRIPT_ID}-title-block`;
const titleLine = document.createElement("div");
titleLine.className = `${SCRIPT_ID}-title-line`;
const title = document.createElement("strong");
title.textContent = "ChatGPT Performance Fix";
applyTooltip(title, TOOLTIPS.panelTitle);
const version = document.createElement("span");
version.className = `${SCRIPT_ID}-version`;
version.textContent = `v${SCRIPT_VERSION}`;
applyTooltip(version, TOOLTIPS.panelVersion);
const headerEnabled = createInlineSwitch(
"masterEnabled",
"Enabled",
`${SCRIPT_ID}-switch--title`,
TOOLTIPS.controls.masterEnabled
);
titleLine.append(title, version, headerEnabled);
applyTooltip(titleLine, TOOLTIPS.panelTitle);
const summary = document.createElement("div");
summary.className = `${SCRIPT_ID}-summary`;
const summaryDom = createSummaryItem("DOM", "domNodes", TOOLTIPS.summary.domNodes);
const summaryParked = createSummaryItem("Parked", "parkedTurns", TOOLTIPS.summary.parkedTurns);
const summaryStatus = createSummaryItem("Status", "statusLabel", TOOLTIPS.summary.statusLabel);
summaryStatus.classList.add(`${SCRIPT_ID}-summary-item--status`);
summary.append(summaryDom, summaryParked, summaryStatus);
titleBlock.append(titleLine, summary);
const headerActions = document.createElement("div");
headerActions.className = `${SCRIPT_ID}-header-actions`;
const collapseButton = document.createElement("button");
collapseButton.type = "button";
collapseButton.className = `${SCRIPT_ID}-toggle`;
collapseButton.addEventListener("click", () => {
setPanelMode(getPanelMode() === "expanded" ? "compact" : "expanded");
});
state.buttons.collapse = collapseButton;
const iconifyButton = document.createElement("button");
iconifyButton.type = "button";
iconifyButton.className = `${SCRIPT_ID}-toggle ${SCRIPT_ID}-toggle--quiet`;
iconifyButton.textContent = "-";
iconifyButton.addEventListener("click", () => {
setPanelMode("icon");
});
state.buttons.iconify = iconifyButton;
headerActions.append(collapseButton, iconifyButton);
header.append(titleBlock, headerActions);
const body = document.createElement("div");
body.className = `${SCRIPT_ID}-body`;
const metrics = document.createElement("div");
metrics.className = `${SCRIPT_ID}-metrics`;
metrics.append(
createMetric("DOM Nodes", "domNodes", TOOLTIPS.metrics.domNodes),
createMetric("Conversation Turns", "turns", TOOLTIPS.metrics.turns),
createMetric("Parked Turns", "parkedTurns", TOOLTIPS.metrics.parkedTurns),
createMetric("Hitch Max", "hitchMax", TOOLTIPS.metrics.hitchMax)
);
const foundationSection = document.createElement("section");
foundationSection.className = `${SCRIPT_ID}-section`;
state.fieldsets.foundationSection = foundationSection;
const foundationTitle = document.createElement("div");
foundationTitle.className = `${SCRIPT_ID}-section-title`;
applyTooltip(foundationTitle, TOOLTIPS.sections.foundation);
foundationTitle.appendChild(
createSectionHeading("Containment Foundation", TOOLTIPS.sections.foundation)
);
foundationTitle.appendChild(
createInlineSwitch("foundationEnabled", "On", "", TOOLTIPS.controls.foundationEnabled)
);
const foundationNote = document.createElement("p");
foundationNote.className = `${SCRIPT_ID}-section-note`;
foundationNote.textContent = "Keeps conversation turns lightweight with CSS-first containment.";
const foundationFieldset = document.createElement("fieldset");
state.fieldsets.foundation = foundationFieldset;
const foundationGrid = document.createElement("div");
foundationGrid.className = `${SCRIPT_ID}-grid`;
foundationGrid.append(
createCheckboxControl("containHeavyBlocks", "Heavy Blocks", TOOLTIPS.controls.containHeavyBlocks),
createNumberControl("intrinsicSizePx", "Intrinsic Size", {
min: 400,
max: 2400,
step: 50,
}, TOOLTIPS.controls.intrinsicSizePx)
);
foundationFieldset.appendChild(foundationGrid);
foundationSection.append(foundationTitle, foundationNote, foundationFieldset);
const editSection = document.createElement("section");
editSection.className = `${SCRIPT_ID}-section`;
state.fieldsets.editSection = editSection;
const editTitle = document.createElement("div");
editTitle.className = `${SCRIPT_ID}-section-title`;
applyTooltip(editTitle, TOOLTIPS.sections.editBoost);
editTitle.appendChild(createSectionHeading("Inline Edit Boost", TOOLTIPS.sections.editBoost));
editTitle.appendChild(
createInlineSwitch("editBoostEnabled", "On", "", TOOLTIPS.controls.editBoostEnabled)
);
const editNote = document.createElement("p");
editNote.className = `${SCRIPT_ID}-section-note`;
editNote.textContent = "Uses proxy-idle while preserving native save and cancel.";
const editFieldset = document.createElement("fieldset");
state.fieldsets.edit = editFieldset;
const editGrid = document.createElement("div");
editGrid.className = `${SCRIPT_ID}-grid`;
editGrid.append(
createCheckboxControl("autoFocusProxy", "Auto Focus Proxy", TOOLTIPS.controls.autoFocusProxy),
createNumberControl("syncDelayMs", "Sync Delay", {
min: 50,
max: 2000,
step: 10,
}, TOOLTIPS.controls.syncDelayMs),
createCheckboxControl(
"editorDiagnosticsOverlay",
"Diagnostics Overlay",
TOOLTIPS.controls.editorDiagnosticsOverlay
)
);
editFieldset.appendChild(editGrid);
editSection.append(editTitle, editNote, editFieldset);
const parkingSection = document.createElement("section");
parkingSection.className = `${SCRIPT_ID}-section`;
state.fieldsets.parkingSection = parkingSection;
const parkingTitle = document.createElement("div");
parkingTitle.className = `${SCRIPT_ID}-section-title`;
applyTooltip(parkingTitle, TOOLTIPS.sections.parking);
parkingTitle.appendChild(
createSectionHeading("Smart Offscreen Parking", TOOLTIPS.sections.parking)
);
parkingTitle.appendChild(
createInlineSwitch("parkingEnabled", "Experimental", "", TOOLTIPS.controls.parkingEnabled)
);
const parkingNote = document.createElement("p");
parkingNote.className = `${SCRIPT_ID}-section-note`;
parkingNote.textContent = "Uses shell-only parking with staged restore margins.";
const parkingFieldset = document.createElement("fieldset");
state.fieldsets.parking = parkingFieldset;
const parkingGrid = document.createElement("div");
parkingGrid.className = `${SCRIPT_ID}-grid`;
parkingGrid.append(
createSelectControl(
"parkingPreset",
"Presets",
[
{ value: "minimal", label: "Minimal" },
{ value: "conservative", label: "Conservative" },
{ value: "balanced", label: "Balanced" },
{ value: "aggressive", label: "Aggressive" },
{ value: "maximum", label: "Maximum" },
{ value: "custom", label: "Custom" },
],
getParkingPresetTooltipText(),
{
wide: true,
compact: true,
onChange: (value) => {
applyParkingPreset(value);
},
}
),
createNumberControl("keepMarginPx", "Keep Margin", {
min: 200,
max: 5000,
step: 50,
}, TOOLTIPS.controls.keepMarginPx),
createNumberControl("warmMarginPx", "Warm Margin", {
min: 300,
max: 7000,
step: 50,
}, TOOLTIPS.controls.warmMarginPx),
createNumberControl("pruneBatchSize", "Prune Batch", {
min: 1,
max: 8,
step: 1,
}, TOOLTIPS.controls.pruneBatchSize),
createNumberControl("restoreBatchSize", "Restore Batch", {
min: 1,
max: 8,
step: 1,
}, TOOLTIPS.controls.restoreBatchSize)
);
parkingFieldset.appendChild(parkingGrid);
parkingSection.append(parkingTitle, parkingNote, parkingFieldset);
const actions = document.createElement("div");
actions.className = `${SCRIPT_ID}-actions`;
const restoreButton = document.createElement("button");
restoreButton.type = "button";
restoreButton.textContent = "Restore Parked";
applyTooltip(restoreButton, TOOLTIPS.actions.restore);
restoreButton.addEventListener("click", () => {
requestRestoreAll("Restoring parked turns in small batches.");
});
state.buttons.restore = restoreButton;
const supportButton = document.createElement("button");
supportButton.type = "button";
supportButton.textContent = "Copy Support Snapshot";
applyTooltip(supportButton, TOOLTIPS.actions.copySupport);
supportButton.addEventListener("click", () => {
void copySupportSnapshot();
});
state.buttons.copySupport = supportButton;
actions.append(restoreButton, supportButton);
const footer = document.createElement("div");
footer.className = `${SCRIPT_ID}-footer`;
const reportLink = document.createElement("a");
reportLink.href = SUPPORT_URL;
reportLink.target = "_blank";
reportLink.rel = "noopener noreferrer";
reportLink.textContent = "Report a Bug";
const footerSeparatorLeft = document.createElement("span");
footerSeparatorLeft.className = `${SCRIPT_ID}-footer-separator`;
footerSeparatorLeft.textContent = "|";
const resetLink = document.createElement("a");
resetLink.href = "#";
resetLink.textContent = "Reset to Defaults";
applyTooltip(resetLink, TOOLTIPS.actions.resetDefaults);
resetLink.addEventListener("click", (event) => {
event.preventDefault();
resetConfigToDefaults();
});
const footerSeparatorRight = document.createElement("span");
footerSeparatorRight.className = `${SCRIPT_ID}-footer-separator`;
footerSeparatorRight.textContent = "|";
const supportLink = document.createElement("a");
supportLink.href = DONATION_URL;
supportLink.target = "_blank";
supportLink.rel = "noopener noreferrer";
supportLink.textContent = "Support the Dev";
footer.append(reportLink, footerSeparatorLeft, resetLink, footerSeparatorRight, supportLink);
const notice = document.createElement("div");
notice.className = `${SCRIPT_ID}-notice`;
notice.dataset.tone = "success";
panel.dataset.noticeVisible = "false";
state.noticeEl = notice;
body.append(foundationSection, editSection, parkingSection, actions, metrics, footer);
panel.append(header, body, notice);
mountRoot.appendChild(panel);
state.panel = panel;
syncPanelControls();
setStatus(state.statusMessage || getDefaultStatusMessage());
return panel;
}
function ensureUiMounted() {
injectPageStyles();
const host = ensureHost();
if (!host || !state.shadowRoot) {
return false;
}
ensureShadowStyles();
syncViewportSizing();
if (!state.panel || !state.panel.isConnected) {
createPanel();
}
return Boolean(state.panel?.isConnected);
}
function syncPanelControls() {
if (!state.panel) {
return;
}
const { config, inputs, fieldsets, buttons } = state;
const panelMode = getPanelMode();
if (inputs.masterEnabled) {
inputs.masterEnabled.checked = config.masterEnabled;
}
if (inputs.foundationEnabled) {
inputs.foundationEnabled.checked = config.foundationEnabled;
}
if (inputs.containHeavyBlocks) {
inputs.containHeavyBlocks.checked = config.containHeavyBlocks;
}
if (inputs.intrinsicSizePx) {
syncNumberInputValue(inputs.intrinsicSizePx, config.intrinsicSizePx);
}
if (inputs.editBoostEnabled) {
inputs.editBoostEnabled.checked = config.editBoostEnabled;
}
if (inputs.syncDelayMs) {
syncNumberInputValue(inputs.syncDelayMs, config.syncDelayMs);
}
if (inputs.editorDiagnosticsOverlay) {
inputs.editorDiagnosticsOverlay.checked = config.editorDiagnosticsOverlay;
}
if (inputs.autoFocusProxy) {
inputs.autoFocusProxy.checked = config.autoFocusProxy;
}
if (inputs.parkingEnabled) {
inputs.parkingEnabled.checked = config.parkingEnabled;
}
if (inputs.keepMarginPx) {
syncNumberInputValue(inputs.keepMarginPx, config.keepMarginPx);
}
if (inputs.warmMarginPx) {
syncNumberInputValue(inputs.warmMarginPx, config.warmMarginPx);
}
if (inputs.pruneBatchSize) {
syncNumberInputValue(inputs.pruneBatchSize, config.pruneBatchSize);
}
if (inputs.restoreBatchSize) {
syncNumberInputValue(inputs.restoreBatchSize, config.restoreBatchSize);
}
if (inputs.parkingPreset) {
const tooltip = getParkingPresetTooltipText();
syncSelectInputValue(inputs.parkingPreset, getParkingPresetKey(config));
applyTooltip(inputs.parkingPreset, tooltip);
applyTooltip(inputs.parkingPreset.closest(`.${SCRIPT_ID}-control`), tooltip);
}
state.panel.dataset.collapsed = config.panelCollapsed ? "true" : "false";
state.panel.dataset.iconOnly = config.panelIconOnly ? "true" : "false";
if (buttons.collapse) {
buttons.collapse.textContent = config.panelCollapsed ? "+" : "-";
const collapseLabel =
panelMode === "expanded"
? TOOLTIPS.toggles.compact
: panelMode === "icon"
? TOOLTIPS.toggles.show
: TOOLTIPS.toggles.expand;
buttons.collapse.setAttribute("aria-label", collapseLabel);
buttons.collapse.title = collapseLabel;
}
if (buttons.iconify) {
buttons.iconify.hidden = panelMode !== "compact";
buttons.iconify.setAttribute("aria-label", TOOLTIPS.toggles.icon);
buttons.iconify.title = TOOLTIPS.toggles.icon;
}
const foundationEnabled = config.masterEnabled && config.foundationEnabled;
const editEnabled = config.masterEnabled && config.editBoostEnabled;
const parkingEnabled = config.masterEnabled && config.parkingEnabled;
if (fieldsets.foundationSection) {
fieldsets.foundationSection.dataset.disabled = foundationEnabled ? "false" : "true";
}
if (fieldsets.editSection) {
fieldsets.editSection.dataset.disabled = editEnabled ? "false" : "true";
}
if (fieldsets.parkingSection) {
fieldsets.parkingSection.dataset.disabled = parkingEnabled ? "false" : "true";
}
if (fieldsets.foundation) {
fieldsets.foundation.disabled = !foundationEnabled;
}
if (fieldsets.edit) {
fieldsets.edit.disabled = !editEnabled;
}
if (fieldsets.parking) {
fieldsets.parking.disabled = !parkingEnabled;
}
if (buttons.restore) {
buttons.restore.disabled = state.prunedTurns.size === 0;
}
}
function resetConfigToDefaults() {
const preservedPanelMode = {
panelCollapsed: state.config.panelCollapsed,
panelIconOnly: state.config.panelIconOnly,
};
state.config = {
...DEFAULT_CONFIG,
...preservedPanelMode,
};
persistConfig();
syncRootRuntimeState();
syncPanelControls();
syncAllNativeEditorPresentations();
setStatus("Defaults restored. The current panel view stayed open for verification.", {
reason: "reset-defaults",
durationMs: 2800,
});
showPanelNotice("Defaults restored.", {
tone: "success",
durationMs: 2800,
});
scheduleRuntimeMaintenance("reset-defaults");
requestParkingWork("reset-defaults");
queueRefresh();
}
function updateConfig(name, rawValue) {
const nextValue = sanitizeConfigValue(name, rawValue);
if (nextValue === undefined) {
syncPanelControls();
return;
}
if (state.config[name] === nextValue) {
syncPanelControls();
return;
}
state.config[name] = nextValue;
if (state.config.warmMarginPx < state.config.keepMarginPx) {
state.config.warmMarginPx = state.config.keepMarginPx;
}
persistConfig();
syncRootRuntimeState();
syncPanelControls();
switch (name) {
case "panelCollapsed":
case "panelIconOnly":
queueRefresh();
break;
case "masterEnabled":
if (!state.config.masterEnabled) {
teardownProxyEditor("master-disabled");
requestRestoreAll("Restoring parked turns because ChatGPT Performance Fix was disabled.");
setStatus("ChatGPT Performance Fix is paused and the native editing path is active again.", {
reason: "master-disabled",
});
} else {
setStatus("ChatGPT Performance Fix is active again. Containment and edit boost are ready.", {
reason: "master-enabled",
});
scheduleRuntimeMaintenance("master-enabled");
}
break;
case "foundationEnabled":
case "containHeavyBlocks":
case "intrinsicSizePx":
setStatus("Containment settings updated instantly.");
break;
case "editBoostEnabled":
if (!state.config.masterEnabled || !state.config.editBoostEnabled) {
teardownProxyEditor("edit-boost-disabled");
setStatus("Inline edit boost is off. Old-message editing now uses the native surface only.", {
reason: "edit-boost-disabled",
});
} else {
setStatus("Inline edit boost is ready. Open an older message to compare responsiveness.", {
reason: "edit-boost-enabled",
});
scheduleRuntimeMaintenance("edit-boost-enabled");
}
break;
case "syncDelayMs":
setStatus(`Proxy idle sync delay set to ${state.config.syncDelayMs} ms.`, {
reason: "delay-change",
});
if (hasActiveProxyEditors()) {
for (const proxy of getProxyEditorList()) {
scheduleProxySync(proxy, "delay-change");
}
}
break;
case "editorDiagnosticsOverlay":
syncAllNativeEditorPresentations();
setStatus(
state.config.editorDiagnosticsOverlay
? "Editor diagnostics overlay is on. Proxy, native editor, and action buttons are highlighted."
: "Editor diagnostics overlay is off. The clean editing view is active again.",
{ reason: "editor-diagnostics-toggle" }
);
break;
case "autoFocusProxy":
setStatus(state.config.autoFocusProxy ? "Proxy auto focus is on." : "Proxy auto focus is off.", {
reason: "autofocus-toggle",
});
break;
case "parkingEnabled":
if (!state.config.masterEnabled || !state.config.parkingEnabled) {
requestRestoreAll("Restoring parked turns because offscreen parking was disabled.");
setStatus("Experimental parking is off. Containment remains active.", {
reason: "parking-disabled",
});
} else {
setStatus("Experimental parking is on. It will engage conservatively outside the keep margin.", {
reason: "parking-enabled",
});
requestParkingWork("parking-enabled");
}
break;
case "keepMarginPx":
case "warmMarginPx":
case "pruneBatchSize":
case "restoreBatchSize":
setStatus("Offscreen parking settings updated instantly.", {
reason: "parking-config",
});
requestParkingWork("parking-config");
break;
default:
break;
}
queueRefresh();
}
// SECTION: editor helpers
function getTurnElements() {
return Array.from(document.querySelectorAll(SELECTORS.turns));
}
function isVisibleElement(element) {
if (!(element instanceof Element) || !element.isConnected) {
return false;
}
const style = window.getComputedStyle(element);
return style.display !== "none" && style.visibility !== "hidden" && style.opacity !== "0";
}
function isManagedHiddenInlineEditor(element) {
return Boolean(
element instanceof Element &&
element.isConnected &&
element.getAttribute(PROXY_ATTRIBUTES.nativeHidden) === "true"
);
}
function isLikelyInlineMessageEditor(element) {
if (!(element instanceof HTMLElement) || !element.isConnected) {
return false;
}
if (element.closest(`[${PROXY_ATTRIBUTES.wrapper}="true"]`)) {
return false;
}
const turn = element.closest(SELECTORS.userTurns);
if (!turn) {
return false;
}
if (element instanceof HTMLTextAreaElement) {
const ariaLabel = (element.getAttribute("aria-label") || "").trim().toLowerCase();
return ariaLabel === "edit message";
}
if (!element.isContentEditable || element.getAttribute("role") !== "textbox") {
return false;
}
const id = (element.id || "").trim();
if (id === "prompt-textarea") {
return false;
}
const ariaLabel = (element.getAttribute("aria-label") || "").trim().toLowerCase();
if (ariaLabel && ariaLabel !== "edit message") {
return false;
}
const { submitButton, cancelButton } = getEditActionButtons(element);
return Boolean(submitButton || cancelButton);
}
function getInlineEditors(options = {}) {
const includeManagedHidden = Boolean(options.includeManagedHidden);
const candidates = Array.from(
document.querySelectorAll(
`${SELECTORS.userTurns} textarea, ${SELECTORS.userTurns} [contenteditable="true"][role="textbox"]`
)
);
return candidates.filter((element) => {
if (!isLikelyInlineMessageEditor(element)) {
return false;
}
if (isVisibleElement(element)) {
return true;
}
return includeManagedHidden && isManagedHiddenInlineEditor(element);
});
}
function getVisibleInlineEditors() {
return getInlineEditors();
}
function getEditorText(element) {
if (!element) {
return "";
}
if ("value" in element && typeof element.value === "string") {
return element.value;
}
return element.innerText || element.textContent || "";
}
function selectElementContents(element) {
const selection = window.getSelection();
if (!selection) {
return;
}
const range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
}
function replaceContentEditableText(element, text) {
const lines = String(text).split(/\r?\n/);
const fragment = document.createDocumentFragment();
lines.forEach((line, index) => {
fragment.appendChild(document.createTextNode(line));
if (index < lines.length - 1) {
fragment.appendChild(document.createElement("br"));
}
});
element.replaceChildren(fragment);
element.dispatchEvent(new Event("input", { bubbles: true }));
}
function setFormControlValue(element, text) {
if (!(element instanceof HTMLTextAreaElement || element instanceof HTMLInputElement)) {
return false;
}
const prototype =
element instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
const descriptor = Object.getOwnPropertyDescriptor(prototype, "value");
if (descriptor?.set) {
descriptor.set.call(element, text);
} else {
element.value = text;
}
return true;
}
function applyTextToEditor(element, text, options = {}) {
if (!element) {
return false;
}
const avoidFocus = Boolean(options.avoidFocus);
if (!avoidFocus) {
element.focus();
}
if ("value" in element) {
setFormControlValue(element, text);
try {
element.dispatchEvent(
new InputEvent("input", {
bubbles: true,
data: text,
inputType: "insertReplacementText",
})
);
} catch {
element.dispatchEvent(new Event("input", { bubbles: true }));
}
element.dispatchEvent(new Event("change", { bubbles: true }));
return true;
}
if (!element.isContentEditable) {
return false;
}
let inserted = false;
if (avoidFocus) {
replaceContentEditableText(element, text);
return true;
}
try {
selectElementContents(element);
inserted = Boolean(document.execCommand && document.execCommand("insertText", false, text));
} catch {
inserted = false;
}
if (!inserted) {
replaceContentEditableText(element, text);
}
return true;
}
function getEditActionButtons(editor) {
const turn = editor?.closest(SELECTORS.turns);
if (!turn) {
return { submitButton: null, cancelButton: null };
}
const containers = [];
let current = editor.parentElement;
while (current && current !== turn.parentElement) {
containers.push(current);
current = current.parentElement;
}
containers.push(turn);
for (const container of containers) {
const visibleButtons = Array.from(container.querySelectorAll("button")).filter((button) => {
if (!(button instanceof HTMLButtonElement) || !isVisibleElement(button)) {
return false;
}
return compareNodeOrder(button, editor) >= 0 || container === turn;
});
let submitButton = null;
let cancelButton = null;
for (const button of visibleButtons) {
const label = `${button.getAttribute("aria-label") || ""} ${button.textContent || ""}`
.trim()
.toLowerCase();
if (!cancelButton && label.includes("cancel")) {
cancelButton = button;
continue;
}
if (!submitButton && (label.includes("save") || label.includes("submit") || button.type === "submit")) {
submitButton = button;
}
}
if (submitButton || cancelButton) {
return { submitButton, cancelButton };
}
}
return { submitButton: null, cancelButton: null };
}
function isButtonDisabled(button) {
return Boolean(
button instanceof HTMLButtonElement &&
(button.disabled ||
button.getAttribute("aria-disabled") === "true" ||
button.getAttribute("data-disabled") === "true")
);
}
function clearProxySyncTimer(proxy) {
if (!proxy?.syncTimer) {
return;
}
window.clearTimeout(proxy.syncTimer);
proxy.syncTimer = 0;
}
function findEditorScrollHost(editor) {
if (!(editor instanceof HTMLElement) || !editor.isConnected) {
return editor || null;
}
const turn = editor.closest(SELECTORS.turns);
let current = editor.parentElement;
while (current && current !== turn?.parentElement) {
const computed = window.getComputedStyle(current);
if (/(auto|scroll)/.test(computed.overflowY || "") || /(auto|scroll)/.test(computed.overflow || "")) {
return current;
}
current = current.parentElement;
}
return editor;
}
function getEditorGridHost(editor) {
if (!(editor instanceof HTMLElement) || !editor.isConnected) {
return null;
}
const turn = editor.closest(SELECTORS.turns);
let current = editor.parentElement;
let fallbackHost = editor.parentElement instanceof HTMLElement ? editor.parentElement : null;
while (current && current !== turn?.parentElement) {
if (current.contains(editor)) {
const computed = window.getComputedStyle(current);
if (computed.display.includes("grid")) {
return current;
}
if (!fallbackHost && current instanceof HTMLElement) {
fallbackHost = current;
}
}
current = current.parentElement;
}
return fallbackHost;
}
function markDiagnosticsElement(element, role, label = "") {
if (!(element instanceof Element) || !element.isConnected) {
return;
}
const nextRoles = new Set(
(element.getAttribute(PROXY_ATTRIBUTES.diagnosticsRole) || "")
.split(/\s+/)
.filter(Boolean)
);
nextRoles.add(role);
element.setAttribute(PROXY_ATTRIBUTES.diagnosticsRole, Array.from(nextRoles).join(" "));
if (!label) {
return;
}
const nextLabels = (element.getAttribute(PROXY_ATTRIBUTES.diagnosticsLabel) || "")
.split(" / ")
.map((value) => value.trim())
.filter(Boolean);
if (!nextLabels.includes(label)) {
nextLabels.push(label);
element.setAttribute(PROXY_ATTRIBUTES.diagnosticsLabel, nextLabels.join(" / "));
}
}
function clearNativePresentationAttributes(keepTargets = []) {
const keepSet = new Set(keepTargets.filter(Boolean));
const selector = [
`[${PROXY_ATTRIBUTES.nativeHidden}]`,
`[${PROXY_ATTRIBUTES.nativeGuide}]`,
`[${PROXY_ATTRIBUTES.nativeTextMuted}]`,
`[${PROXY_ATTRIBUTES.diagnosticsLabel}]`,
`[${PROXY_ATTRIBUTES.diagnosticsRole}]`,
].join(", ");
for (const element of document.querySelectorAll(selector)) {
if (!keepSet.has(element)) {
element.removeAttribute(PROXY_ATTRIBUTES.nativeHidden);
element.removeAttribute(PROXY_ATTRIBUTES.nativeGuide);
element.removeAttribute(PROXY_ATTRIBUTES.nativeTextMuted);
element.removeAttribute(PROXY_ATTRIBUTES.diagnosticsLabel);
element.removeAttribute(PROXY_ATTRIBUTES.diagnosticsRole);
}
}
}
function syncProxyTextareaVisualStyle(proxy) {
const textarea = proxy?.textarea;
const editor = proxy?.editor;
if (!textarea || !editor?.isConnected) {
return;
}
const computed = window.getComputedStyle(editor);
textarea.style.fontFamily = computed.fontFamily;
textarea.style.fontSize = computed.fontSize;
textarea.style.fontWeight = computed.fontWeight;
textarea.style.lineHeight = computed.lineHeight;
textarea.style.letterSpacing = computed.letterSpacing;
textarea.style.color = computed.color;
textarea.style.textAlign = computed.textAlign;
textarea.style.textTransform = computed.textTransform;
textarea.style.whiteSpace = computed.whiteSpace;
textarea.style.wordBreak = computed.wordBreak;
textarea.style.overflowWrap = computed.overflowWrap;
textarea.style.padding = computed.padding;
textarea.style.margin = "0";
textarea.style.background = "transparent";
textarea.style.border = "0";
textarea.style.borderRadius = "0";
}
function syncProxyContentHeight(proxy) {
const textarea = proxy?.textarea;
const wrapper = proxy?.wrapper;
const editor = proxy?.editor;
if (!textarea || !wrapper?.isConnected) {
return 0;
}
const minHeight = Math.max(
PROXY_MIN_HEIGHT_PX,
Math.round(editor?.getBoundingClientRect().height || editor?.clientHeight || PROXY_MIN_HEIGHT_PX)
);
textarea.style.height = "0px";
textarea.style.minHeight = `${minHeight}px`;
const contentHeight = Math.max(minHeight, Math.ceil(textarea.scrollHeight || 0));
textarea.style.height = `${contentHeight}px`;
wrapper.style.minHeight = `${contentHeight}px`;
return contentHeight;
}
function syncSingleProxyEditor(proxy) {
const wrapper = proxy?.wrapper;
const editor = proxy?.editor;
const grid = proxy?.grid;
if (!wrapper?.isConnected || !editor?.isConnected || !grid?.isConnected) {
return false;
}
proxy.scrollHost = findEditorScrollHost(editor);
const actions = getEditActionButtons(editor);
proxy.submitButton = actions.submitButton;
proxy.cancelButton = actions.cancelButton;
syncProxyTextareaVisualStyle(proxy);
syncProxyContentHeight(proxy);
return true;
}
function syncAllProxyEditors() {
for (const proxy of getProxyEditorList()) {
syncSingleProxyEditor(proxy);
}
}
function syncAllNativeEditorPresentations() {
const activeProxies = getProxyEditorList().filter(
(proxy) =>
proxy.editor?.isConnected &&
proxy.wrapper?.isConnected &&
state.config.masterEnabled &&
state.config.editBoostEnabled
);
const keepTargets = [];
for (const proxy of activeProxies) {
if (proxy.editor) {
keepTargets.push(proxy.editor);
}
if (state.config.editorDiagnosticsOverlay) {
if (proxy.wrapper) {
keepTargets.push(proxy.wrapper);
}
if (proxy.grid) {
keepTargets.push(proxy.grid);
}
if (proxy.scrollHost) {
keepTargets.push(proxy.scrollHost);
}
if (proxy.submitButton) {
keepTargets.push(proxy.submitButton);
}
if (proxy.cancelButton) {
keepTargets.push(proxy.cancelButton);
}
}
}
clearNativePresentationAttributes(keepTargets);
for (const proxy of activeProxies) {
const editor = proxy.editor;
if (!editor) {
continue;
}
if (!state.config.editorDiagnosticsOverlay) {
editor.setAttribute(PROXY_ATTRIBUTES.nativeHidden, "true");
editor.removeAttribute(PROXY_ATTRIBUTES.nativeGuide);
editor.removeAttribute(PROXY_ATTRIBUTES.nativeTextMuted);
continue;
}
editor.removeAttribute(PROXY_ATTRIBUTES.nativeHidden);
editor.setAttribute(PROXY_ATTRIBUTES.nativeTextMuted, "true");
editor.setAttribute(PROXY_ATTRIBUTES.nativeGuide, "true");
markDiagnosticsElement(editor, "native");
if (proxy.wrapper) {
markDiagnosticsElement(proxy.wrapper, "proxy", "Proxy overlay");
}
if (proxy.grid) {
markDiagnosticsElement(proxy.grid, "grid", "Grid host");
}
if (proxy.scrollHost) {
markDiagnosticsElement(proxy.scrollHost, "scroll", "Scroll host");
}
if (proxy.submitButton) {
markDiagnosticsElement(proxy.submitButton, "save");
}
if (proxy.cancelButton) {
markDiagnosticsElement(proxy.cancelButton, "cancel");
}
}
}
function updateProxyDirtyState(proxy) {
const textarea = proxy?.textarea;
if (!textarea) {
if (proxy) {
proxy.dirty = false;
}
return;
}
proxy.dirty = textarea.value !== proxy.lastAppliedText;
}
// SECTION: proxy editor
function syncProxyToNative(proxy, reason = "idle", showStatus = false) {
const textarea = proxy?.textarea;
const editor = proxy?.editor;
if (!textarea || !editor || !editor.isConnected) {
return false;
}
clearProxySyncTimer(proxy);
const text = textarea.value;
const avoidFocus = reason !== "manual" && reason !== "proxy-submit" && reason !== "submit-pointerdown";
const wasFocused = document.activeElement === textarea;
const selectionStart = textarea.selectionStart;
const selectionEnd = textarea.selectionEnd;
const applied = applyTextToEditor(editor, text, { avoidFocus });
if (!applied) {
if (showStatus) {
setStatus("Proxy sync failed because the native edit surface was not writable.");
}
return false;
}
proxy.lastAppliedText = text;
updateProxyDirtyState(proxy);
if (!avoidFocus && wasFocused && proxy.textarea?.isConnected) {
requestAnimationFrame(() => {
const activeTextarea = proxy.textarea;
if (!activeTextarea?.isConnected) {
return;
}
activeTextarea.focus();
if (
typeof selectionStart === "number" &&
typeof selectionEnd === "number" &&
activeTextarea.setSelectionRange
) {
activeTextarea.setSelectionRange(selectionStart, selectionEnd);
}
});
}
if (showStatus) {
setStatus(`Synced the proxy text back into the native editor (${reason}).`);
}
queueRefresh();
return true;
}
function scheduleProxySync(proxy, reason = "idle") {
const textarea = proxy?.textarea;
if (!textarea) {
return;
}
updateProxyDirtyState(proxy);
clearProxySyncTimer(proxy);
if (!proxy.dirty) {
queueRefresh();
return;
}
proxy.syncTimer = window.setTimeout(() => {
syncProxyToNative(proxy, reason);
}, state.config.syncDelayMs);
queueRefresh();
}
async function submitProxyEdit(proxy, trigger = "button") {
const submitButton = proxy?.submitButton;
if (!submitButton?.isConnected || isButtonDisabled(submitButton) || proxy.submitInFlight) {
return false;
}
proxy.submitInFlight = true;
clearProxySyncTimer(proxy);
const synced = syncProxyToNative(proxy, "proxy-submit");
if (!synced) {
proxy.submitInFlight = false;
setStatus("Proxy submit failed because native sync did not complete.");
return false;
}
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (submitButton.isConnected) {
submitButton.click();
setStatus(`Submitted the proxy text through ChatGPT's native save button (${trigger}).`);
}
window.setTimeout(() => {
proxy.submitInFlight = false;
}, 250);
});
});
return true;
}
function onProxyTextareaInput(proxy) {
if (!proxy) {
return;
}
proxy.dirty = true;
proxy.lastFocusedAt = performance.now();
syncProxyContentHeight(proxy);
scheduleProxySync(proxy, "input");
}
function onProxyTextareaKeydown(proxy, event) {
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
event.preventDefault();
void submitProxyEdit(proxy, "shortcut");
return;
}
if (event.key === "Escape" && proxy?.cancelButton?.isConnected) {
event.preventDefault();
proxy.cancelButton.click();
}
}
function detachSingleProxyEditor(proxy, reason = "detach") {
if (!proxy) {
return;
}
clearProxySyncTimer(proxy);
if (proxy.turn?.isConnected) {
proxy.turn.removeAttribute(TURN_ATTRIBUTES.proxyTurn);
}
if (proxy.wrapper?.isConnected) {
proxy.wrapper.remove();
}
if (
proxy.grid?.isConnected &&
!proxy.grid.querySelector(`[${PROXY_ATTRIBUTES.wrapper}="true"]`)
) {
proxy.grid.removeAttribute(PROXY_ATTRIBUTES.gridHost);
}
state.proxyEditors.delete(proxy.key);
syncAllNativeEditorPresentations();
syncRootRuntimeState();
if (reason !== "detach") {
queueRefresh();
}
}
function teardownProxyEditor(reason = "detach") {
for (const proxy of Array.from(state.proxyEditors.values())) {
detachSingleProxyEditor(proxy, reason);
}
}
function attachProxyEditor(editor) {
if (!(editor instanceof HTMLElement) || !editor.isConnected) {
return false;
}
const turn = editor.closest(SELECTORS.turns);
if (!turn) {
return false;
}
const key = getTurnKey(turn);
const existing = state.proxyEditors.get(key) || null;
if (existing?.editor === editor && existing.wrapper?.isConnected) {
syncSingleProxyEditor(existing);
updateProxyDirtyState(existing);
syncAllNativeEditorPresentations();
syncRootRuntimeState();
queueRefresh();
return true;
}
const preservedDraft =
existing?.textarea instanceof HTMLTextAreaElement ? existing.textarea.value : null;
const preservedDirty = Boolean(existing?.dirty);
const preservedLastAppliedText =
typeof existing?.lastAppliedText === "string" ? existing.lastAppliedText : "";
const shouldFocusProxy =
state.config.autoFocusProxy &&
(document.activeElement === editor || existing?.textarea === document.activeElement);
if (existing) {
detachSingleProxyEditor(existing, "swap-editor");
}
const grid = getEditorGridHost(editor);
if (!grid) {
return false;
}
grid.setAttribute(PROXY_ATTRIBUTES.gridHost, "true");
const wrapper = document.createElement("div");
wrapper.className = `${SCRIPT_ID}-proxy-shell`;
wrapper.setAttribute(PROXY_ATTRIBUTES.wrapper, "true");
wrapper.style.gridColumn = "1 / -1";
wrapper.style.gridRow = "1 / -1";
wrapper.style.position = "relative";
wrapper.style.zIndex = "1";
wrapper.style.width = "100%";
wrapper.style.maxWidth = "100%";
wrapper.style.pointerEvents = "none";
const textarea = document.createElement("textarea");
textarea.className = editor.className || "";
textarea.value = preservedDraft != null ? preservedDraft : getEditorText(editor);
textarea.setAttribute("aria-label", editor.getAttribute("aria-label") || "Edit message");
textarea.placeholder = editor.getAttribute("placeholder") || "";
if (editor.hasAttribute("rows")) {
textarea.rows = Math.max(1, Number(editor.getAttribute("rows")) || 1);
}
if (editor.hasAttribute("autocomplete")) {
textarea.setAttribute("autocomplete", editor.getAttribute("autocomplete") || "off");
}
if (editor.hasAttribute("autocapitalize")) {
textarea.setAttribute("autocapitalize", editor.getAttribute("autocapitalize") || "sentences");
}
if (editor.hasAttribute("autocorrect")) {
textarea.setAttribute("autocorrect", editor.getAttribute("autocorrect") || "on");
}
textarea.spellcheck = editor.spellcheck;
textarea.dir = editor.getAttribute("dir") || "auto";
textarea.style.display = "block";
textarea.style.width = "100%";
textarea.style.maxWidth = "100%";
textarea.style.resize = "none";
textarea.style.pointerEvents = "auto";
const proxy = createEmptyProxyRecord(key);
textarea.addEventListener("keydown", (event) => {
onProxyTextareaKeydown(proxy, event);
}, true);
textarea.addEventListener("input", () => {
onProxyTextareaInput(proxy);
}, true);
textarea.addEventListener("focus", () => {
proxy.lastFocusedAt = performance.now();
queueRefresh();
}, true);
wrapper.appendChild(textarea);
grid.appendChild(wrapper);
const actions = getEditActionButtons(editor);
Object.assign(proxy, {
key,
wrapper,
textarea,
editor,
grid,
scrollHost: findEditorScrollHost(editor),
turn,
submitButton: actions.submitButton,
cancelButton: actions.cancelButton,
dirty: preservedDirty,
syncTimer: 0,
lastAppliedText: preservedDirty ? preservedLastAppliedText : textarea.value,
submitInFlight: false,
lastFocusedAt: performance.now(),
});
state.proxyEditors.set(key, proxy);
turn.setAttribute(TURN_ATTRIBUTES.proxyTurn, "true");
syncSingleProxyEditor(proxy);
updateProxyDirtyState(proxy);
syncAllNativeEditorPresentations();
syncRootRuntimeState();
if (preservedDirty) {
scheduleProxySync(proxy, "reattach");
}
requestAnimationFrame(() => {
syncProxyContentHeight(proxy);
if (shouldFocusProxy && proxy.textarea?.isConnected) {
const activeTextarea = proxy.textarea;
activeTextarea.focus();
activeTextarea.setSelectionRange(activeTextarea.value.length, activeTextarea.value.length);
proxy.lastFocusedAt = performance.now();
}
});
queueRefresh();
return true;
}
function maintainProxyEditor() {
const shouldUseProxy = state.config.masterEnabled && state.config.editBoostEnabled;
if (!shouldUseProxy) {
if (hasActiveProxyEditors()) {
teardownProxyEditor("proxy-disabled");
}
return;
}
const editors = getInlineEditors({ includeManagedHidden: true }).sort(compareNodeOrder);
const openKeys = new Set();
for (const editor of editors) {
const turn = editor.closest(SELECTORS.turns);
if (!turn) {
continue;
}
openKeys.add(getTurnKey(turn));
attachProxyEditor(editor);
}
for (const proxy of Array.from(state.proxyEditors.values())) {
if (!openKeys.has(proxy.key) || !proxy.turn?.isConnected || !proxy.editor?.isConnected) {
detachSingleProxyEditor(proxy, "edit-closed");
}
}
syncAllProxyEditors();
syncAllNativeEditorPresentations();
syncRootRuntimeState();
}
// SECTION: offscreen parking
function getTurnKey(turn) {
if (!(turn instanceof Element)) {
return `turn-${state.nextTurnKey++}`;
}
if (state.turnKeys.has(turn)) {
return state.turnKeys.get(turn);
}
const existingKey =
turn.getAttribute("data-turn-id") ||
turn.getAttribute("data-message-id") ||
turn.getAttribute("data-testid");
const key = existingKey ? String(existingKey) : `turn-${state.nextTurnKey++}`;
state.turnKeys.set(turn, key);
return key;
}
function getTurnPreview(turn) {
return (turn.innerText || turn.textContent || "")
.replace(/\s+/g, " ")
.trim()
.slice(0, 140);
}
function getReservedHeight(element) {
const rect = element.getBoundingClientRect();
return Math.max(48, Math.round(rect.height || element.offsetHeight || 48));
}
function distanceFromViewport(element) {
const rect = element.getBoundingClientRect();
if (rect.bottom < 0) {
return Math.abs(rect.bottom);
}
if (rect.top > window.innerHeight) {
return rect.top - window.innerHeight;
}
return 0;
}
function isWithinMargin(element, marginPx) {
const rect = element.getBoundingClientRect();
const top = -marginPx;
const bottom = window.innerHeight + marginPx;
return rect.bottom >= top && rect.top <= bottom;
}
function getOpenEditTurns() {
const turns = new Set();
for (const proxy of getProxyEditorList()) {
if (proxy.turn?.isConnected) {
turns.add(proxy.turn);
}
}
for (const editor of getVisibleInlineEditors()) {
const turn = editor.closest(SELECTORS.turns);
if (turn) {
turns.add(turn);
}
}
return turns;
}
function getActiveEditTurn() {
const primaryProxy = getPrimaryProxyEditor();
if (primaryProxy?.turn?.isConnected) {
return primaryProxy.turn;
}
const visibleEditor = getVisibleInlineEditors()[0];
if (visibleEditor) {
return visibleEditor.closest(SELECTORS.turns);
}
return null;
}
function hasAnyPrunedTurns() {
return state.prunedTurns.size > 0;
}
function createPlaceholderContent(entry, restoreHandler) {
const wrapper = document.createElement("div");
wrapper.className = `${SCRIPT_ID}-placeholder`;
wrapper.style.setProperty(`--${SCRIPT_ID}-reserved-height`, `${entry.reservedHeight}px`);
wrapper.tabIndex = 0;
wrapper.setAttribute("role", "button");
wrapper.setAttribute("aria-label", "Restore parked turn");
wrapper.title = "Restore parked turn";
wrapper.addEventListener("click", (event) => {
event.preventDefault();
restoreHandler();
});
wrapper.addEventListener("keydown", (event) => {
if (event.key !== "Enter" && event.key !== " ") {
return;
}
event.preventDefault();
restoreHandler();
});
const textWrap = document.createElement("div");
const title = document.createElement("strong");
title.textContent = "Turn parked offscreen";
const detail = document.createElement("span");
detail.textContent =
entry.preview || "This turn is parked locally and will restore automatically near the viewport.";
const meta = document.createElement("span");
meta.className = `${SCRIPT_ID}-placeholder-meta`;
meta.textContent = `Reserved ${entry.reservedHeight}px. Click anywhere to restore.`;
textWrap.append(title, detail, meta);
wrapper.append(textWrap);
return wrapper;
}
function restoreShellTurn(turn, silent = false) {
const entry = state.prunedTurns.get(turn);
if (!entry) {
return false;
}
if (!turn.isConnected) {
state.prunedTurns.delete(turn);
return false;
}
turn.replaceChildren(entry.fragment);
turn.removeAttribute(TURN_ATTRIBUTES.parkedShell);
turn.style.removeProperty(`--${SCRIPT_ID}-reserved-height`);
state.prunedTurns.delete(turn);
if (!silent) {
syncPanelControls();
queueRefresh();
}
return true;
}
function pruneTurnToShell(turn) {
if (!(turn instanceof HTMLElement) || !turn.isConnected || state.prunedTurns.has(turn)) {
return false;
}
const entry = {
key: getTurnKey(turn),
preview: getTurnPreview(turn),
reservedHeight: getReservedHeight(turn),
fragment: document.createDocumentFragment(),
prunedAt: performance.now(),
};
// The parked shell preserves the original children in memory so restore remains reversible.
while (turn.firstChild) {
entry.fragment.appendChild(turn.firstChild);
}
turn.setAttribute(TURN_ATTRIBUTES.parkedShell, "true");
turn.style.setProperty(`--${SCRIPT_ID}-reserved-height`, `${entry.reservedHeight}px`);
turn.replaceChildren(
createPlaceholderContent(entry, () => {
if (restoreShellTurn(turn)) {
setStatus("Restored one parked turn.");
}
})
);
state.prunedTurns.set(turn, entry);
return true;
}
function restoreAllConnectedTurnsImmediately() {
for (const turn of Array.from(state.prunedTurns.keys())) {
restoreShellTurn(turn, true);
}
syncPanelControls();
queueRefresh();
}
function isTailProtected(turn, turns) {
const index = turns.indexOf(turn);
if (index < 0) {
return false;
}
return index >= Math.max(0, turns.length - PARKING_TAIL_KEEP_TURNS);
}
function isTurnProtected(turn, turns) {
const openEditTurns = getOpenEditTurns();
return (
isWithinMargin(turn, state.config.keepMarginPx) ||
openEditTurns.has(turn) ||
turn.contains(document.activeElement) ||
isTailProtected(turn, turns)
);
}
function collectRestoreCandidates(forceAll = false) {
const candidates = [];
for (const [turn, entry] of state.prunedTurns.entries()) {
if (!turn.isConnected) {
state.prunedTurns.delete(turn);
continue;
}
if (forceAll || isWithinMargin(turn, state.config.warmMarginPx)) {
candidates.push({
turn,
entry,
distance: distanceFromViewport(turn),
});
}
}
candidates.sort((a, b) => {
if (forceAll) {
return compareNodeOrder(a.turn, b.turn);
}
return a.distance - b.distance;
});
return candidates;
}
function collectPruneCandidates() {
const turns = getTurnElements();
if (turns.length < PARKING_MIN_TURN_COUNT) {
return [];
}
const candidates = [];
for (const turn of turns) {
if (!(turn instanceof HTMLElement) || !turn.isConnected || state.prunedTurns.has(turn)) {
continue;
}
if (isTurnProtected(turn, turns)) {
continue;
}
const distance = distanceFromViewport(turn);
if (distance <= state.config.keepMarginPx) {
continue;
}
candidates.push({ turn, distance });
}
candidates.sort((a, b) => b.distance - a.distance);
return candidates;
}
function canPruneNow() {
if (!state.config.masterEnabled || !state.config.parkingEnabled || state.forceRestoreAll) {
return false;
}
if (document.hidden) {
return false;
}
if (performance.now() - state.scrollSettledAt < PARKING_SCROLL_SETTLE_MS) {
return false;
}
if (hasActiveProxyEditors() || getVisibleInlineEditors().length) {
return false;
}
return true;
}
function processRestoreBatch(forceAll = false) {
const candidates = collectRestoreCandidates(forceAll);
if (!candidates.length) {
return 0;
}
let restored = 0;
for (const candidate of candidates) {
if (restored >= state.config.restoreBatchSize) {
break;
}
if (restoreShellTurn(candidate.turn, true)) {
restored += 1;
}
}
if (restored) {
syncPanelControls();
queueRefresh();
}
return restored;
}
function processPruneBatch() {
const candidates = collectPruneCandidates();
if (!candidates.length) {
return 0;
}
let pruned = 0;
for (const candidate of candidates) {
if (pruned >= state.config.pruneBatchSize) {
break;
}
if (pruneTurnToShell(candidate.turn)) {
pruned += 1;
}
}
if (pruned) {
syncPanelControls();
queueRefresh();
}
return pruned;
}
function performParkingWork() {
if (state.parkingWorkInProgress) {
state.parkingWorkPending = true;
return;
}
state.parkingWorkInProgress = true;
try {
const restoringAll = state.forceRestoreAll;
const restored = processRestoreBatch(restoringAll);
let pruned = 0;
if (restoringAll && !hasAnyPrunedTurns()) {
state.forceRestoreAll = false;
setStatus("All parked turns were restored.", {
reason: "restore-complete",
});
}
if (canPruneNow()) {
pruned = processPruneBatch();
}
if (
(state.forceRestoreAll && hasAnyPrunedTurns()) ||
restored >= state.config.restoreBatchSize ||
pruned >= state.config.pruneBatchSize
) {
requestParkingWork("follow-up");
}
} finally {
state.parkingWorkInProgress = false;
}
}
function requestParkingWork(reason = "parking") {
noteActivity(reason);
state.parkingWorkPending = true;
if (state.parkingWorkScheduled) {
return;
}
state.parkingWorkScheduled = true;
const run = () => {
state.parkingWorkScheduled = false;
if (!state.parkingWorkPending) {
return;
}
state.parkingWorkPending = false;
performParkingWork();
};
if ("requestIdleCallback" in window) {
window.requestIdleCallback(run, { timeout: PARKING_WORK_TIMEOUT_MS });
} else {
setTimeout(run, 0);
}
}
function requestRestoreAll(message) {
if (!hasAnyPrunedTurns()) {
syncPanelControls();
queueRefresh();
return;
}
state.forceRestoreAll = true;
setStatus(message, {
reason: "restore-all",
durationMs: 2600,
});
requestParkingWork("restore-all");
}
// SECTION: metrics, observers, runtime, init
function getRecentHitchMax() {
const cutoff = performance.now() - 10000;
state.longTasks = state.longTasks.filter((entry) => entry.at >= cutoff);
state.frameGaps = state.frameGaps.filter((entry) => entry.at >= cutoff);
const maxLongTask = state.longTasks.length
? Math.max(...state.longTasks.map((entry) => entry.duration))
: 0;
const maxFrameGap = state.frameGaps.length
? Math.max(...state.frameGaps.map((entry) => entry.duration))
: 0;
return Math.max(maxLongTask, maxFrameGap, 0);
}
function getEditStatusLabel() {
if (!state.config.masterEnabled || !state.config.editBoostEnabled) {
return "Off";
}
const proxies = getProxyEditorList();
if (proxies.length) {
return proxies.some((proxy) => proxy.dirty) ? "Active*" : "Active";
}
return "Ready";
}
function getSnapshot() {
const statusInfo = getCurrentStatusInfo();
const turns = getTurnElements();
return {
domNodes: document.getElementsByTagName("*").length,
turns: turns.length,
parkedTurns: state.prunedTurns.size,
hitchMax: getRecentHitchMax(),
statusLabel: statusInfo.label,
statusDetail: statusInfo.detail,
editStatus: getEditStatusLabel(),
};
}
function setField(field, value) {
if (field) {
field.textContent = value;
}
}
function renderSnapshot(snapshot) {
if (!state.panel?.isConnected) {
return;
}
setField(state.summaryFields.domNodes, formatInteger(snapshot.domNodes));
setField(state.summaryFields.parkedTurns, formatInteger(snapshot.parkedTurns));
setField(state.summaryFields.statusLabel, snapshot.statusLabel);
setField(state.metricFields.domNodes, formatInteger(snapshot.domNodes));
setField(state.metricFields.turns, formatInteger(snapshot.turns));
setField(state.metricFields.parkedTurns, formatInteger(snapshot.parkedTurns));
setField(state.metricFields.hitchMax, formatMs(snapshot.hitchMax));
if (state.summaryFields.statusLabel) {
state.summaryFields.statusLabel.title = `${TOOLTIPS.summary.statusLabel} Current detail: ${snapshot.statusDetail}`;
}
syncPanelControls();
}
function startPerformanceObservers() {
if (
typeof PerformanceObserver === "undefined" ||
!Array.isArray(PerformanceObserver.supportedEntryTypes)
) {
return;
}
if (PerformanceObserver.supportedEntryTypes.includes("longtask")) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
pushRolling(state.longTasks, {
at: performance.now(),
duration: entry.duration,
});
}
queueRefresh();
});
observer.observe({ type: "longtask", buffered: true });
}
}
function startFrameMonitor() {
if (state.frameMonitorStarted) {
return;
}
state.frameMonitorStarted = true;
let lastFrame = performance.now();
const tick = (now) => {
const gap = now - lastFrame;
lastFrame = now;
if (gap >= FRAME_GAP_THRESHOLD_MS) {
pushRolling(state.frameGaps, {
at: now,
duration: gap,
});
}
window.requestAnimationFrame(tick);
};
window.requestAnimationFrame(tick);
}
function startMutationObserver() {
if (!document.documentElement || state.rootObserver) {
return;
}
const observer = new MutationObserver((entries) => {
for (const entry of entries) {
if (state.host && (entry.target === state.host || state.host.contains(entry.target))) {
continue;
}
scheduleRuntimeMaintenance("mutation");
break;
}
});
observer.observe(document.documentElement, {
childList: true,
subtree: true,
});
state.rootObserver = observer;
}
function handleRouteChangeIfNeeded() {
if (location.href === state.routeUrl) {
return false;
}
noteActivity("route-change");
state.routeUrl = location.href;
teardownProxyEditor("route-change");
restoreAllConnectedTurnsImmediately();
setStatus("Conversation route changed. Runtime state was reset for the new page.", {
reason: "route-change",
durationMs: 2600,
});
queueRefresh();
return true;
}
function performRuntimeMaintenance(reason = "runtime") {
noteActivity(reason);
ensureUiMounted();
syncViewportSizing();
if (handleRouteChangeIfNeeded()) {
syncRootRuntimeState();
}
maintainProxyEditor();
syncRootRuntimeState();
if (
state.config.masterEnabled &&
state.config.parkingEnabled &&
!hasActiveProxyEditors()
) {
requestParkingWork(reason);
}
queueRefresh();
}
function onDocumentPointerDown(event) {
const target = event.target;
if (!(target instanceof Element) || !hasActiveProxyEditors()) {
return;
}
for (const proxy of getProxyEditorList()) {
if (
proxy.submitButton?.isConnected &&
!isButtonDisabled(proxy.submitButton) &&
(target === proxy.submitButton || proxy.submitButton.contains(target))
) {
syncProxyToNative(proxy, "submit-pointerdown");
return;
}
if (
proxy.cancelButton?.isConnected &&
(target === proxy.cancelButton || proxy.cancelButton.contains(target))
) {
clearProxySyncTimer(proxy);
}
}
}
function registerGlobalHandlers() {
document.addEventListener("pointerdown", onDocumentPointerDown, true);
window.addEventListener(
"scroll",
() => {
const nextScrollY = window.scrollY || 0;
if (nextScrollY !== state.lastScrollY) {
state.lastScrollY = nextScrollY;
state.scrollSettledAt = performance.now();
}
noteActivity("scroll");
queueRefresh();
if (state.config.parkingEnabled) {
requestParkingWork("scroll");
}
},
true
);
window.addEventListener("resize", () => {
syncViewportSizing();
scheduleRuntimeMaintenance("resize");
});
if (window.visualViewport) {
window.visualViewport.addEventListener("resize", () => {
syncViewportSizing();
});
}
document.addEventListener("focusin", () => {
scheduleRuntimeMaintenance("focusin");
});
document.addEventListener("focusout", () => {
scheduleRuntimeMaintenance("focusout");
});
}
function summarizeNumericList(values) {
const numericValues = values
.map((value) => Number(value))
.filter((value) => Number.isFinite(value));
if (!numericValues.length) {
return {
count: 0,
};
}
const total = numericValues.reduce((sum, value) => sum + value, 0);
return {
count: numericValues.length,
min: Math.round(Math.min(...numericValues)),
max: Math.round(Math.max(...numericValues)),
avg: Math.round(total / numericValues.length),
};
}
function sanitizeRoutePath(pathname) {
const rawPath = String(pathname || "/");
const segments = rawPath.split("/");
const sanitizedSegments = segments.map((segment, index) => {
if (!segment || index === 0) {
return segment;
}
if (
/^[0-9a-f]{8}-[0-9a-f-]{8,}$/i.test(segment) ||
/^[A-Za-z0-9_-]{20,}$/.test(segment) ||
segment.length >= 28
) {
return "[id]";
}
return segment;
});
return sanitizedSegments.join("/") || "/";
}
function getPageType() {
if (/^\/c\/[^/]+/.test(location.pathname)) {
return "conversation";
}
if (/^\/g\/[^/]+/.test(location.pathname)) {
return "gpt";
}
if (location.pathname === "/") {
return "home";
}
return "other";
}
function getFeatureSupportSnapshot() {
const supportedEntryTypes =
typeof PerformanceObserver !== "undefined" &&
Array.isArray(PerformanceObserver.supportedEntryTypes)
? PerformanceObserver.supportedEntryTypes.slice()
: [];
return {
requestIdleCallback: "requestIdleCallback" in window,
visualViewport: Boolean(window.visualViewport),
performanceObserver: typeof PerformanceObserver !== "undefined",
longtaskObserver: supportedEntryTypes.includes("longtask"),
clipboardWriteText: Boolean(navigator.clipboard?.writeText),
execCommandCopy: typeof document.execCommand === "function",
contentVisibility:
typeof CSS !== "undefined" && typeof CSS.supports === "function"
? CSS.supports("content-visibility", "auto")
: false,
containIntrinsicSize:
typeof CSS !== "undefined" && typeof CSS.supports === "function"
? CSS.supports("contain-intrinsic-size", "auto 900px")
: false,
};
}
function getViewportSnapshot() {
const viewport = window.visualViewport;
return {
innerWidth: Math.round(window.innerWidth || 0),
innerHeight: Math.round(window.innerHeight || 0),
visualViewportWidth: viewport ? Math.round(viewport.width || 0) : null,
visualViewportHeight: viewport ? Math.round(viewport.height || 0) : null,
visualViewportScale: viewport ? Number(viewport.scale || 1) : 1,
devicePixelRatio: Number(window.devicePixelRatio || 1),
scrollY: Math.round(window.scrollY || 0),
};
}
function getElementRectSnapshot(element) {
if (!(element instanceof Element) || !element.isConnected) {
return null;
}
const rect = element.getBoundingClientRect();
return {
top: Math.round(rect.top),
left: Math.round(rect.left),
width: Math.round(rect.width),
height: Math.round(rect.height),
bottom: Math.round(rect.bottom),
right: Math.round(rect.right),
};
}
function getPanelSnapshot() {
const rect = state.panel?.isConnected ? state.panel.getBoundingClientRect() : null;
return {
mounted: Boolean(state.panel?.isConnected),
mode: getPanelMode(),
collapsed: Boolean(state.config.panelCollapsed),
iconOnly: Boolean(state.config.panelIconOnly),
widthPx: rect ? Math.round(rect.width) : null,
heightPx: rect ? Math.round(rect.height) : null,
};
}
function getRootAttributeSnapshot() {
const root = document.documentElement;
return {
masterEnabled: root?.getAttribute(ROOT_ATTRIBUTES.masterEnabled) === "true",
foundationEnabled: root?.getAttribute(ROOT_ATTRIBUTES.foundationEnabled) === "true",
heavyBlocksEnabled: root?.getAttribute(ROOT_ATTRIBUTES.heavyBlocksEnabled) === "true",
parkingEnabled: root?.getAttribute(ROOT_ATTRIBUTES.parkingEnabled) === "true",
proxyActive: root?.getAttribute(ROOT_ATTRIBUTES.proxyActive) === "true",
};
}
function getConversationSnapshot(turns) {
const allTurns = Array.isArray(turns) ? turns : getTurnElements();
const activeEditTurn = getActiveEditTurn();
const parkedEntries = Array.from(state.prunedTurns.values());
const parkedAgesMs = parkedEntries.map((entry) => performance.now() - entry.prunedAt);
const reservedHeightsPx = parkedEntries.map((entry) => entry.reservedHeight);
const proxyTextareaCount = document.querySelectorAll(
`[${PROXY_ATTRIBUTES.wrapper}="true"] textarea`
).length;
const turnTextareaCount =
document.querySelectorAll(`${SELECTORS.turns} textarea`).length - proxyTextareaCount;
const turnTextboxCount = document.querySelectorAll(
`${SELECTORS.turns} [contenteditable="true"][role="textbox"]`
).length;
return {
turnCount: allTurns.length,
detectedComposer: Boolean(document.querySelector(SELECTORS.composer)),
visibleInlineEditors: getVisibleInlineEditors().length,
totalInlineEditors: getInlineEditors({ includeManagedHidden: true }).length,
turnTextareaCount,
proxyTextareaCount,
turnTextboxCount,
proxyGridHostCount: document.querySelectorAll(`[${PROXY_ATTRIBUTES.gridHost}="true"]`).length,
heavyBlockCount: document.querySelectorAll(SELECTORS.heavyBlocks).length,
parkedShellCount: document.querySelectorAll(
`${SELECTORS.turns}[${TURN_ATTRIBUTES.parkedShell}="true"]`
).length,
proxyTurnCount: document.querySelectorAll(
`${SELECTORS.turns}[${TURN_ATTRIBUTES.proxyTurn}="true"]`
).length,
activeEditTurnIndex: activeEditTurn ? allTurns.indexOf(activeEditTurn) + 1 : 0,
parkedReservedHeightPx: summarizeNumericList(reservedHeightsPx),
parkedAgeMs: summarizeNumericList(parkedAgesMs),
};
}
function getProxyEditorSnapshot(turns) {
const proxies = getProxyEditorList();
const primaryProxy = getPrimaryProxyEditor();
const allTurns = Array.isArray(turns) ? turns : getTurnElements();
return {
active: proxies.length > 0,
activeCount: proxies.length,
dirtyCount: proxies.filter((proxy) => proxy.dirty).length,
submitInFlightCount: proxies.filter((proxy) => proxy.submitInFlight).length,
nativePresentationAttributeCount: document.querySelectorAll(
`[${PROXY_ATTRIBUTES.nativeHidden}], [${PROXY_ATTRIBUTES.nativeGuide}], [${PROXY_ATTRIBUTES.nativeTextMuted}], [${PROXY_ATTRIBUTES.diagnosticsLabel}], [${PROXY_ATTRIBUTES.diagnosticsRole}]`
).length,
primary: !primaryProxy
? null
: {
editorConnected: Boolean(primaryProxy.editor?.isConnected),
scrollHostConnected: Boolean(primaryProxy.scrollHost?.isConnected),
submitButtonDetected: Boolean(primaryProxy.submitButton?.isConnected),
cancelButtonDetected: Boolean(primaryProxy.cancelButton?.isConnected),
nativeHidden: Boolean(
primaryProxy.editor?.getAttribute(PROXY_ATTRIBUTES.nativeHidden) === "true"
),
nativeGuideVisible: Boolean(
primaryProxy.editor?.getAttribute(PROXY_ATTRIBUTES.nativeGuide) === "true"
),
gridTag: primaryProxy.grid?.tagName || null,
gridDisplay: primaryProxy.grid?.isConnected
? window.getComputedStyle(primaryProxy.grid).display
: null,
editorRect: getElementRectSnapshot(primaryProxy.editor),
scrollHostRect: getElementRectSnapshot(primaryProxy.scrollHost),
gridRect: getElementRectSnapshot(primaryProxy.grid),
proxyRect: getElementRectSnapshot(primaryProxy.wrapper),
submitButtonRect: getElementRectSnapshot(primaryProxy.submitButton),
cancelButtonRect: getElementRectSnapshot(primaryProxy.cancelButton),
submitInFlight: Boolean(primaryProxy.submitInFlight),
editorTag: primaryProxy.editor?.tagName || null,
editorRole: primaryProxy.editor?.getAttribute?.("role") || null,
turnIndex: primaryProxy.turn ? allTurns.indexOf(primaryProxy.turn) + 1 : 0,
proxyTextLength:
primaryProxy.textarea instanceof HTMLTextAreaElement ? primaryProxy.textarea.value.length : 0,
lastAppliedTextLength: primaryProxy.lastAppliedText.length,
selectionStart:
primaryProxy.textarea instanceof HTMLTextAreaElement &&
typeof primaryProxy.textarea.selectionStart === "number"
? primaryProxy.textarea.selectionStart
: null,
selectionEnd:
primaryProxy.textarea instanceof HTMLTextAreaElement &&
typeof primaryProxy.textarea.selectionEnd === "number"
? primaryProxy.textarea.selectionEnd
: null,
},
proxies: proxies.slice(0, 6).map((proxy) => ({
turnIndex: proxy.turn ? allTurns.indexOf(proxy.turn) + 1 : 0,
dirty: Boolean(proxy.dirty),
submitInFlight: Boolean(proxy.submitInFlight),
editorConnected: Boolean(proxy.editor?.isConnected),
submitButtonDetected: Boolean(proxy.submitButton?.isConnected),
cancelButtonDetected: Boolean(proxy.cancelButton?.isConnected),
nativeHidden: Boolean(proxy.editor?.getAttribute(PROXY_ATTRIBUTES.nativeHidden) === "true"),
nativeGuideVisible: Boolean(proxy.editor?.getAttribute(PROXY_ATTRIBUTES.nativeGuide) === "true"),
gridTag: proxy.grid?.tagName || null,
gridDisplay: proxy.grid?.isConnected ? window.getComputedStyle(proxy.grid).display : null,
scrollHostRect: getElementRectSnapshot(proxy.scrollHost),
gridRect: getElementRectSnapshot(proxy.grid),
proxyTextLength: proxy.textarea instanceof HTMLTextAreaElement ? proxy.textarea.value.length : 0,
lastAppliedTextLength: proxy.lastAppliedText.length,
})),
};
}
function getParkingSnapshot() {
const parkedEntries = Array.from(state.prunedTurns.values());
return {
active: state.config.masterEnabled && state.config.parkingEnabled,
parkedTurns: state.prunedTurns.size,
workScheduled: Boolean(state.parkingWorkScheduled),
workPending: Boolean(state.parkingWorkPending),
workInProgress: Boolean(state.parkingWorkInProgress),
forceRestoreAll: Boolean(state.forceRestoreAll),
presetKey: getParkingPresetKey(),
reservedHeightPx: summarizeNumericList(
parkedEntries.map((entry) => entry.reservedHeight)
),
};
}
function getRuntimeSnapshot() {
return {
initComplete: Boolean(state.initComplete),
frameMonitorStarted: Boolean(state.frameMonitorStarted),
mutationObserverActive: Boolean(state.rootObserver),
samplingLoopActive: Boolean(state.sampleTimer),
runtimeMaintenanceQueued: Boolean(state.runtimeMaintenanceQueued),
lastActivityReason: state.lastActivityReason,
lastActivityAgeMs: Math.round(performance.now() - state.lastActivityAt),
documentHidden: Boolean(document.hidden),
documentHasFocus: Boolean(document.hasFocus?.()),
};
}
function getTimingSnapshot() {
return {
recentHitchMaxMs: getRecentHitchMax(),
longTasks: state.longTasks.slice(-10),
frameGaps: state.frameGaps.slice(-10),
longTaskSummaryMs: summarizeNumericList(
state.longTasks.slice(-10).map((entry) => entry.duration)
),
frameGapSummaryMs: summarizeNumericList(
state.frameGaps.slice(-10).map((entry) => entry.duration)
),
};
}
function buildSupportSnapshot() {
const turns = getTurnElements();
const snapshot = getSnapshot();
return {
capturedAt: new Date().toISOString(),
scriptVersion: SCRIPT_VERSION,
page: {
origin: location.origin,
path: sanitizeRoutePath(location.pathname),
pageType: getPageType(),
hasSearch: Boolean(location.search),
hasHash: Boolean(location.hash),
},
config: { ...state.config },
status: getCurrentStatusInfo(),
snapshot,
viewport: getViewportSnapshot(),
panel: getPanelSnapshot(),
runtime: getRuntimeSnapshot(),
rootAttributes: getRootAttributeSnapshot(),
conversation: getConversationSnapshot(turns),
proxyEditor: getProxyEditorSnapshot(turns),
parking: getParkingSnapshot(),
timings: getTimingSnapshot(),
featureSupport: getFeatureSupportSnapshot(),
browser: {
userAgent: navigator.userAgent,
language: navigator.language || null,
},
};
}
async function copyTextToClipboard(text) {
if (navigator.clipboard?.writeText) {
try {
await navigator.clipboard.writeText(text);
return "clipboard-api";
} catch {
// Fall through to the legacy copy path if the async clipboard API is blocked.
}
}
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.setAttribute("readonly", "true");
textarea.style.position = "fixed";
textarea.style.left = "-9999px";
textarea.style.top = "0";
textarea.style.opacity = "0";
document.body.appendChild(textarea);
textarea.select();
textarea.setSelectionRange(0, textarea.value.length);
try {
const copied = Boolean(document.execCommand && document.execCommand("copy"));
if (!copied) {
throw new Error("Legacy copy command did not succeed.");
}
return "execCommand";
} finally {
textarea.remove();
}
}
async function copySupportSnapshot() {
noteActivity("support");
const payload = buildSupportSnapshot();
const serialized = JSON.stringify(payload, null, 2);
try {
await copyTextToClipboard(serialized);
setStatus("Support snapshot copied. Paste it into your bug report.", {
reason: "support",
durationMs: 3800,
});
showPanelNotice("Support snapshot copied. Paste it into your bug report.", {
tone: "success",
durationMs: 3800,
});
} catch (error) {
console.error(`[${SCRIPT_ID}] Failed to copy support snapshot`, error);
console.log(serialized);
setStatus("Clipboard copy failed. The support snapshot was written to the console for manual copy.", {
reason: "support",
durationMs: 4200,
});
showPanelNotice("Clipboard copy failed. Copy the support snapshot from the console instead.", {
tone: "error",
durationMs: 4200,
});
}
}
function startSamplingLoop() {
if (state.sampleTimer) {
return;
}
state.sampleTimer = window.setInterval(() => {
performRuntimeMaintenance("sample");
}, SAMPLE_INTERVAL_MS);
}
function attemptEarlyBoot() {
injectPageStyles();
syncRootRuntimeState();
}
function init() {
ensureUiMounted();
syncRootRuntimeState();
startPerformanceObservers();
startFrameMonitor();
startMutationObserver();
startSamplingLoop();
registerGlobalHandlers();
performRuntimeMaintenance("init");
state.initComplete = true;
queueRefresh();
}
attemptEarlyBoot();
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init, { once: true });
} else {
init();
}
})();