Skip to content

Commit

Permalink
Enhanced is<Idle|Loaded> methods w/ timeout param
Browse files Browse the repository at this point in the history
  • Loading branch information
adamlui committed Sep 12, 2024
1 parent 1fccb70 commit fe003dd
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 59 deletions.
130 changes: 72 additions & 58 deletions chatgpt.js
Original file line number Diff line number Diff line change
Expand Up @@ -410,38 +410,43 @@ const chatgpt = { // eslint-disable-line no-redeclare
return codeBlocks ? codeBlocks[codeBlocks.length - 1] : msg;
},

async isIdle() {
async isIdle(timeout = null) {
const obsConfig = { childList: true, subtree: true },
selectors = { msgDiv: 'div[data-message-author-role]',
replyDiv: 'div[data-message-author-role="assistant"]' };

await new Promise(resolve => { // when in conversation page
if (document.querySelector(selectors.msgDiv)) resolve();
else new MutationObserver((_, obs) => {
if (document.querySelector(selectors.msgDiv)) { obs.disconnect(); resolve(); }
}).observe(document.body, obsConfig);
});
await new Promise(resolve => { // when reply starts generating
new MutationObserver((_, obs) => {
if (chatgpt.getStopBtn()) { obs.disconnect(); resolve(); }
}).observe(document.body, { childList: true, subtree: true });
});
const replyDivs = document.querySelectorAll(selectors.replyDiv),
lastReplyDiv = replyDivs[replyDivs.length - 1];
await new Promise(resolve => { // when code starts generating
new MutationObserver((_, obs) => {
if (lastReplyDiv?.querySelector('pre')) { obs.disconnect(); resolve(); }
}).observe(document.body, obsConfig);
});
await new Promise(resolve => { // when code stops generating
new MutationObserver((_, obs) => {
if (lastReplyDiv?.querySelector('pre').nextElementSibling // code block not last child of reply div
|| !chatgpt.getStopBtn() // ...or reply outright stopped generating
) { obs.disconnect(); resolve(); }
}).observe(document.body, obsConfig);
});
},
// Create promises
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null;
const isIdlePromise = (async () => {
await new Promise(resolve => { // when on convo page
if (document.querySelector(selectors.msgDiv)) resolve();
else new MutationObserver((_, obs) => {
if (document.querySelector(selectors.msgDiv)) { obs.disconnect(); resolve(); }
}).observe(document.body, obsConfig);
});
await new Promise(resolve => { // when reply starts generating
new MutationObserver((_, obs) => {
if (chatgpt.getStopBtn()) { obs.disconnect(); resolve(); }
}).observe(document.body, { childList: true, subtree: true });
});
const replyDivs = document.querySelectorAll(selectors.replyDiv),
lastReplyDiv = replyDivs[replyDivs.length - 1];
await new Promise(resolve => { // when code starts generating
new MutationObserver((_, obs) => {
if (lastReplyDiv?.querySelector('pre')) { obs.disconnect(); resolve(); }
}).observe(document.body, obsConfig);
});
return new Promise(resolve => { // when code stops generating
new MutationObserver((_, obs) => {
if (lastReplyDiv?.querySelector('pre')?.nextElementSibling // code block not last child of reply div
|| !chatgpt.getStopBtn() // ...or reply outright stopped generating
) { obs.disconnect(); resolve(true); }
}).observe(document.body, obsConfig);
});
})();

return await (timeoutPromise ? Promise.race([isIdlePromise, timeoutPromise]) : isIdlePromise);
},

