diff --git a/.hintrc b/.hintrc index 78629df..fe3262b 100644 --- a/.hintrc +++ b/.hintrc @@ -3,6 +3,7 @@ "development" ], "hints": { - "axe/forms": "off" + "axe/forms": "off", + "no-inline-styles": "off" } } \ No newline at end of file diff --git a/build/webchatgpt-3.2.5-chrome.zip b/build/webchatgpt-3.2.6-chrome.zip similarity index 70% rename from build/webchatgpt-3.2.5-chrome.zip rename to build/webchatgpt-3.2.6-chrome.zip index c16cccb..8e41c07 100644 Binary files a/build/webchatgpt-3.2.5-chrome.zip and b/build/webchatgpt-3.2.6-chrome.zip differ diff --git a/build/webchatgpt-3.2.5-firefox.zip b/build/webchatgpt-3.2.6-firefox.zip similarity index 70% rename from build/webchatgpt-3.2.5-firefox.zip rename to build/webchatgpt-3.2.6-firefox.zip index a5b4803..fb32086 100644 Binary files a/build/webchatgpt-3.2.5-firefox.zip and b/build/webchatgpt-3.2.6-firefox.zip differ diff --git a/package.json b/package.json index af1bc3d..bd6d9f4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "webchatgpt", - "version": "3.2.5", + "version": "3.2.6", "license": "MIT", "scripts": { "build-dev": "node build.mjs", diff --git a/src/components/errorMessage.tsx b/src/components/errorMessage.tsx index 2e727c9..a952af4 100644 --- a/src/components/errorMessage.tsx +++ b/src/components/errorMessage.tsx @@ -17,10 +17,10 @@ function ErrorMessage(props: { message: string }): h.JSX.Element | null { return ( //
-
+
An error occurred
{props.message}

- Please Check the console for more details. (Ctrl+Shift+J) + You can check the console for more details. (Ctrl+Shift+J)
) } diff --git a/src/components/footer.tsx b/src/components/footer.tsx index c2d6f3e..0ca20a9 100644 --- a/src/components/footer.tsx +++ b/src/components/footer.tsx @@ -6,10 +6,10 @@ function Footer() { return (
- + WebChatGPT extension v.{extension_version} . - If you like the extension, please consider supporting me. + If you like the extension, please consider supporting me.
) } diff --git a/src/components/toolbar.tsx b/src/components/toolbar.tsx index 0d7335e..e556b49 100644 --- a/src/components/toolbar.tsx +++ b/src/components/toolbar.tsx @@ -10,6 +10,11 @@ import Dropdown from './dropdown' import { getTranslation, localizationKeys, setLocaleLanguage } from 'src/util/localization' import Footer from './footer' +type DropdownItem = { + target: { + value: string + } +} const numResultsOptions = Array.from({ length: 10 }, (_, i) => i + 1).map((num) => ({ value: num, @@ -21,11 +26,10 @@ numResultsOptions.push({ label: 'Max results' }) -function Toolbar( - props: { - textarea: HTMLTextAreaElement | null, - } -) { +interface ToolbarProps { + textarea: HTMLTextAreaElement | null +} +const Toolbar = ({ textarea }: ToolbarProps) => { const [webAccess, setWebAccess] = useState(false) const [numResults, setNumResults] = useState(3) const [timePeriod, setTimePeriod] = useState('') @@ -56,18 +60,15 @@ function Toolbar( Browser.runtime.onMessage.addListener(handleMessage) - return function cleanup() { - Browser.runtime.onMessage.removeListener(handleMessage) - } + return () => Browser.runtime.onMessage.removeListener(handleMessage) }, []) useEffect(() => { updateUserConfig({ webAccess }) updateTextAreaPlaceholder(webAccess) - props.textarea?.focus() + textarea?.focus() }, [webAccess]) - const handlePromptClick = () => updatePrompts() const updatePrompts = () => { @@ -77,28 +78,28 @@ function Toolbar( } const updateTextAreaPlaceholder = (show: boolean) => { - props.textarea?.setAttribute('placeholder', show ? getTranslation(localizationKeys.UI.textareaPlaceholder) : '') + textarea?.setAttribute('placeholder', show ? getTranslation(localizationKeys.UI.textareaPlaceholder) : '') } const handleWebAccessToggle = () => setWebAccess((prev) => !prev) - const handleNumResultsChange = (e: { target: { value: string } }) => { + const handleNumResultsChange = (e: DropdownItem) => { const value = parseInt(e.target.value, 10) setNumResults(value) updateUserConfig({ numWebResults: value }) } - const handleTimePeriodChange = (e: { target: { value: string } }) => { + const handleTimePeriodChange = (e: DropdownItem) => { setTimePeriod(e.target.value) updateUserConfig({ timePeriod: e.target.value }) } - const handleRegionChange = (e: { target: { value: string } }) => { + const handleRegionChange = (e: DropdownItem) => { setRegion(e.target.value) updateUserConfig({ region: e.target.value }) } - const handlePromptChange = (e: { target: { value: string } }) => { + const handlePromptChange = (e: DropdownItem) => { const uuid = e.target.value @@ -159,7 +160,7 @@ function Toolbar( value={promptUUID} onChange={handlePromptChange} options={ - prompts.map((prompt) => ({ value: prompt.uuid, label: prompt.name })).concat({ value: 'wcg-new-prompt', label: `+ ${getTranslation(localizationKeys.buttons.newPrompt)}` }) + prompts.map((prompt) => ({ value: prompt.uuid ?? 'undefin', label: prompt.name })).concat({ value: 'wcg-new-prompt', label: `+ ${getTranslation(localizationKeys.buttons.newPrompt)}` }) } onClick={handlePromptClick} /> diff --git a/src/content-scripts/api.ts b/src/content-scripts/api.ts index 161257f..5cb8563 100644 --- a/src/content-scripts/api.ts +++ b/src/content-scripts/api.ts @@ -35,22 +35,23 @@ export async function getWebpageTitleAndText(url: string, html_str = ''): Promis } } html = await response.text() - } const doc = parseHTML(html).document const parsed = new Readability(doc).parse() - if (!parsed) { - return { title: "Could not parse the page.", body: "", url } + if (!parsed || !parsed.textContent) { + return { title: "Could not parse the page.", body: "Could not parse the page.", url } } - - let text = cleanText(parsed.textContent) + console.log("text", parsed.textContent); + let text = cleanText(parsed.textContent) + const userConfig = await getUserConfig() - if (userConfig.trimLongText) { - text = text.slice(0, 14500) + if (userConfig.trimLongText && text.length > 14400) { + text = text.slice(0, 14400) + text += "\n\n[Text has been trimmed to 14,500 characters. You can disable this on WebChatGPT's options page.]" } return { title: parsed.title, body: text, url } } diff --git a/src/content-scripts/ddg_search.ts b/src/content-scripts/ddg_search.ts index 5412bab..dc61868 100644 --- a/src/content-scripts/ddg_search.ts +++ b/src/content-scripts/ddg_search.ts @@ -52,7 +52,7 @@ export async function getHtml({ query, timerange, region }: SearchRequest): Prom } function htmlToSearchResults(html: string, numResults: number): SearchResult[] { - console.log("htmlToSearchResults", numResults) + // console.log("htmlToSearchResults", numResults) const $ = cheerio.load(html) const results: SearchResult[] = [] @@ -72,8 +72,8 @@ function htmlToSearchResults(html: string, numResults: number): SearchResult[] { // Extract web search results const upperBound = zeroClickLink.length > 0 ? numResults - 1 : numResults - const webLinks = $(`table:nth-of-type(${numTables}) .result-link`).slice(0, upperBound) - const webSnippets = $(`table:nth-of-type(${numTables}) .result-snippet`).slice(0, upperBound) + const webLinks = $(`table:nth-of-type(${numTables}) tr:not(.result-sponsored) .result-link`).slice(0, upperBound) + const webSnippets = $(`table:nth-of-type(${numTables}) tr:not(.result-sponsored) .result-snippet`).slice(0, upperBound) webLinks.each((i, element) => { const link = $(element) const snippet = $(webSnippets[i]).text().trim() diff --git a/src/content-scripts/mainUI.tsx b/src/content-scripts/mainUI.tsx index 6490209..9b9750a 100644 --- a/src/content-scripts/mainUI.tsx +++ b/src/content-scripts/mainUI.tsx @@ -13,9 +13,9 @@ import { apiExtractText } from './api' let isProcessing = false -let btnSubmit: HTMLButtonElement -let textarea: HTMLTextAreaElement -let footer: HTMLDivElement +let btnSubmit: HTMLButtonElement | null | undefined +let textarea: HTMLTextAreaElement | null +let chatGptFooter: HTMLDivElement | null function renderSlashCommandsMenu() { @@ -24,7 +24,7 @@ function renderSlashCommandsMenu() { if (div) div.remove() div = document.createElement('wcg-slash-commands-menu') - const textareaParentParent = textarea.parentElement?.parentElement + const textareaParentParent = textarea?.parentElement?.parentElement textareaParentParent?.insertBefore(div, textareaParentParent.firstChild) render(, div) @@ -58,6 +58,8 @@ async function processQuery(query: string, userConfig: UserConfig) { async function handleSubmit(query: string) { + if (!textarea) return + const userConfig = await getUserConfig() if (!userConfig.webAccess) { @@ -81,6 +83,9 @@ async function handleSubmit(query: string) { } async function onSubmit(event: MouseEvent | KeyboardEvent) { + + if (!textarea) return + const isKeyEvent = event instanceof KeyboardEvent if (isKeyEvent && event.shiftKey && event.key === 'Enter') return @@ -88,14 +93,17 @@ async function onSubmit(event: MouseEvent | KeyboardEvent) { if (isKeyEvent && event.key === 'Enter' && event.isComposing) return if (!isProcessing && (event.type === "click" || (isKeyEvent && event.key === 'Enter'))) { - const query = textarea.value.trim() + const query = textarea?.value.trim() - if (query === "") return + // if query is empty or undefined, return + if (!query) return textarea.value = "" const isPartialCommand = slashCommands.some(command => command.name.startsWith(query) && query.length <= command.name.length) - if (isPartialCommand) return + if (isPartialCommand) { + return + } isProcessing = true await handleSubmit(query) @@ -104,14 +112,14 @@ async function onSubmit(event: MouseEvent | KeyboardEvent) { } function pressEnter() { - textarea.focus() + textarea?.focus() const enterEvent = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: 'Enter', code: 'Enter' }) - textarea.dispatchEvent(enterEvent) + textarea?.dispatchEvent(enterEvent) } function showErrorMessage(error: Error) { @@ -124,59 +132,93 @@ function showErrorMessage(error: Error) { async function updateUI() { - if (getWebChatGPTToolbar()) return + formChild = document.querySelector('form')?.children[0] as HTMLElement + textarea = getTextArea() + // console.info("UpdateUI textarea: ", textarea) + + const toolbar = getWebChatGPTToolbar() + if (!textarea) { + toolbar?.remove() + return + } + + if (toolbar) return + + console.info("WebChatGPT: Updating UI") btnSubmit = getSubmitButton() - textarea = getTextArea() - footer = getFooter() + btnSubmit?.addEventListener("click", onSubmit) - if (textarea && btnSubmit) { + textarea?.addEventListener("keydown", onSubmit) - textarea.addEventListener("keydown", onSubmit) - btnSubmit.addEventListener("click", onSubmit) + await renderToolbar() - const textareaParentParent = textarea.parentElement?.parentElement - if (textareaParentParent && textareaParentParent.parentElement) { - textareaParentParent.style.flexDirection = 'column' - textareaParentParent.parentElement.style.flexDirection = 'column' - textareaParentParent.parentElement.style.gap = '0px' - textareaParentParent.parentElement.style.marginBottom = '0.5em' - } + renderSlashCommandsMenu() - try { - const { shadowRootDiv, shadowRoot } = await createShadowRoot('content-scripts/mainUI.css') - shadowRootDiv.classList.add('wcg-toolbar') - textareaParentParent?.appendChild(shadowRootDiv) - render(, shadowRoot) - } catch (e) { - if (e instanceof Error) { - showErrorMessage(Error(`Error loading WebChatGPT toolbar: ${e.message}. Please reload the page (F5).`)); - console.error(e) - } - } - // textarea.parentElement.style.flexDirection = 'row' + // textarea.parentElement.style.flexDirection = 'row' - renderSlashCommandsMenu() + chatGptFooter = getFooter() + if (chatGptFooter) { + const lastChild = chatGptFooter.lastElementChild as HTMLElement + if (lastChild) lastChild.style.padding = '0 0 0.5em 0' } +} - if (footer) { - const lastChild = footer.lastElementChild as HTMLElement - if (lastChild) - lastChild.style.padding = '0 0 0.5em 0' +async function renderToolbar() { + + try { + const textareaParentParent = textarea?.parentElement?.parentElement + // const textareaParentParent = formChild + // if (textareaParentParent && textareaParentParent.parentElement) { + // textareaParentParent.style.flexDirection = 'column' + // textareaParentParent.parentElement.style.flexDirection = 'column' + // textareaParentParent.parentElement.style.gap = '0px' + // textareaParentParent.parentElement.style.marginBottom = '0.5em' + // } + + const { shadowRootDiv, shadowRoot } = await createShadowRoot('content-scripts/mainUI.css') + shadowRootDiv.classList.add('wcg-toolbar') + textareaParentParent?.appendChild(shadowRootDiv) + render(, shadowRoot) + + } catch (e) { + if (e instanceof Error) { + showErrorMessage(Error(`Error loading WebChatGPT toolbar: ${e.message}. Please reload the page (F5).`)) + } } } -const rootEl = getRootElement() -window.onload = function () { - updateUI() +const form = document.querySelector('form') +const formParent = form?.parentElement +const rootEl = getRootElement() +let formChild = document.querySelector('form')?.children[0] as HTMLElement +const mutationObserver = new MutationObserver(() => { try { - new MutationObserver(() => { - updateUI() - }).observe(rootEl, { childList: true }) + updateUI() } catch (e) { if (e instanceof Error) { showErrorMessage(e) } } +}) + +window.onload = function () { + updateUI() + + if (formChild) { + mutationObserver.observe(formChild, { childList: true }) + } + + if (rootEl) { + mutationObserver.observe(rootEl, { childList: true }) + } + + if (formParent) { + mutationObserver.observe(formParent, { childList: true }) + } +} + +window.onunload = function () { + mutationObserver.disconnect() } diff --git a/src/manifest.json b/src/manifest.json index ca03c92..fa2f1f4 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -3,7 +3,7 @@ "name": "__MSG_appName__", "description": "__MSG_appDesc__", "default_locale": "en", - "version": "3.2.5", + "version": "3.2.6", "icons": { "16": "icons/icon16.png", "48": "icons/icon48.png", diff --git a/src/manifest.v2.json b/src/manifest.v2.json index 6171e35..9c2efa6 100644 --- a/src/manifest.v2.json +++ b/src/manifest.v2.json @@ -3,7 +3,7 @@ "name": "__MSG_appName__", "description": "__MSG_appDesc__", "default_locale": "en", - "version": "3.2.5", + "version": "3.2.6", "icons": { "16": "icons/icon16.png", "48": "icons/icon48.png",