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.2
// @match *://*.chatgpt.com/*
// @supportURL https://greasyfork.org/en/scripts/526998/feedback
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
"use strict";
// SECTION: constants, state, storage, utilities
const SCRIPT_ID = "cgpfp001";
const SCRIPT_VERSION = "1.1.2";
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 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`,
nativeHidden: `data-${SCRIPT_ID}-native-hidden`,
nativeGuide: `data-${SCRIPT_ID}-native-guide`,
nativeTextMuted: `data-${SCRIPT_ID}-native-text-muted`,
});
const SELECTORS = Object.freeze({
turns: '[data-testid^="conversation-turn-"]',
composer:
'#prompt-textarea.ProseMirror, .ProseMirror#prompt-textarea, div[contenteditable="true"][role="textbox"]#prompt-textarea',
inlineEditors:
'[data-testid^="conversation-turn-"] [contenteditable="true"][role="textbox"], [data-testid^="conversation-turn-"] 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,
showNativeGuide: false,
autoFocusProxy: true,
parkingEnabled: true,
keepMarginPx: 1200,
warmMarginPx: 2200,
pruneBatchSize: 2,
restoreBatchSize: 1,
});
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.",
showNativeGuide:
"Shows a faint native editor guide without duplicate text. Turn it on to inspect overlay alignment; turn it off for the cleanest editing view.",
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.",
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.",
}),
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 createEmptyProxyState() {
return {
wrapper: null,
scrollport: null,
textarea: null,
editor: null,
scrollHost: null,
turn: null,
submitButton: null,
cancelButton: null,
dirty: false,
syncTimer: 0,
lastAppliedText: "",
scrollTop: 0,
submitInFlight: false,
};
}
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,
proxyEditor: createEmptyProxyState(),
turnKeys: new WeakMap(),
nextTurnKey: 1,
startedAt: performance.now(),
initComplete: false,
lastActivityReason: "starting",
lastActivityAt: performance.now(),
noticeHideTimer: 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 "showNativeGuide":
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.showNativeGuide === undefined && "hideNativeEditor" in rawConfig) {
nextConfig.showNativeGuide = !Boolean(rawConfig.hideNativeEditor);
}
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 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;
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 (state.proxyEditor.submitInFlight) {
label = "Optimising";
detail = "Optimising while saving an inline edit.";
} else if (state.proxyEditor.wrapper?.isConnected && (state.proxyEditor.dirty || state.proxyEditor.syncTimer)) {
label = "Optimising";
detail = "Optimising while editing.";
} else if (state.proxyEditor.wrapper?.isConnected) {
label = "Editing";
detail = "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, Boolean(state.proxyEditor.wrapper?.isConnected));
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;
}
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} 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[data-kind="checkbox"] {
flex-direction: row;
align-items: center;
justify-content: space-between;
}
#${PANEL_ID} .${SCRIPT_ID}-control-label {
color: #61756a;
font-size: 11px;
}
#${PANEL_ID} .${SCRIPT_ID}-control input[type="number"] {
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}-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: space-between;
gap: 8px;
padding-top: 2px;
color: #5d7266;
font-size: 11px;
}
#${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 {
position: fixed;
left: 0;
top: 0;
z-index: 2147483645;
display: block;
box-sizing: border-box;
padding: 0;
border: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
pointer-events: none;
overflow: hidden;
contain: layout paint style;
isolation: isolate;
}
.${SCRIPT_ID}-proxy-scrollport {
position: absolute;
inset: 0;
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
pointer-events: auto;
scrollbar-gutter: stable both-edges;
}
.${SCRIPT_ID}-proxy-shell textarea {
display: block;
width: 100%;
min-height: 100%;
height: auto;
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 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 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("showNativeGuide", "Show Native Guide", TOOLTIPS.controls.showNativeGuide)
);
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 = "Experimental layer with mixed benefits. It uses shell-only parking and conservative restore margins.";
const parkingFieldset = document.createElement("fieldset");
state.fieldsets.parking = parkingFieldset;
const parkingGrid = document.createElement("div");
parkingGrid.className = `${SCRIPT_ID}-grid`;
parkingGrid.append(
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 supportLink = document.createElement("a");
supportLink.href = DONATION_URL;
supportLink.target = "_blank";
supportLink.rel = "noopener noreferrer";
supportLink.textContent = "Support the Dev";
footer.append(reportLink, 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.showNativeGuide) {
inputs.showNativeGuide.checked = config.showNativeGuide;
}
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);
}
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 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 (state.proxyEditor.wrapper?.isConnected) {
scheduleProxySync("delay-change");
}
break;
case "showNativeGuide":
applyNativeEditorPresentation();
setStatus(
state.config.showNativeGuide
? "The native editor guide is visible without duplicate text."
: "The proxy editor is using the clean editing view again.",
{ reason: "native-guide-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 getInlineEditors(options = {}) {
const includeManagedHidden = Boolean(options.includeManagedHidden);
return Array.from(document.querySelectorAll(SELECTORS.inlineEditors)).filter((element) => {
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() {
if (!state.proxyEditor.syncTimer) {
return;
}
window.clearTimeout(state.proxyEditor.syncTimer);
state.proxyEditor.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 syncProxyTextareaVisualStyle() {
const textarea = state.proxyEditor.textarea;
const editor = state.proxyEditor.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 getProxyScrollTop() {
const scrollport = state.proxyEditor.scrollport;
return scrollport?.isConnected ? scrollport.scrollTop : state.proxyEditor.scrollTop || 0;
}
function restoreProxyScrollTop(value = state.proxyEditor.scrollTop || 0) {
const scrollport = state.proxyEditor.scrollport;
if (!scrollport?.isConnected) {
return;
}
const nextValue = Math.max(0, Math.round(Number(value) || 0));
scrollport.scrollTop = nextValue;
state.proxyEditor.scrollTop = nextValue;
}
function syncProxyContentHeight() {
const textarea = state.proxyEditor.textarea;
const wrapper = state.proxyEditor.wrapper;
const scrollport = state.proxyEditor.scrollport;
if (!textarea || !wrapper?.isConnected) {
return 0;
}
const minHeight = Math.max(44, Math.round(wrapper.getBoundingClientRect().height || 0));
const previousScrollTop = getProxyScrollTop();
textarea.style.height = "auto";
textarea.style.minHeight = `${minHeight}px`;
const contentHeight = Math.max(minHeight, Math.ceil(textarea.scrollHeight || 0));
textarea.style.height = `${contentHeight}px`;
if (scrollport?.isConnected) {
restoreProxyScrollTop(previousScrollTop);
}
return contentHeight;
}
function syncProxyOverlayPosition() {
const wrapper = state.proxyEditor.wrapper;
const anchor = state.proxyEditor.scrollHost || state.proxyEditor.editor;
const textarea = state.proxyEditor.textarea;
const scrollport = state.proxyEditor.scrollport;
if (!wrapper?.isConnected || !anchor?.isConnected) {
return false;
}
const rect = anchor.getBoundingClientRect();
const viewportWidth = window.visualViewport?.width || window.innerWidth || 0;
const viewportHeight = window.visualViewport?.height || window.innerHeight || 0;
if (
rect.width < 40 ||
rect.height < 20 ||
rect.bottom < 0 ||
rect.top > viewportHeight ||
rect.right < 0 ||
rect.left > viewportWidth
) {
wrapper.style.display = "none";
return false;
}
const overlayHeight = Math.max(44, Math.round(rect.height || anchor.clientHeight || anchor.scrollHeight || 44));
wrapper.style.display = "block";
wrapper.style.left = `${Math.round(rect.left)}px`;
wrapper.style.top = `${Math.round(rect.top)}px`;
wrapper.style.width = `${Math.round(rect.width)}px`;
wrapper.style.height = `${overlayHeight}px`;
wrapper.style.minHeight = `${overlayHeight}px`;
if (scrollport) {
scrollport.style.height = "100%";
scrollport.style.maxHeight = "100%";
}
if (textarea) {
textarea.style.width = "100%";
syncProxyContentHeight();
}
return true;
}
function applyNativeEditorPresentation() {
const editor = state.proxyEditor.editor;
const scrollHost = state.proxyEditor.scrollHost;
if (!editor?.isConnected) {
return;
}
const proxyActive =
state.config.masterEnabled &&
state.config.editBoostEnabled &&
Boolean(state.proxyEditor.wrapper?.isConnected);
if (proxyActive && !state.config.showNativeGuide) {
editor.setAttribute(PROXY_ATTRIBUTES.nativeHidden, "true");
editor.removeAttribute(PROXY_ATTRIBUTES.nativeGuide);
editor.removeAttribute(PROXY_ATTRIBUTES.nativeTextMuted);
if (scrollHost?.isConnected && scrollHost !== editor) {
scrollHost.setAttribute(PROXY_ATTRIBUTES.nativeHidden, "true");
scrollHost.removeAttribute(PROXY_ATTRIBUTES.nativeGuide);
scrollHost.removeAttribute(PROXY_ATTRIBUTES.nativeTextMuted);
}
return;
}
editor.removeAttribute(PROXY_ATTRIBUTES.nativeHidden);
if (scrollHost?.isConnected && scrollHost !== editor) {
scrollHost.removeAttribute(PROXY_ATTRIBUTES.nativeHidden);
}
if (proxyActive && state.config.showNativeGuide) {
editor.setAttribute(PROXY_ATTRIBUTES.nativeTextMuted, "true");
if (scrollHost?.isConnected && scrollHost !== editor) {
editor.removeAttribute(PROXY_ATTRIBUTES.nativeGuide);
scrollHost.setAttribute(PROXY_ATTRIBUTES.nativeGuide, "true");
scrollHost.removeAttribute(PROXY_ATTRIBUTES.nativeTextMuted);
} else {
editor.setAttribute(PROXY_ATTRIBUTES.nativeGuide, "true");
}
} else {
editor.removeAttribute(PROXY_ATTRIBUTES.nativeGuide);
editor.removeAttribute(PROXY_ATTRIBUTES.nativeTextMuted);
if (scrollHost?.isConnected && scrollHost !== editor) {
scrollHost.removeAttribute(PROXY_ATTRIBUTES.nativeGuide);
scrollHost.removeAttribute(PROXY_ATTRIBUTES.nativeTextMuted);
}
}
}
function updateProxyDirtyState() {
const textarea = state.proxyEditor.textarea;
if (!textarea) {
state.proxyEditor.dirty = false;
return;
}
state.proxyEditor.dirty = textarea.value !== state.proxyEditor.lastAppliedText;
}
// SECTION: proxy editor
function syncProxyToNative(reason = "idle", showStatus = false) {
const textarea = state.proxyEditor.textarea;
const editor = state.proxyEditor.editor;
if (!textarea || !editor || !editor.isConnected) {
return false;
}
clearProxySyncTimer();
const text = textarea.value;
const avoidFocus = reason !== "manual" && reason !== "proxy-submit" && reason !== "submit-pointerdown";
const wasFocused = document.activeElement === textarea || state.shadowRoot?.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;
}
state.proxyEditor.lastAppliedText = text;
updateProxyDirtyState();
if (!avoidFocus && wasFocused && state.proxyEditor.textarea?.isConnected) {
requestAnimationFrame(() => {
const activeTextarea = state.proxyEditor.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(reason = "idle") {
const textarea = state.proxyEditor.textarea;
if (!textarea) {
return;
}
updateProxyDirtyState();
clearProxySyncTimer();
if (!state.proxyEditor.dirty) {
queueRefresh();
return;
}
state.proxyEditor.syncTimer = window.setTimeout(() => {
syncProxyToNative(reason);
}, state.config.syncDelayMs);
queueRefresh();
}
async function submitProxyEdit(trigger = "button") {
const submitButton = state.proxyEditor.submitButton;
if (!submitButton?.isConnected || isButtonDisabled(submitButton) || state.proxyEditor.submitInFlight) {
return false;
}
state.proxyEditor.submitInFlight = true;
clearProxySyncTimer();
const synced = syncProxyToNative("proxy-submit");
if (!synced) {
state.proxyEditor.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(() => {
state.proxyEditor.submitInFlight = false;
}, 250);
});
});
return true;
}
function onProxyTextareaInput() {
state.proxyEditor.dirty = true;
scheduleProxySync("input");
}
function onProxyTextareaKeydown(event) {
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
event.preventDefault();
void submitProxyEdit("shortcut");
return;
}
if (event.key === "Escape" && state.proxyEditor.cancelButton?.isConnected) {
event.preventDefault();
state.proxyEditor.cancelButton.click();
}
}
function onProxyScroll(event) {
if (event.currentTarget instanceof HTMLElement) {
state.proxyEditor.scrollTop = event.currentTarget.scrollTop;
}
}
function detachProxyEditor(reason = "detach") {
clearProxySyncTimer();
if (state.proxyEditor.editor?.isConnected) {
state.proxyEditor.editor.removeAttribute(PROXY_ATTRIBUTES.nativeHidden);
state.proxyEditor.editor.removeAttribute(PROXY_ATTRIBUTES.nativeGuide);
state.proxyEditor.editor.removeAttribute(PROXY_ATTRIBUTES.nativeTextMuted);
}
if (state.proxyEditor.scrollHost?.isConnected && state.proxyEditor.scrollHost !== state.proxyEditor.editor) {
state.proxyEditor.scrollHost.removeAttribute(PROXY_ATTRIBUTES.nativeHidden);
state.proxyEditor.scrollHost.removeAttribute(PROXY_ATTRIBUTES.nativeGuide);
state.proxyEditor.scrollHost.removeAttribute(PROXY_ATTRIBUTES.nativeTextMuted);
}
if (state.proxyEditor.turn?.isConnected) {
state.proxyEditor.turn.removeAttribute(TURN_ATTRIBUTES.proxyTurn);
}
if (state.proxyEditor.wrapper?.isConnected) {
state.proxyEditor.wrapper.remove();
}
state.proxyEditor = createEmptyProxyState();
syncRootRuntimeState();
if (reason !== "detach") {
queueRefresh();
}
}
function teardownProxyEditor(reason = "detach") {
detachProxyEditor(reason);
}
function attachProxyEditor(editor) {
if (!(editor instanceof HTMLElement) || !editor.isConnected) {
return false;
}
const turn = editor.closest(SELECTORS.turns);
if (!turn) {
return false;
}
const existing = state.proxyEditor;
if (existing.editor === editor && existing.wrapper?.isConnected) {
const actions = getEditActionButtons(editor);
existing.scrollHost = findEditorScrollHost(editor);
existing.submitButton = actions.submitButton;
existing.cancelButton = actions.cancelButton;
syncProxyTextareaVisualStyle();
applyNativeEditorPresentation();
syncProxyOverlayPosition();
updateProxyDirtyState();
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 preservedScrollTop = existing.scrollTop || getProxyScrollTop();
detachProxyEditor("swap-editor");
const wrapper = document.createElement("div");
wrapper.className = `${SCRIPT_ID}-proxy-shell`;
wrapper.setAttribute(PROXY_ATTRIBUTES.wrapper, "true");
const scrollport = document.createElement("div");
scrollport.className = `${SCRIPT_ID}-proxy-scrollport`;
scrollport.addEventListener("scroll", onProxyScroll, true);
const textarea = document.createElement("textarea");
textarea.value = preservedDraft != null ? preservedDraft : getEditorText(editor);
textarea.setAttribute("aria-label", editor.getAttribute("aria-label") || "Edit message");
textarea.placeholder = editor.getAttribute("placeholder") || "";
textarea.addEventListener("keydown", onProxyTextareaKeydown, true);
textarea.addEventListener("input", onProxyTextareaInput, true);
scrollport.appendChild(textarea);
wrapper.appendChild(scrollport);
ensureHost();
state.shadowRoot?.appendChild(wrapper);
const actions = getEditActionButtons(editor);
state.proxyEditor = {
wrapper,
scrollport,
textarea,
editor,
scrollHost: findEditorScrollHost(editor),
turn,
submitButton: actions.submitButton,
cancelButton: actions.cancelButton,
dirty: preservedDirty,
syncTimer: 0,
lastAppliedText: preservedDirty ? preservedLastAppliedText : textarea.value,
scrollTop: preservedScrollTop,
submitInFlight: false,
};
turn.setAttribute(TURN_ATTRIBUTES.proxyTurn, "true");
syncProxyTextareaVisualStyle();
applyNativeEditorPresentation();
syncProxyOverlayPosition();
updateProxyDirtyState();
syncRootRuntimeState();
if (preservedDirty) {
scheduleProxySync("reattach");
}
requestAnimationFrame(() => {
restoreProxyScrollTop(preservedScrollTop);
if (state.config.autoFocusProxy && state.proxyEditor.textarea?.isConnected) {
const activeTextarea = state.proxyEditor.textarea;
activeTextarea.focus();
activeTextarea.setSelectionRange(activeTextarea.value.length, activeTextarea.value.length);
}
});
queueRefresh();
return true;
}
function maintainProxyEditor() {
const shouldUseProxy = state.config.masterEnabled && state.config.editBoostEnabled;
if (!shouldUseProxy) {
if (state.proxyEditor.wrapper?.isConnected) {
teardownProxyEditor("proxy-disabled");
}
return;
}
const editors = getInlineEditors({ includeManagedHidden: true });
const targetEditor = editors[0] || state.proxyEditor.editor;
if (!targetEditor || !targetEditor.isConnected) {
if (state.proxyEditor.wrapper?.isConnected) {
teardownProxyEditor("edit-closed");
}
return;
}
attachProxyEditor(targetEditor);
syncProxyOverlayPosition();
}
// 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 getActiveEditTurn() {
const visibleEditor = getVisibleInlineEditors()[0];
if (visibleEditor) {
return visibleEditor.closest(SELECTORS.turns);
}
return state.proxyEditor.turn?.isConnected ? state.proxyEditor.turn : 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 activeEditTurn = getActiveEditTurn();
return (
isWithinMargin(turn, state.config.keepMarginPx) ||
turn === activeEditTurn ||
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 (state.proxyEditor.wrapper?.isConnected || 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";
}
if (state.proxyEditor.wrapper?.isConnected) {
return state.proxyEditor.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();
syncProxyOverlayPosition();
syncRootRuntimeState();
if (
state.config.masterEnabled &&
state.config.parkingEnabled &&
!state.proxyEditor.wrapper?.isConnected
) {
requestParkingWork(reason);
}
queueRefresh();
}
function onDocumentPointerDown(event) {
const target = event.target;
if (!(target instanceof Element) || !state.proxyEditor.wrapper?.isConnected) {
return;
}
if (
state.proxyEditor.submitButton?.isConnected &&
!isButtonDisabled(state.proxyEditor.submitButton) &&
(target === state.proxyEditor.submitButton || state.proxyEditor.submitButton.contains(target))
) {
syncProxyToNative("submit-pointerdown");
return;
}
if (
state.proxyEditor.cancelButton?.isConnected &&
(target === state.proxyEditor.cancelButton || state.proxyEditor.cancelButton.contains(target))
) {
clearProxySyncTimer();
}
}
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");
syncProxyOverlayPosition();
queueRefresh();
if (state.config.parkingEnabled) {
requestParkingWork("scroll");
}
},
true
);
window.addEventListener("resize", () => {
syncViewportSizing();
syncProxyOverlayPosition();
scheduleRuntimeMaintenance("resize");
});
if (window.visualViewport) {
window.visualViewport.addEventListener("resize", () => {
syncViewportSizing();
syncProxyOverlayPosition();
});
window.visualViewport.addEventListener("scroll", () => {
syncProxyOverlayPosition();
});
}
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 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);
return {
turnCount: allTurns.length,
detectedComposer: Boolean(document.querySelector(SELECTORS.composer)),
visibleInlineEditors: getVisibleInlineEditors().length,
totalInlineEditors: getInlineEditors({ includeManagedHidden: 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 editor = state.proxyEditor.editor;
const textarea = state.proxyEditor.textarea;
const turn = state.proxyEditor.turn;
const allTurns = Array.isArray(turns) ? turns : getTurnElements();
return {
active: Boolean(state.proxyEditor.wrapper?.isConnected),
dirty: Boolean(state.proxyEditor.dirty),
editorConnected: Boolean(editor?.isConnected),
scrollHostConnected: Boolean(state.proxyEditor.scrollHost?.isConnected),
submitButtonDetected: Boolean(state.proxyEditor.submitButton?.isConnected),
cancelButtonDetected: Boolean(state.proxyEditor.cancelButton?.isConnected),
nativeHidden: Boolean(
editor?.getAttribute(PROXY_ATTRIBUTES.nativeHidden) === "true"
),
nativeGuideVisible: Boolean(
editor?.getAttribute(PROXY_ATTRIBUTES.nativeGuide) === "true" ||
state.proxyEditor.scrollHost?.getAttribute(PROXY_ATTRIBUTES.nativeGuide) === "true"
),
submitInFlight: Boolean(state.proxyEditor.submitInFlight),
editorTag: editor?.tagName || null,
editorRole: editor?.getAttribute?.("role") || null,
turnIndex: turn ? allTurns.indexOf(turn) + 1 : 0,
proxyTextLength:
textarea instanceof HTMLTextAreaElement ? textarea.value.length : 0,
lastAppliedTextLength: state.proxyEditor.lastAppliedText.length,
selectionStart:
textarea instanceof HTMLTextAreaElement && typeof textarea.selectionStart === "number"
? textarea.selectionStart
: null,
selectionEnd:
textarea instanceof HTMLTextAreaElement && typeof textarea.selectionEnd === "number"
? textarea.selectionEnd
: null,
scrollTop: Math.round(state.proxyEditor.scrollTop || 0),
};
}
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),
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();
}
})();