async minify(code) {
if (!code) return console.error('Code argument not supplied. Pass some code!');
Expand Down Expand Up @@ -942,13 +947,15 @@ const chatgpt = { // eslint-disable-line no-redeclare
hideHeader() { chatgpt.header.hide(); },

history: {
isLoaded() {
return new Promise(resolve => {
async isLoaded(timeout = null) {
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null;
const isLoadedPromise = new Promise(resolve => {
if (document.querySelector('nav')) resolve(true);
else new MutationObserver((_, obs) => {
if (document.querySelector('nav')) { obs.disconnect(); resolve(true); }
}).observe(document.body, { childList: true, subtree: true });
});
return await ( timeoutPromise ? Promise.race([isLoadedPromise, timeoutPromise]) : isLoadedPromise );
}
},

Expand Down Expand Up @@ -1080,35 +1087,43 @@ const chatgpt = { // eslint-disable-line no-redeclare
isDarkMode() { return document.documentElement.classList.toString().includes('dark'); },
isFullScreen() { return chatgpt.browser.isFullScreen(); },

async isIdle() {
async isIdle(timeout = null) {
const obsConfig = { childList: true, subtree: true },
msgDivSelector = 'div[data-message-author-role]';

await new Promise(resolve => { // when in conversation page
if (document.querySelector(msgDivSelector)) resolve();
else new MutationObserver((_, obs) => {
if (document.querySelector(msgDivSelector)) { obs.disconnect(); resolve(); }
}).observe(document.body, obsConfig);
});
await new Promise(resolve => { // when reply starts generating
new MutationObserver((_, obs) => {
if (chatgpt.getStopBtn()) { obs.disconnect(); resolve(); }
}).observe(document.body, obsConfig);
});
return new Promise(resolve => { // when reply stops generating
new MutationObserver((_, obs) => {
if (!chatgpt.getStopBtn()) { obs.disconnect(); resolve(true); }
}).observe(document.body, obsConfig);
});
// Create promises
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null;
const isIdlePromise = (async () => {
await new Promise(resolve => { // when on convo page
if (document.querySelector(msgDivSelector)) resolve();
else new MutationObserver((_, obs) => {
if (document.querySelector(msgDivSelector)) { obs.disconnect(); resolve(); }
}).observe(document.body, obsConfig);
});
await new Promise(resolve => { // when reply starts generating
new MutationObserver((_, obs) => {
if (chatgpt.getStopBtn()) { obs.disconnect(); resolve(); }
}).observe(document.body, obsConfig);
});
return new Promise(resolve => { // when reply stops generating
new MutationObserver((_, obs) => {
if (!chatgpt.getStopBtn()) { obs.disconnect(); resolve(true); }
}).observe(document.body, obsConfig);
});
})();

return await (timeoutPromise ? Promise.race([isIdlePromise, timeoutPromise]) : isIdlePromise);
},

isLoaded() {
return new Promise(resolve => {
async isLoaded(timeout = null) {
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null;
const isLoadedPromise = new Promise(resolve => {
if (chatgpt.getNewChatBtn()) resolve(true);
else new MutationObserver((_, obs) => {
if (chatgpt.getNewChatBtn()) { obs.disconnect(); resolve(true); }
}).observe(document.body, { childList: true, subtree: true });
});
return await ( timeoutPromise ? Promise.race([isLoadedPromise, timeoutPromise]) : isLoadedPromise );
},

isLightMode() { return document.documentElement.classList.toString().includes('light'); },
Expand Down Expand Up @@ -1806,17 +1821,16 @@ const chatgpt = { // eslint-disable-line no-redeclare
console.error('Sidebar toggle not found!');
},

async isLoaded() {
async isLoaded(timeout = 5000) {
await chatgpt.isLoaded();
return Promise.race([
new Promise(resolve => {
if (chatgpt.getNewChatLink()) resolve(true);
else new MutationObserver((_, obs) => {
if (chatgpt.getNewChatLink()) { obs.disconnect(); resolve(true); }
}).observe(document.body, { childList: true, subtree: true });
}),
new Promise(resolve => setTimeout(resolve, 5000)) // since New Chat link not always present
]);
const timeoutPromise = new Promise(resolve => setTimeout(() => { resolve(false); }, timeout));
const isLoadedPromise = new Promise(resolve => {
if (chatgpt.getNewChatLink()) resolve(true);
else new MutationObserver((_, obs) => {
if (chatgpt.getNewChatLink()) { obs.disconnect(); resolve(true); }
}).observe(document.body, { childList: true, subtree: true });
});
return await Promise.race([isLoadedPromise, timeoutPromise]);
}
},

Expand Down
22 changes: 21 additions & 1 deletion docs/USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,10 @@ if (chatgpt.isFullScreen()) {

Resolves a promise when ChatGPT has finished loading.

**Parameters**:

`timeout` (optional): An integer specifying the number of milliseconds to wait before resolving with `false`. If not provided, waits indefinitely until ChatGPT finishes loading.

Example code:

```js
Expand Down Expand Up @@ -912,6 +916,10 @@ console.log(fifthResp); // Example output: 'Hello from ChatGPT!'

Resolves a promise when ChatGPT has finished generating a response.

**Parameters**:

`timeout` (optional): An integer specifying the number of milliseconds to wait before resolving with `false`. If not provided, waits indefinitely until response generation finishes.

Example code:

```js
Expand Down Expand Up @@ -1440,6 +1448,10 @@ Example code:

Resolves a promise when code has finished generating.

**Parameters**:

`timeout` (optional): An integer specifying the number of milliseconds to wait before resolving with `false`. If not provided, waits indefinitely until code generation finishes.

Example code:

```js
Expand Down Expand Up @@ -1651,6 +1663,10 @@ API related to the chat history.

Resolves a promise when chat history has finished loading.

**Parameters**:

`timeout` (optional): An integer specifying the number of milliseconds to wait before resolving with `false`. If not provided, waits indefinitely until chat history finishes loading.

Example code:

```js
Expand Down Expand Up @@ -1971,7 +1987,11 @@ chatgpt.sidebar.toggle();

### isLoaded `async`

Resolves a promise when the ChatGPT sidebar has finished loading. (Times out 5s after New Chat button loads, since New Chat link does not always appear.)
Resolves a promise when the ChatGPT sidebar has finished loading.

**Parameters**:

`timeout` (optional): An integer specifying the number of milliseconds to wait before resolving with `false`. If not provided, waits 5s or until New Chat link appears (since it is not always present).

Example code:

Expand Down

0 comments on commit fe003dd

Please sign in to comment.