@cloro-dev/response-parser 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -27
- package/dist/index.d.ts +4 -0
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/ai-mode.d.ts.map +1 -1
- package/dist/providers/copilot.d.ts +4 -0
- package/dist/providers/copilot.d.ts.map +1 -1
- package/dist/providers/perplexity.d.ts.map +1 -1
- package/dist/react/components/ResponseRenderer.d.ts.map +1 -1
- package/dist/react/hooks/useResponse.d.ts.map +1 -1
- package/dist/react/index.esm.js +1 -1
- package/dist/react/index.esm.js.map +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/providers/ai-mode.d.ts.map +1 -1
- package/dist/react/providers/copilot.d.ts +4 -0
- package/dist/react/providers/copilot.d.ts.map +1 -1
- package/dist/react/providers/perplexity.d.ts.map +1 -1
- package/dist/react/react/components/ResponseRenderer.d.ts.map +1 -1
- package/dist/react/react/hooks/useResponse.d.ts.map +1 -1
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -74,8 +74,8 @@ Parse an AI response with auto-detected provider.
|
|
|
74
74
|
|
|
75
75
|
- `removeLinks`: `boolean` - Remove all hyperlinks from HTML (default: `false`)
|
|
76
76
|
- `invertColors`: `boolean` - Apply color inversion for dark mode (default: `false`)
|
|
77
|
-
- `
|
|
78
|
-
- `
|
|
77
|
+
- `removeHeader`: `boolean` - Remove navigation bar/header (default: `false`, Perplexity, Gemini, Copilot, AI Overview & AI Mode)
|
|
78
|
+
- `removeFooter`: `boolean` - Remove follow-up input box/footer (default: `false`, ChatGPT, Perplexity, Gemini, Copilot, AI Overview & AI Mode)
|
|
79
79
|
- `removeSidebar`: `boolean` - Remove sidebar (default: `false`, ChatGPT, Gemini & Copilot)
|
|
80
80
|
|
|
81
81
|
**Returns:** `ParsedResponse | null`
|
|
@@ -99,8 +99,9 @@ Main React component for rendering AI responses.
|
|
|
99
99
|
- `provider`: Explicitly specify provider
|
|
100
100
|
- `removeLinks`: Remove all hyperlinks (default: `false`)
|
|
101
101
|
- `invertColors`: Apply color inversion (default: `false`)
|
|
102
|
-
- `
|
|
103
|
-
- `
|
|
102
|
+
- `removeHeader`: Remove navigation bar/header (default: `false`, Perplexity, Gemini, Copilot, AI Overview & AI Mode)
|
|
103
|
+
- `removeFooter`: Remove follow-up input box/footer (default: `false`, ChatGPT, Perplexity, Gemini, Copilot, AI Overview & AI Mode)
|
|
104
|
+
- `removeSidebar`: Remove sidebar (default: `false`, ChatGPT, Gemini & Copilot)
|
|
104
105
|
- `className`: CSS class for container
|
|
105
106
|
- `iframeProps`: Additional props for iframe
|
|
106
107
|
- `loadingComponent`: Custom loading component
|
|
@@ -121,8 +122,9 @@ React hook for parsing AI responses.
|
|
|
121
122
|
- `provider`: Explicitly specify provider
|
|
122
123
|
- `removeLinks`: Remove all hyperlinks (default: `false`)
|
|
123
124
|
- `invertColors`: Apply color inversion (default: `false`)
|
|
124
|
-
- `
|
|
125
|
-
- `
|
|
125
|
+
- `removeHeader`: Remove navigation bar/header (default: `false`, Perplexity, Gemini, Copilot, AI Overview & AI Mode)
|
|
126
|
+
- `removeFooter`: Remove follow-up input box/footer (default: `false`, ChatGPT, Perplexity, Gemini, Copilot, AI Overview & AI Mode)
|
|
127
|
+
- `removeSidebar`: Remove sidebar (default: `false`, ChatGPT, Gemini & Copilot)
|
|
126
128
|
- `onProviderDetected`: Callback when provider is detected
|
|
127
129
|
- `onError`: Callback when error occurs
|
|
128
130
|
|
|
@@ -139,14 +141,14 @@ React hook for parsing AI responses.
|
|
|
139
141
|
|
|
140
142
|
## Supported Providers
|
|
141
143
|
|
|
142
|
-
| Provider | Status | Features
|
|
143
|
-
| ----------- | ------ |
|
|
144
|
-
| ChatGPT | ✅ | Dark mode, sidebar hiding
|
|
145
|
-
| Gemini | ✅ | Material Design overrides, navbar removal
|
|
146
|
-
| Perplexity | ✅ | Color inversion, link removal, UI element removal
|
|
147
|
-
| Copilot | ✅ | UI element hiding
|
|
148
|
-
| AI Overview | ✅ | WIZ data extraction
|
|
149
|
-
| AI Mode | ✅ | Google UI hiding
|
|
144
|
+
| Provider | Status | Features |
|
|
145
|
+
| ----------- | ------ | ------------------------------------------------- |
|
|
146
|
+
| ChatGPT | ✅ | Dark mode, sidebar hiding |
|
|
147
|
+
| Gemini | ✅ | Material Design overrides, navbar removal |
|
|
148
|
+
| Perplexity | ✅ | Color inversion, link removal, UI element removal |
|
|
149
|
+
| Copilot | ✅ | UI element hiding |
|
|
150
|
+
| AI Overview | ✅ | WIZ data extraction |
|
|
151
|
+
| AI Mode | ✅ | Google UI hiding |
|
|
150
152
|
|
|
151
153
|
## Examples
|
|
152
154
|
|
|
@@ -175,8 +177,8 @@ const parsed = parseAiResponse(response, {
|
|
|
175
177
|
```tsx
|
|
176
178
|
<ResponseRenderer
|
|
177
179
|
response={response}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
removeHeader={true} // Remove top navigation bar
|
|
181
|
+
removeFooter={true} // Remove follow-up input box
|
|
180
182
|
removeLinks={true} // Remove all hyperlinks
|
|
181
183
|
invertColors={true} // Dark mode
|
|
182
184
|
/>
|
|
@@ -205,9 +207,18 @@ const ErrorComponent = ({ error, retry }) => (
|
|
|
205
207
|
<ResponseRenderer response={response} errorComponent={ErrorComponent} />;
|
|
206
208
|
```
|
|
207
209
|
|
|
208
|
-
## What's Changed
|
|
210
|
+
## What's Changed
|
|
209
211
|
|
|
210
|
-
###
|
|
212
|
+
### v0.1.4
|
|
213
|
+
|
|
214
|
+
- ✅ **Renamed** `removeNavbar` → `removeHeader` (removes navigation bar/header)
|
|
215
|
+
- ✅ **Renamed** `removeFollowup` → `removeFooter` (removes follow-up input box/footer)
|
|
216
|
+
- ✅ **Extended** `removeHeader` support to AI Overview and AI Mode
|
|
217
|
+
- ✅ **Extended** `removeFooter` support to all providers (ChatGPT, Perplexity, Gemini, Copilot, AI Overview, AI Mode)
|
|
218
|
+
- ✅ **Improved** ChatGPT styling
|
|
219
|
+
- ✅ **Updated** AI Mode and AI Overview parsers
|
|
220
|
+
|
|
221
|
+
### v0.1.3
|
|
211
222
|
|
|
212
223
|
- ✅ **Always sanitizes** HTML by default (removes scripts for security)
|
|
213
224
|
- ✅ **Always includes** provider-specific styles
|
|
@@ -216,15 +227,6 @@ const ErrorComponent = ({ error, retry }) => (
|
|
|
216
227
|
- ❌ **Removed** `sanitize` option (always enabled)
|
|
217
228
|
- ❌ **Removed** `includeStyles` option (always enabled)
|
|
218
229
|
- ❌ **Removed** `baseUrl` option (uses provider default)
|
|
219
|
-
- ✅ **Added** `removeNavbar` option (removes navigation bar)
|
|
220
|
-
- ✅ **Added** `removeFollowup` option (removes follow-up input)
|
|
221
|
-
|
|
222
|
-
### Remaining Options
|
|
223
|
-
|
|
224
|
-
- `removeLinks`: Remove all hyperlinks from HTML
|
|
225
|
-
- `invertColors`: Apply color inversion
|
|
226
|
-
- `removeNavbar`: Remove navigation bar (Perplexity & Gemini)
|
|
227
|
-
- `removeFollowup`: Remove follow-up input (Perplexity only)
|
|
228
230
|
|
|
229
231
|
### Component Renaming
|
|
230
232
|
|
package/dist/index.d.ts
CHANGED
|
@@ -186,6 +186,10 @@ declare class CopilotProvider extends BaseProvider {
|
|
|
186
186
|
* Remove Copilot footer/followup input box from HTML
|
|
187
187
|
*/
|
|
188
188
|
removeFooter(html: string): string;
|
|
189
|
+
/**
|
|
190
|
+
* Remove Copilot cookie banner from HTML
|
|
191
|
+
*/
|
|
192
|
+
removeCookieBanner(html: string): string;
|
|
189
193
|
/**
|
|
190
194
|
* Remove Copilot sidebar from HTML
|
|
191
195
|
*/
|
package/dist/index.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
class e{static detect(e){const t={CHATGPT:0,GEMINI:0,PERPLEXITY:0,COPILOT:0,AIOVERVIEW:0,AIMODE:0};for(const[r,o]of Object.entries(this.PROVIDER_PATTERNS))for(const n of o)try{n(e)&&t[r]++}catch(e){}let r=0,o=null;for(const[e,n]of Object.entries(t))n>r&&(r=n,o=e);return 0===r?null:{provider:o,confidence:r/Object.values(t).reduce((e,t)=>Math.max(e,t),1)}}static getAllProviders(e){const t=[];for(const[r,o]of Object.entries(this.PROVIDER_PATTERNS)){let n=0;for(const t of o)try{t(e)&&n++}catch(e){}n>0&&t.push({provider:r,confidence:n/o.length})}return t.sort((e,t)=>t.confidence-e.confidence)}static validate(e,t){const r=this.detect(e);return(null==r?void 0:r.provider)===t&&r.confidence>.5}}e.PROVIDER_PATTERNS={CHATGPT:[e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("bg-token-bg-primary")||r.includes("text-token-text-secondary")},e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("chatgpt.com")||r.includes("openai.com")}],GEMINI:[e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("bard-sidenav")||r.includes("mat-sidenav")},e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("gemini.google.com")||r.includes("gem-sys-color")}],PERPLEXITY:[e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("perplexity.ai")||r.includes("prose")}],COPILOT:[e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("copilot.microsoft.com")||r.includes('data-testid="sidebar-container"')}],AIOVERVIEW:[e=>{var t,r,o,n;if(null===(r=null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.aioverview)||void 0===r?void 0:r.text)return!0;if(null===(o=null==e?void 0:e.aioverview)||void 0===o?void 0:o.text)return!0;const i=(null===(n=null==e?void 0:e.result)||void 0===n?void 0:n.html)||(null==e?void 0:e.html)||"";return i.includes("WIZ_global_data")||i.includes("DnVkpd")}],AIMODE:[e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("DZ13He")||r.includes("wYq63b")||r.includes("AI Mode")}]};class t{injectStyles(e,t){let r=e;(null==t?void 0:t.baseUrl)&&!e.includes("<base ")&&(r=r.includes("<head>")?r.replace("<head>",`<head><base href="${t.baseUrl}">`):r.replace(/(<html[^>]*>)/i,`$1<head><base href="${t.baseUrl}"></head>`));const o=(null==t?void 0:t.customCSS)||"";return o&&r.includes("<head>")?r=r.replace("<head>",`<head><style>${o}</style>`):o&&(r=r.replace(/(<html[^>]*>)/i,`$1<head><style>${o}</style></head>`)),r}sanitizeHtml(e){let t=e.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gim,"");return t=t.replace(/<noscript[\s\S]*?<\/noscript>/gi,""),t}isFullDocument(e){return/^\s*(<html|<!DOCTYPE)/i.test(e)}removeLinks(e){return e.replace(/<a\b[^>]*>([\s\S]*?)<\/a>/gim,"$1")}wrapFragment(e){return`<!DOCTYPE html>\n<html>\n<head>\n <meta charset="utf-8">\n <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'none'; style-src 'unsafe-inline';">\n <style>\n body {\n margin: 0;\n padding: 16px;\n font-family: system-ui, -apple-system, sans-serif;\n white-space: pre-wrap;\n word-wrap: break-word;\n }\n img { max-width: 100%; height: auto; }\n body {\n pointer-events: none;\n user-select: none;\n }\n </style>\n</head>\n<body>\n ${e}\n</body>\n</html>`}}class r extends t{constructor(){super(...arguments),this.name="CHATGPT",this.baseUrl="https://chatgpt.com"}extractContent(e){let t="",r="",o=e;return e.result&&(o=e.result),"string"==typeof o?o.trim().startsWith("<")&&o.includes(">")?t=o:r=o:"object"==typeof o&&null!==o&&(o.html?t=o.html:o.content?o.content.trim().startsWith("<")?t=o.content:r=o.content:o.text&&(r=o.text)),{html:t,text:r}}removeLinks(e){return e.replace(/<a\b([^>]*)>/gi,(e,t)=>{const r=t.replace(/\s+href\s*=\s*["'][^"']*["']/gi,"").replace(/\s+alt\s*=\s*["'][^"']*["']/gi,"").replace(/\s+rel\s*=\s*["'][^"']*["']/gi,"").replace(/\s+target\s*=\s*["'][^"']*["']/gi,"").trim();return`<span${r?" "+r:""}>`}).replace(/<\/a>/gi,"</span>")}removeSidebar(e){let t=e;return t=t.replace(/<nav[^>]*>.*?<\/nav>/gis,""),t=t.replace(/<div[^>]*class="[^"]*sidebar[^"]*"[^>]*>.*?<\/div>/gis,""),t}removeFooter(e){let t=e;return t=t.replace(/<div[^>]*id="thread-bottom-container"[^>]*>.*?<\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*id="thread-bottom"[^>]*>.*?<\/div><\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*\[grid-area:leading\][^"]*"[^>]*>.*?<\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*\[grid-area:footer\][^"]*"[^>]*>.*?<\/div><\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*\[grid-area:trailing\][^"]*"[^>]*>.*?<\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*text-token-text-secondary[^"]*"[^>]*>.*?ChatGPT can make mistakes\. Check important info\..*?<\/div><\/div>/gis,""),t}parse(e,t){var r;const{html:o,text:n}=this.extractContent(e);if(!o)throw new Error("No HTML content found in ChatGPT response");let i=o;const s=null!==(r=null==t?void 0:t.removeFooter)&&void 0!==r&&r;i=this.sanitizeHtml(i),(null==t?void 0:t.removeSidebar)&&(i=this.removeSidebar(i)),s&&(i=this.removeFooter(i)),(null==t?void 0:t.removeLinks)&&(i=this.removeLinks(i));const l=(null==t?void 0:t.invertColors)?"\n /* Force Dark Background on main containers */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n }\n ":'\n /* Force Light Background on main containers */\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Override ChatGPT token text color classes - targeting text content only */\n p[class*="text-token"], span[class*="text-token"], div[class*="text-token"],\n h1[class*="text-token"], h2[class*="text-token"], h3[class*="text-token"],\n h4[class*="text-token"], h5[class*="text-token"], h6[class*="text-token"],\n li[class*="text-token"], td[class*="text-token"], th[class*="text-token"],\n [class*="markdown-"], strong, b, em, i, h1, h2, h3, h4, h5, h6, p, li, td, th {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*="border"], [class*="divider"], [class*="separator"],\n [class*="border-t"], [class*="border-b"], [class*="border-l"], [class*="border-r"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n ';return i=this.injectStyles(i,{baseUrl:this.baseUrl,customCSS:l}),{provider:this.name,html:i,text:n,metadata:{isFullDocument:this.isFullDocument(i),sidebarRemoved:(null==t?void 0:t.removeSidebar)||!1,footerRemoved:s,linksRemoved:(null==t?void 0:t.removeLinks)||!1,colorsInverted:(null==t?void 0:t.invertColors)||!1}}}}class o extends t{constructor(){super(...arguments),this.name="GEMINI",this.baseUrl="https://gemini.google.com"}extractContent(e){let t="",r="",o=[],n=e;return e.result&&(n=e.result),"string"==typeof n?n.trim().startsWith("<")&&n.includes(">")?t=n:r=n:"object"==typeof n&&null!==n&&(n.html?t=n.html:n.content?n.content.trim().startsWith("<")?t=n.content:r=n.content:n.text&&(r=n.text),n.aioverview&&(n.aioverview.text&&(r=n.aioverview.text),n.aioverview.sources&&(o=n.aioverview.sources))),{html:t,text:r,sources:o}}removeHeader(e){let t=e;return t=t.replace(/<div[^>]*class="[^"]*boqOnegoogleliteOgbOneGoogleBar[^"]*"[^>]*>.*?<\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*side-nav-menu-button[^"]*"[^>]*>.*?<\/div>/gis,""),t=t.replace(/<top-bar-actions[^>]*>.*?<\/top-bar-actions>/gis,""),t=t.replace(/<div[^>]*class="[^"]*desktop-ogb-buffer[^"]*"[^>]*><\/div>/gis,""),t}removeFooter(e){return e.replace(/<input-container[^>]*>.*?<\/input-container>/gis,"")}removeSidebar(e){return e.replace(/<bard-sidenav[^>]*>.*?<\/bard-sidenav>/gis,"")}parse(e,t){var r,o;const{html:n,text:i,sources:s}=this.extractContent(e);if(!n)throw new Error("No HTML content found in Gemini response");let l=n;const a=null!==(r=null==t?void 0:t.removeHeader)&&void 0!==r&&r,d=null!==(o=null==t?void 0:t.removeFooter)&&void 0!==o&&o;return l=this.sanitizeHtml(l),a&&(l=this.removeHeader(l)),d&&(l=this.removeFooter(l)),(null==t?void 0:t.removeSidebar)&&(l=this.removeSidebar(l)),(null==t?void 0:t.removeLinks)&&(l=this.removeLinks(l)),l=this.injectStyles(l,{baseUrl:this.baseUrl,customCSS:""}),{provider:this.name,html:l,text:i,sources:s,metadata:{isFullDocument:this.isFullDocument(l),headerRemoved:a,footerRemoved:d,sidebarRemoved:(null==t?void 0:t.removeSidebar)||!1,linksRemoved:(null==t?void 0:t.removeLinks)||!1}}}}class n extends t{constructor(){super(...arguments),this.name="PERPLEXITY",this.baseUrl="https://www.perplexity.ai"}extractContent(e){let t="",r="",o=e;return e.result&&(o=e.result),"string"==typeof o?o.trim().startsWith("<")&&o.includes(">")?t=o:r=o:"object"==typeof o&&null!==o&&(o.html?t=o.html:o.content?o.content.trim().startsWith("<")?t=o.content:r=o.content:o.text&&(r=o.text)),{html:t,text:r}}removeHeader(e){return e.replace(/<div[^>]*class="[^"]*@container\/header[^"]*"[^>]*>.*?<div[^>]*class="[^"]*absolute bottom-0 inset-x-0 border-b[^"]*"[^>]*><\/div>/gis,"")}removeFooter(e){return e.replace(/<div[^>]*class="[^"]*erp-sidecar:fixed[^"]*"[^>]*>.*?<\/div>\s*<\/div>\s*<\/div>\s*<\/div>/gis,"")}parse(e,t){var r,o;const{html:n,text:i}=this.extractContent(e);if(!n)throw new Error("No HTML content found in Perplexity response");let s=n;const l=null!==(r=null==t?void 0:t.removeHeader)&&void 0!==r&&r,a=null!==(o=null==t?void 0:t.removeFooter)&&void 0!==o&&o;s=this.sanitizeHtml(s),l&&(s=this.removeHeader(s)),a&&(s=this.removeFooter(s)),(null==t?void 0:t.removeLinks)&&(s=this.removeLinks(s));const d=(null==t?void 0:t.invertColors)?"\n html {\n filter: invert(1) hue-rotate(180deg);\n background-color: white !important;\n }\n ":"";return s=this.injectStyles(s,{baseUrl:this.baseUrl,customCSS:d}),{provider:this.name,html:s,text:i,metadata:{isFullDocument:this.isFullDocument(s),linksRemoved:(null==t?void 0:t.removeLinks)||!1,colorsInverted:(null==t?void 0:t.invertColors)||!1,headerRemoved:l,footerRemoved:a}}}}class i extends t{constructor(){super(...arguments),this.name="COPILOT",this.baseUrl="https://copilot.microsoft.com"}extractContent(e){let t="",r="",o=e;return e.result&&(o=e.result),"string"==typeof o?o.trim().startsWith("<")&&o.includes(">")?t=o:r=o:"object"==typeof o&&null!==o&&(o.html?t=o.html:o.content?o.content.trim().startsWith("<")?t=o.content:r=o.content:o.text&&(r=o.text)),{html:t,text:r}}removeHeader(e){let t=e;return t=t.replace(/<div[^>]*class="[^"]*relative shrink-0 min-h-14[^"]*"[^>]*data-testid="backstage-chats"[^>]*>.*?<\/div>/gis,""),t}removeFooter(e){let t=e;return t=t.replace(/<div[^>]*class="[^"]*absolute bottom-0 w-full[^"]*"[^>]*>.*?<\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*mt-\[var\(--composer-container-height\)\][^"]*"[^>]*><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*pointer-events-none absolute flex justify-center z-20 inset-x-0 bottom-\[calc\(var\(--composer-container-height\)\+1rem\)\][^"]*"[^>]*>.*?<button[^>]*data-testid="scroll-to-bottom-button"[^>]*>.*?<\/button><\/div>/gis,""),t}removeSidebar(e){let t=e;return t=t.replace(/<div[^>]*class="[^"]*absolute h-full w-0 will-change-auto max-md:bg-sidebar-light[^"]*"[^>]*style="[^"]*"[^>]*>.*?<main/gis,"<main"),t}parse(e,t){var r,o;const{html:n,text:i}=this.extractContent(e);if(!n)throw new Error("No HTML content found in Copilot response");let s=n;const l=null!==(r=null==t?void 0:t.removeHeader)&&void 0!==r&&r,a=null!==(o=null==t?void 0:t.removeFooter)&&void 0!==o&&o;return s=this.sanitizeHtml(s),l&&(s=this.removeHeader(s)),a&&(s=this.removeFooter(s)),(null==t?void 0:t.removeSidebar)&&(s=this.removeSidebar(s)),(null==t?void 0:t.removeLinks)&&(s=this.removeLinks(s)),s=this.injectStyles(s,{baseUrl:this.baseUrl,customCSS:""}),{provider:this.name,html:s,text:i,metadata:{isFullDocument:this.isFullDocument(s),headerRemoved:l,footerRemoved:a,sidebarRemoved:(null==t?void 0:t.removeSidebar)||!1,linksRemoved:(null==t?void 0:t.removeLinks)||!1}}}}class s extends t{constructor(){super(...arguments),this.name="AIOVERVIEW",this.baseUrl="https://www.google.com"}extractContent(e){let t="",r=[],o=e;return e.result&&(o=e.result),o.aioverview&&o.aioverview.sources&&(r=o.aioverview.sources),"string"==typeof o?o.trim().startsWith("<")&&o.includes(">")&&(t=o):"object"==typeof o&&null!==o&&(o.html?t=o.html:o.content&&o.content.trim().startsWith("<")&&(t=o.content)),{html:t,sources:r}}removeHeader(e){return e}removeFooter(e){return e}removeSidebar(e){return e}parse(e,t){var r,o;const{html:n,sources:i}=this.extractContent(e);if(!n)throw new Error("No HTML content found in AI Overview response");let s=n;const l=null===(r=null==t?void 0:t.removeHeader)||void 0===r||r,a=null===(o=null==t?void 0:t.removeFooter)||void 0===o||o;s=this.sanitizeHtml(s),l&&(s=this.removeHeader(s)),a&&(s=this.removeFooter(s)),(null==t?void 0:t.removeSidebar)&&(s=this.removeSidebar(s)),(null==t?void 0:t.removeLinks)&&(s=this.removeLinks(s));let d="";return(null==t?void 0:t.invertColors)?d+="\n /* Dark theme */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n color: #e3e3e3 !important;\n }\n ":d+='\n /* Light theme - force all backgrounds to white */\n *, div, span, p, a, li, td, th, section, article, main, aside, nav, header, footer {\n background-color: #ffffff !important;\n }\n\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Force dark text color on all text elements */\n *, p, span, div, h1, h2, h3, h4, h5, h6, li, td, th, strong, b, em, i, a {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*="border"], [class*="divider"], [class*="separator"],\n [class*="border-t"], [class*="border-b"], [class*="border-l"], [class*="border-r"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n ',l&&(d+='\n /* Header hiding */\n header, #header, #searchform, .sfbg, #appbar,\n div[role="navigation"], #leftnav, #sidetogether,\n [role="banner"], .Fgvgjc, #hdtb, .hdtb-msb,\n .DZ13He, .wYq63b, .eT9Cje, .bNg8Rb, .S6VXfe, .Lu57id {\n display: none !important;\n }\n '),(null==t?void 0:t.removeSidebar)&&(d+="\n /* Sidebar hiding */\n #leftnav, #sidetogether {\n display: none !important;\n }\n "),a&&(d+='\n /* Footer hiding */\n footer, #footer, .fbar,\n .pdp-nav, [aria-label="Main menu"], .gb_Td, .gb_L {\n display: none !important;\n }\n '),s=this.injectStyles(s,{baseUrl:this.baseUrl,customCSS:d}),{provider:this.name,html:s,sources:i,metadata:{isFullDocument:this.isFullDocument(s),headerRemoved:l,footerRemoved:a,sidebarRemoved:(null==t?void 0:t.removeSidebar)||!1,linksRemoved:(null==t?void 0:t.removeLinks)||!1,colorsInverted:(null==t?void 0:t.invertColors)||!1}}}}class l extends t{constructor(){super(...arguments),this.name="AIMODE",this.baseUrl="https://www.google.com"}extractContent(e){let t="",r="",o=e;return e.result&&(o=e.result),"string"==typeof o?o.trim().startsWith("<")&&o.includes(">")?t=o:r=o:"object"==typeof o&&null!==o&&(o.html?t=o.html:o.content?o.content.trim().startsWith("<")?t=o.content:r=o.content:o.text&&(r=o.text)),{html:t,text:r}}removeHeader(e){let t=e;return t=t.replace(/<div[^>]*jsname="oEQ3x"[^>]*class="[^"]*DZ13He[^"]*"[^>]*>.*?<hr class="e9c7U">.*?<\/div><\/div><\/div><\/div><\/div>/gis,""),t}removeFooter(e){let t=e;return t=t.replace(/<div[^>]*data-xid="aim-mars-input-plate"[^>]*>[\s\S]*?<\/div><\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*jscontroller="P5gZDb"[^>]*>[\s\S]*?<\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*t0ITR hh3ttd[^"]*"[^>]*>[\s\S]*?<\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*y4VEUd vve6Ce[^"]*"[^>]*>[\s\S]*?<\/div><\/div>/gis,""),t}removeSidebar(e){let t=e;return t=t.replace(/<div[^>]*class="[^"]*ho072b[^"]*"[^>]*aria-label="AI Mode history"[^>]*>[\s\S]*?<\/div><\/div><\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*jsname="NlVIob"[^>]*class="[^"]*qEn1od[^"]*"[^>]*>[\s\S]*?<\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*OEwhSe[^"]*"[^>]*>[\s\S]*?<\/div><\/div>/gis,""),t=t.replace(/<button[^>]*aria-label="Start new search"[^>]*>[\s\S]*?<\/button>/gis,""),t=t.replace(/<button[^>]*aria-label="AI Mode history"[^>]*>[\s\S]*?<\/button>/gis,""),t}parse(e,t){var r,o;const{html:n,text:i}=this.extractContent(e);if(!n)throw new Error("No HTML content found in AI Mode response");let s=n;const l=null===(r=null==t?void 0:t.removeHeader)||void 0===r||r,a=null===(o=null==t?void 0:t.removeFooter)||void 0===o||o;s=this.sanitizeHtml(s),l&&(s=this.removeHeader(s)),a&&(s=this.removeFooter(s)),(null==t?void 0:t.removeSidebar)&&(s=this.removeSidebar(s)),(null==t?void 0:t.removeLinks)&&(s=this.removeLinks(s));let d="";return d+="",(null==t?void 0:t.invertColors)?d+="\n /* Dark theme */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n color: #e3e3e3 !important;\n }\n ":d+='\n /* Light theme - force all backgrounds to white */\n *, div, span, p, a, li, td, th, section, article, main, aside, nav, header, footer {\n background-color: #ffffff !important;\n }\n\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Force dark text color on all text elements */\n *, p, span, div, h1, h2, h3, h4, h5, h6, li, td, th, strong, b, em, i, a {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*="border"], [class*="divider"], [class*="separator"],\n [class*="border-t"], [class*="border-b"], [class*="border-l"], [class*="border-r"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n ',l&&(d+='\n /* Header hiding */\n header, #header, #searchform, .sfbg, #appbar,\n div[role="navigation"], #leftnav, #sidetogether,\n [role="banner"], .Fgvgjc, #hdtb, .hdtb-msb,\n .DZ13He, .wYq63b, .eT9Cje, .bNg8Rb, .S6VXfe, .Lu57id {\n display: none !important;\n }\n '),(null==t?void 0:t.removeSidebar)&&(d+="\n /* Sidebar hiding */\n #leftnav, #sidetogether {\n display: none !important;\n }\n "),a&&(d+='\n /* Footer hiding */\n footer, #footer, .fbar,\n .pdp-nav, [aria-label="Main menu"], .gb_Td, .gb_L {\n display: none !important;\n }\n '),s=this.injectStyles(s,{baseUrl:this.baseUrl,customCSS:d}),{provider:this.name,html:s,text:i,metadata:{isFullDocument:this.isFullDocument(s),headerRemoved:l,footerRemoved:a,sidebarRemoved:(null==t?void 0:t.removeSidebar)||!1,linksRemoved:(null==t?void 0:t.removeLinks)||!1,colorsInverted:(null==t?void 0:t.invertColors)||!1}}}}class a{constructor(){this.providers=new Map,this.providers.set("CHATGPT",new r),this.providers.set("GEMINI",new o),this.providers.set("PERPLEXITY",new n),this.providers.set("COPILOT",new i),this.providers.set("AIOVERVIEW",new s),this.providers.set("AIMODE",new l)}parse(t,r){const o=e.detect(t);if(!o)return this.parseGeneric(t,r);const n=this.providers.get(o.provider);if(!n)return null;try{const e=n.parse(t,r);return{...e,metadata:{...e.metadata,detectionConfidence:o.confidence}}}catch(e){return console.error(`Failed to parse response with ${o.provider}:`,e),null}}parseWithProvider(e,t,r){const o=this.providers.get(t);if(!o)throw new Error(`Unknown provider: ${t}`);try{return o.parse(e,r)}catch(e){return console.error(`Failed to parse response with ${t}:`,e),null}}detectProvider(t){const r=e.detect(t);return(null==r?void 0:r.provider)||null}detectAllProviders(t){return e.getAllProviders(t)}parseGeneric(e,t){let r="",o="";let n=e;var i;return e.result&&(n=e.result),"string"==typeof(i=n)?i.trim().startsWith("<")&&i.includes(">")?r=i:o=i:"object"==typeof i&&null!==i&&(i.html?r=i.html:i.content?i.content.trim().startsWith("<")?r=i.content:o=i.content:i.text&&(o=i.text)),r||o?(r&&(r=r.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gim,""),r=r.replace(/<noscript[\s\S]*?<\/noscript>/gi,"")),{provider:"CHATGPT",html:r,text:o,metadata:{isGeneric:!0}}):null}getSupportedProviders(){return Array.from(this.providers.keys())}registerProvider(e,t){this.providers.set(e,t)}}const d=new a,c=(e,t)=>d.parse(e,t),v=e=>d.detectProvider(e);export{l as AIModeProvider,s as AIOverviewProvider,a as AIResponseParser,t as BaseProvider,r as ChatGPTProvider,i as CopilotProvider,o as GeminiProvider,n as PerplexityProvider,e as ProviderDetector,v as detectProvider,c as parseAiResponse,d as parser};
|
|
1
|
+
class e{static detect(e){const t={CHATGPT:0,GEMINI:0,PERPLEXITY:0,COPILOT:0,AIOVERVIEW:0,AIMODE:0};for(const[r,o]of Object.entries(this.PROVIDER_PATTERNS))for(const n of o)try{n(e)&&t[r]++}catch(e){}let r=0,o=null;for(const[e,n]of Object.entries(t))n>r&&(r=n,o=e);return 0===r?null:{provider:o,confidence:r/Object.values(t).reduce((e,t)=>Math.max(e,t),1)}}static getAllProviders(e){const t=[];for(const[r,o]of Object.entries(this.PROVIDER_PATTERNS)){let n=0;for(const t of o)try{t(e)&&n++}catch(e){}n>0&&t.push({provider:r,confidence:n/o.length})}return t.sort((e,t)=>t.confidence-e.confidence)}static validate(e,t){const r=this.detect(e);return(null==r?void 0:r.provider)===t&&r.confidence>.5}}e.PROVIDER_PATTERNS={CHATGPT:[e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("bg-token-bg-primary")||r.includes("text-token-text-secondary")},e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("chatgpt.com")||r.includes("openai.com")}],GEMINI:[e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("bard-sidenav")||r.includes("mat-sidenav")},e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("gemini.google.com")||r.includes("gem-sys-color")}],PERPLEXITY:[e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("perplexity.ai")||r.includes("prose")}],COPILOT:[e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("copilot.microsoft.com")||r.includes('data-testid="sidebar-container"')}],AIOVERVIEW:[e=>{var t,r,o,n;if(null===(r=null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.aioverview)||void 0===r?void 0:r.text)return!0;if(null===(o=null==e?void 0:e.aioverview)||void 0===o?void 0:o.text)return!0;const i=(null===(n=null==e?void 0:e.result)||void 0===n?void 0:n.html)||(null==e?void 0:e.html)||"";return i.includes("WIZ_global_data")||i.includes("DnVkpd")}],AIMODE:[e=>{var t;const r=(null===(t=null==e?void 0:e.result)||void 0===t?void 0:t.html)||(null==e?void 0:e.html)||"";return r.includes("DZ13He")||r.includes("wYq63b")||r.includes("AI Mode")}]};class t{injectStyles(e,t){let r=e;(null==t?void 0:t.baseUrl)&&!e.includes("<base ")&&(r=r.includes("<head>")?r.replace("<head>",`<head><base href="${t.baseUrl}">`):r.replace(/(<html[^>]*>)/i,`$1<head><base href="${t.baseUrl}"></head>`));const o=(null==t?void 0:t.customCSS)||"";return o&&r.includes("<head>")?r=r.replace("<head>",`<head><style>${o}</style>`):o&&(r=r.replace(/(<html[^>]*>)/i,`$1<head><style>${o}</style></head>`)),r}sanitizeHtml(e){let t=e.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gim,"");return t=t.replace(/<noscript[\s\S]*?<\/noscript>/gi,""),t}isFullDocument(e){return/^\s*(<html|<!DOCTYPE)/i.test(e)}removeLinks(e){return e.replace(/<a\b[^>]*>([\s\S]*?)<\/a>/gim,"$1")}wrapFragment(e){return`<!DOCTYPE html>\n<html>\n<head>\n <meta charset="utf-8">\n <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'none'; style-src 'unsafe-inline';">\n <style>\n body {\n margin: 0;\n padding: 16px;\n font-family: system-ui, -apple-system, sans-serif;\n white-space: pre-wrap;\n word-wrap: break-word;\n }\n img { max-width: 100%; height: auto; }\n body {\n pointer-events: none;\n user-select: none;\n }\n </style>\n</head>\n<body>\n ${e}\n</body>\n</html>`}}class r extends t{constructor(){super(...arguments),this.name="CHATGPT",this.baseUrl="https://chatgpt.com"}extractContent(e){let t="",r="",o=e;return e.result&&(o=e.result),"string"==typeof o?o.trim().startsWith("<")&&o.includes(">")?t=o:r=o:"object"==typeof o&&null!==o&&(o.html?t=o.html:o.content?o.content.trim().startsWith("<")?t=o.content:r=o.content:o.text&&(r=o.text)),{html:t,text:r}}removeLinks(e){return e.replace(/<a\b([^>]*)>/gi,(e,t)=>{const r=t.replace(/\s+href\s*=\s*["'][^"']*["']/gi,"").replace(/\s+alt\s*=\s*["'][^"']*["']/gi,"").replace(/\s+rel\s*=\s*["'][^"']*["']/gi,"").replace(/\s+target\s*=\s*["'][^"']*["']/gi,"").trim();return`<span${r?" "+r:""}>`}).replace(/<\/a>/gi,"</span>")}removeSidebar(e){let t=e;return t=t.replace(/<nav[^>]*>.*?<\/nav>/gis,""),t=t.replace(/<div[^>]*class="[^"]*sidebar[^"]*"[^>]*>.*?<\/div>/gis,""),t}removeFooter(e){let t=e;return t=t.replace(/<div[^>]*id="thread-bottom-container"[^>]*>.*?<\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*id="thread-bottom"[^>]*>.*?<\/div><\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*\[grid-area:leading\][^"]*"[^>]*>.*?<\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*\[grid-area:footer\][^"]*"[^>]*>.*?<\/div><\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*\[grid-area:trailing\][^"]*"[^>]*>.*?<\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*text-token-text-secondary[^"]*"[^>]*>.*?ChatGPT can make mistakes\. Check important info\..*?<\/div><\/div>/gis,""),t}parse(e,t){var r;const{html:o,text:n}=this.extractContent(e);if(!o)throw new Error("No HTML content found in ChatGPT response");let i=o;const s=null!==(r=null==t?void 0:t.removeFooter)&&void 0!==r&&r;i=this.sanitizeHtml(i),(null==t?void 0:t.removeSidebar)&&(i=this.removeSidebar(i)),s&&(i=this.removeFooter(i)),(null==t?void 0:t.removeLinks)&&(i=this.removeLinks(i));const l=(null==t?void 0:t.invertColors)?"\n /* Force Dark Background on main containers */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n }\n ":'\n /* Force Light Background on main containers */\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Override ChatGPT token text color classes - targeting text content only */\n p[class*="text-token"], span[class*="text-token"], div[class*="text-token"],\n h1[class*="text-token"], h2[class*="text-token"], h3[class*="text-token"],\n h4[class*="text-token"], h5[class*="text-token"], h6[class*="text-token"],\n li[class*="text-token"], td[class*="text-token"], th[class*="text-token"],\n [class*="markdown-"], strong, b, em, i, h1, h2, h3, h4, h5, h6, p, li, td, th {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*="border"], [class*="divider"], [class*="separator"],\n [class*="border-t"], [class*="border-b"], [class*="border-l"], [class*="border-r"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n ';return i=this.injectStyles(i,{baseUrl:this.baseUrl,customCSS:l}),{provider:this.name,html:i,text:n,metadata:{isFullDocument:this.isFullDocument(i),sidebarRemoved:(null==t?void 0:t.removeSidebar)||!1,footerRemoved:s,linksRemoved:(null==t?void 0:t.removeLinks)||!1,colorsInverted:(null==t?void 0:t.invertColors)||!1}}}}class o extends t{constructor(){super(...arguments),this.name="GEMINI",this.baseUrl="https://gemini.google.com"}extractContent(e){let t="",r="",o=[],n=e;return e.result&&(n=e.result),"string"==typeof n?n.trim().startsWith("<")&&n.includes(">")?t=n:r=n:"object"==typeof n&&null!==n&&(n.html?t=n.html:n.content?n.content.trim().startsWith("<")?t=n.content:r=n.content:n.text&&(r=n.text),n.aioverview&&(n.aioverview.text&&(r=n.aioverview.text),n.aioverview.sources&&(o=n.aioverview.sources))),{html:t,text:r,sources:o}}removeHeader(e){let t=e;return t=t.replace(/<div[^>]*class="[^"]*boqOnegoogleliteOgbOneGoogleBar[^"]*"[^>]*>.*?<\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*side-nav-menu-button[^"]*"[^>]*>.*?<\/div>/gis,""),t=t.replace(/<top-bar-actions[^>]*>.*?<\/top-bar-actions>/gis,""),t=t.replace(/<div[^>]*class="[^"]*desktop-ogb-buffer[^"]*"[^>]*><\/div>/gis,""),t}removeFooter(e){return e.replace(/<input-container[^>]*>.*?<\/input-container>/gis,"")}removeSidebar(e){return e.replace(/<bard-sidenav[^>]*>.*?<\/bard-sidenav>/gis,"")}parse(e,t){var r,o;const{html:n,text:i,sources:s}=this.extractContent(e);if(!n)throw new Error("No HTML content found in Gemini response");let l=n;const a=null!==(r=null==t?void 0:t.removeHeader)&&void 0!==r&&r,d=null!==(o=null==t?void 0:t.removeFooter)&&void 0!==o&&o;return l=this.sanitizeHtml(l),a&&(l=this.removeHeader(l)),d&&(l=this.removeFooter(l)),(null==t?void 0:t.removeSidebar)&&(l=this.removeSidebar(l)),(null==t?void 0:t.removeLinks)&&(l=this.removeLinks(l)),l=this.injectStyles(l,{baseUrl:this.baseUrl,customCSS:""}),{provider:this.name,html:l,text:i,sources:s,metadata:{isFullDocument:this.isFullDocument(l),headerRemoved:a,footerRemoved:d,sidebarRemoved:(null==t?void 0:t.removeSidebar)||!1,linksRemoved:(null==t?void 0:t.removeLinks)||!1}}}}class n extends t{constructor(){super(...arguments),this.name="PERPLEXITY",this.baseUrl="https://www.perplexity.ai"}extractContent(e){let t="",r="",o=e;return e.result&&(o=e.result),"string"==typeof o?o.trim().startsWith("<")&&o.includes(">")?t=o:r=o:"object"==typeof o&&null!==o&&(o.html?t=o.html:o.content?o.content.trim().startsWith("<")?t=o.content:r=o.content:o.text&&(r=o.text)),{html:t,text:r}}removeHeader(e){return e.replace(/<div[^>]*class="[^"]*@container\/header[^"]*"[^>]*>.*?<div[^>]*class="[^"]*absolute bottom-0 inset-x-0 border-b[^"]*"[^>]*><\/div>/gis,"")}removeFooter(e){let t=e;return t=t.replace(/<div[^>]*class="[^"]*erp-sidecar:fixed erp-sidecar:w-full bottom-safeAreaInsetBottom p-md pointer-events-none z-10 absolute border-subtlest ring-subtlest divide-subtlest bg-transparent[^"]*"[^>]*>[\s\S]*?(?=<div[^>]*class="[^"]*fixed inset-y-0 right-0[^"]*")/gis,""),t}parse(e,t){var r,o;const{html:n,text:i}=this.extractContent(e);if(!n)throw new Error("No HTML content found in Perplexity response");let s=n;const l=null!==(r=null==t?void 0:t.removeHeader)&&void 0!==r&&r,a=null!==(o=null==t?void 0:t.removeFooter)&&void 0!==o&&o;s=this.sanitizeHtml(s),l&&(s=this.removeHeader(s)),a&&(s=this.removeFooter(s)),(null==t?void 0:t.removeLinks)&&(s=this.removeLinks(s));const d=(null==t?void 0:t.invertColors)?"\n html {\n filter: invert(1) hue-rotate(180deg);\n background-color: white !important;\n }\n ":"";return s=this.injectStyles(s,{baseUrl:this.baseUrl,customCSS:d}),{provider:this.name,html:s,text:i,metadata:{isFullDocument:this.isFullDocument(s),linksRemoved:(null==t?void 0:t.removeLinks)||!1,colorsInverted:(null==t?void 0:t.invertColors)||!1,headerRemoved:l,footerRemoved:a}}}}class i extends t{constructor(){super(...arguments),this.name="COPILOT",this.baseUrl="https://copilot.microsoft.com"}extractContent(e){let t="",r="",o=e;return e.result&&(o=e.result),"string"==typeof o?o.trim().startsWith("<")&&o.includes(">")?t=o:r=o:"object"==typeof o&&null!==o&&(o.html?t=o.html:o.content?o.content.trim().startsWith("<")?t=o.content:r=o.content:o.text&&(r=o.text)),{html:t,text:r}}removeHeader(e){let t=e;return t=t.replace(/<div[^>]*class="[^"]*relative shrink-0 min-h-14[^"]*"[^>]*data-testid="backstage-chats"[^>]*>.*?<\/div>/gis,""),t}removeFooter(e){let t=e;return t=t.replace(/<div[^>]*class="[^"]*absolute bottom-0 w-full[^"]*"[^>]*>.*?<\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*mt-\[var\(--composer-container-height\)\][^"]*"[^>]*><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*pointer-events-none absolute flex justify-center z-20 inset-x-0 bottom-\[calc\(var\(--composer-container-height\)\+1rem\)\][^"]*"[^>]*>.*?<button[^>]*data-testid="scroll-to-bottom-button"[^>]*>.*?<\/button><\/div>/gis,""),t}removeCookieBanner(e){let t=e;return t=t.replace(/<div[^>]*id="cookie-banner"[^>]*>.*?<\/div>.*?<\/div>.*?<\/div>.*?<\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*max-w-cookie-banner[^"]*"[^>]*>.*?<\/div>.*?<\/div>.*?<\/div>.*?<\/div>/gis,""),t}removeSidebar(e){let t=e;return t=t.replace(/<div[^>]*class="[^"]*absolute h-full w-0 will-change-auto max-md:bg-sidebar-light[^"]*"[^>]*style="[^"]*"[^>]*>.*?<main/gis,"<main"),t}parse(e,t){var r,o;const{html:n,text:i}=this.extractContent(e);if(!n)throw new Error("No HTML content found in Copilot response");let s=n;const l=null!==(r=null==t?void 0:t.removeHeader)&&void 0!==r&&r,a=null!==(o=null==t?void 0:t.removeFooter)&&void 0!==o&&o;return s=this.sanitizeHtml(s),s=this.removeCookieBanner(s),l&&(s=this.removeHeader(s)),a&&(s=this.removeFooter(s)),(null==t?void 0:t.removeSidebar)&&(s=this.removeSidebar(s)),(null==t?void 0:t.removeLinks)&&(s=this.removeLinks(s)),s=this.injectStyles(s,{baseUrl:this.baseUrl,customCSS:""}),{provider:this.name,html:s,text:i,metadata:{isFullDocument:this.isFullDocument(s),cookieBannerRemoved:!0,headerRemoved:l,footerRemoved:a,sidebarRemoved:(null==t?void 0:t.removeSidebar)||!1,linksRemoved:(null==t?void 0:t.removeLinks)||!1}}}}class s extends t{constructor(){super(...arguments),this.name="AIOVERVIEW",this.baseUrl="https://www.google.com"}extractContent(e){let t="",r=[],o=e;return e.result&&(o=e.result),o.aioverview&&o.aioverview.sources&&(r=o.aioverview.sources),"string"==typeof o?o.trim().startsWith("<")&&o.includes(">")&&(t=o):"object"==typeof o&&null!==o&&(o.html?t=o.html:o.content&&o.content.trim().startsWith("<")&&(t=o.content)),{html:t,sources:r}}removeHeader(e){return e}removeFooter(e){return e}removeSidebar(e){return e}parse(e,t){var r,o;const{html:n,sources:i}=this.extractContent(e);if(!n)throw new Error("No HTML content found in AI Overview response");let s=n;const l=null===(r=null==t?void 0:t.removeHeader)||void 0===r||r,a=null===(o=null==t?void 0:t.removeFooter)||void 0===o||o;s=this.sanitizeHtml(s),l&&(s=this.removeHeader(s)),a&&(s=this.removeFooter(s)),(null==t?void 0:t.removeSidebar)&&(s=this.removeSidebar(s)),(null==t?void 0:t.removeLinks)&&(s=this.removeLinks(s));let d="";return(null==t?void 0:t.invertColors)?d+="\n /* Dark theme */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n color: #e3e3e3 !important;\n }\n ":d+='\n /* Light theme - force all backgrounds to white */\n *, div, span, p, a, li, td, th, section, article, main, aside, nav, header, footer {\n background-color: #ffffff !important;\n }\n\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Force dark text color on all text elements */\n *, p, span, div, h1, h2, h3, h4, h5, h6, li, td, th, strong, b, em, i, a {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*="border"], [class*="divider"], [class*="separator"],\n [class*="border-t"], [class*="border-b"], [class*="border-l"], [class*="border-r"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n ',l&&(d+='\n /* Header hiding */\n header, #header, #searchform, .sfbg, #appbar,\n div[role="navigation"], #leftnav, #sidetogether,\n [role="banner"], .Fgvgjc, #hdtb, .hdtb-msb,\n .DZ13He, .wYq63b, .eT9Cje, .bNg8Rb, .S6VXfe, .Lu57id {\n display: none !important;\n }\n '),(null==t?void 0:t.removeSidebar)&&(d+="\n /* Sidebar hiding */\n #leftnav, #sidetogether {\n display: none !important;\n }\n "),a&&(d+='\n /* Footer hiding */\n footer, #footer, .fbar,\n .pdp-nav, [aria-label="Main menu"], .gb_Td, .gb_L {\n display: none !important;\n }\n '),s=this.injectStyles(s,{baseUrl:this.baseUrl,customCSS:d}),{provider:this.name,html:s,sources:i,metadata:{isFullDocument:this.isFullDocument(s),headerRemoved:l,footerRemoved:a,sidebarRemoved:(null==t?void 0:t.removeSidebar)||!1,linksRemoved:(null==t?void 0:t.removeLinks)||!1,colorsInverted:(null==t?void 0:t.invertColors)||!1}}}}class l extends t{constructor(){super(...arguments),this.name="AIMODE",this.baseUrl="https://www.google.com"}extractContent(e){let t="",r="",o=e;return e.result&&(o=e.result),"string"==typeof o?o.trim().startsWith("<")&&o.includes(">")?t=o:r=o:"object"==typeof o&&null!==o&&(o.html?t=o.html:o.content?o.content.trim().startsWith("<")?t=o.content:r=o.content:o.text&&(r=o.text)),{html:t,text:r}}removeHeader(e){let t=e;return t=t.replace(/<div[^>]*jsname="oEQ3x"[^>]*class="[^"]*DZ13He[^"]*"[^>]*>.*?<hr class="e9c7U">.*?<\/div><\/div><\/div><\/div><\/div>/gis,""),t}removeFooter(e){let t=e;return t=t.replace(/<div[^>]*data-xid="aim-mars-input-plate"[^>]*>[\s\S]*?<\/div><\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*jscontroller="P5gZDb"[^>]*>[\s\S]*?<\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*t0ITR hh3ttd[^"]*"[^>]*>[\s\S]*?<\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*y4VEUd vve6Ce[^"]*"[^>]*>[\s\S]*?<\/div><\/div>/gis,""),t}removeSidebar(e){let t=e;return t=t.replace(/<div[^>]*class="[^"]*ho072b[^"]*"[^>]*aria-label="AI Mode history"[^>]*>[\s\S]*?<\/div><\/div><\/div><\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*jsname="NlVIob"[^>]*class="[^"]*qEn1od[^"]*"[^>]*>[\s\S]*?<\/div><\/div><\/div>/gis,""),t=t.replace(/<div[^>]*class="[^"]*OEwhSe[^"]*"[^>]*>[\s\S]*?<\/div><\/div>/gis,""),t=t.replace(/<button[^>]*aria-label="Start new search"[^>]*>[\s\S]*?<\/button>/gis,""),t=t.replace(/<button[^>]*aria-label="AI Mode history"[^>]*>[\s\S]*?<\/button>/gis,""),t}parse(e,t){var r,o;const{html:n,text:i}=this.extractContent(e);if(!n)throw new Error("No HTML content found in AI Mode response");let s=n;const l=null===(r=null==t?void 0:t.removeHeader)||void 0===r||r,a=null===(o=null==t?void 0:t.removeFooter)||void 0===o||o;s=this.sanitizeHtml(s),l&&(s=this.removeHeader(s)),a&&(s=this.removeFooter(s)),(null==t?void 0:t.removeSidebar)&&(s=this.removeSidebar(s)),(null==t?void 0:t.removeLinks)&&(s=this.removeLinks(s));let d="";return d+="\n .KxvlWc, #CXQnmb {\n display: none !important;\n }\n ",(null==t?void 0:t.invertColors)?d+="\n /* Dark theme */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n color: #e3e3e3 !important;\n }\n ":d+='\n /* Light theme - force all backgrounds to white */\n *, div, span, p, a, li, td, th, section, article, main, aside, nav, header, footer {\n background-color: #ffffff !important;\n }\n\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Force dark text color on all text elements */\n *, p, span, div, h1, h2, h3, h4, h5, h6, li, td, th, strong, b, em, i, a {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*="border"], [class*="divider"], [class*="separator"],\n [class*="border-t"], [class*="border-b"], [class*="border-l"], [class*="border-r"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n ',l&&(d+='\n /* Header hiding */\n header, #header, #searchform, .sfbg, #appbar,\n div[role="navigation"], #leftnav, #sidetogether,\n [role="banner"], .Fgvgjc, #hdtb, .hdtb-msb,\n .DZ13He, .wYq63b, .eT9Cje, .bNg8Rb, .S6VXfe, .Lu57id {\n display: none !important;\n }\n '),(null==t?void 0:t.removeSidebar)&&(d+="\n /* Sidebar hiding */\n #leftnav, #sidetogether {\n display: none !important;\n }\n "),a&&(d+='\n /* Footer hiding */\n footer, #footer, .fbar,\n .pdp-nav, [aria-label="Main menu"], .gb_Td, .gb_L {\n display: none !important;\n }\n '),s=this.injectStyles(s,{baseUrl:this.baseUrl,customCSS:d}),{provider:this.name,html:s,text:i,metadata:{isFullDocument:this.isFullDocument(s),headerRemoved:l,footerRemoved:a,sidebarRemoved:(null==t?void 0:t.removeSidebar)||!1,linksRemoved:(null==t?void 0:t.removeLinks)||!1,colorsInverted:(null==t?void 0:t.invertColors)||!1}}}}class a{constructor(){this.providers=new Map,this.providers.set("CHATGPT",new r),this.providers.set("GEMINI",new o),this.providers.set("PERPLEXITY",new n),this.providers.set("COPILOT",new i),this.providers.set("AIOVERVIEW",new s),this.providers.set("AIMODE",new l)}parse(t,r){const o=e.detect(t);if(!o)return this.parseGeneric(t);const n=this.providers.get(o.provider);if(!n)return null;try{const e=n.parse(t,r);return{...e,metadata:{...e.metadata,detectionConfidence:o.confidence}}}catch(e){return console.error(`Failed to parse response with ${o.provider}:`,e),null}}parseWithProvider(e,t,r){const o=this.providers.get(t);if(!o)throw new Error(`Unknown provider: ${t}`);try{return o.parse(e,r)}catch(e){return console.error(`Failed to parse response with ${t}:`,e),null}}detectProvider(t){const r=e.detect(t);return(null==r?void 0:r.provider)||null}detectAllProviders(t){return e.getAllProviders(t)}parseGeneric(e){let t="",r="";let o=e;var n;return e.result&&(o=e.result),"string"==typeof(n=o)?n.trim().startsWith("<")&&n.includes(">")?t=n:r=n:"object"==typeof n&&null!==n&&(n.html?t=n.html:n.content?n.content.trim().startsWith("<")?t=n.content:r=n.content:n.text&&(r=n.text)),t||r?(t&&(t=t.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gim,""),t=t.replace(/<noscript[\s\S]*?<\/noscript>/gi,"")),{provider:"CHATGPT",html:t,text:r,metadata:{isGeneric:!0}}):null}getSupportedProviders(){return Array.from(this.providers.keys())}registerProvider(e,t){this.providers.set(e,t)}}const d=new a,c=(e,t)=>d.parse(e,t),v=e=>d.detectProvider(e);export{l as AIModeProvider,s as AIOverviewProvider,a as AIResponseParser,t as BaseProvider,r as ChatGPTProvider,i as CopilotProvider,o as GeminiProvider,n as PerplexityProvider,e as ProviderDetector,v as detectProvider,c as parseAiResponse,d as parser};
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/core/detector.ts","../src/providers/base-provider.ts","../src/providers/chatgpt.ts","../src/providers/gemini.ts","../src/providers/perplexity.ts","../src/providers/copilot.ts","../src/providers/ai-overview.ts","../src/providers/ai-mode.ts","../src/core/parser.ts"],"sourcesContent":["import { AIProvider, DetectedProvider } from './types';\n\nexport class ProviderDetector {\n private static readonly PROVIDER_PATTERNS = {\n CHATGPT: [\n // Check for token-based CSS classes\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('bg-token-bg-primary') || html.includes('text-token-text-secondary');\n },\n // Check for OpenAI-specific patterns\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('chatgpt.com') || html.includes('openai.com');\n },\n ],\n GEMINI: [\n // Check for Material Design components\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('bard-sidenav') || html.includes('mat-sidenav');\n },\n // Check for Gemini-specific classes\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('gemini.google.com') || html.includes('gem-sys-color');\n },\n ],\n PERPLEXITY: [\n // Check for Perplexity-specific structure\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('perplexity.ai') || html.includes('prose');\n },\n ],\n COPILOT: [\n // Check for Copilot-specific elements\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('copilot.microsoft.com') || html.includes('data-testid=\"sidebar-container\"');\n },\n ],\n AIOVERVIEW: [\n // Check for AI Overview specific structure\n (response: any) => {\n // Check for aioverview nested structure\n if (response?.result?.aioverview?.text) return true;\n if (response?.aioverview?.text) return true;\n\n // Check for WIZ_global_data (AI Overview specific)\n const html = response?.result?.html || response?.html || '';\n return html.includes('WIZ_global_data') || html.includes('DnVkpd');\n },\n ],\n AIMODE: [\n // Check for AI Mode specific elements\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('DZ13He') || html.includes('wYq63b') || html.includes('AI Mode');\n },\n ],\n };\n\n /**\n * Detect the AI provider from the response\n */\n static detect(response: any): DetectedProvider | null {\n const scores: Record<AIProvider, number> = {\n CHATGPT: 0,\n GEMINI: 0,\n PERPLEXITY: 0,\n COPILOT: 0,\n AIOVERVIEW: 0,\n AIMODE: 0,\n };\n\n // Score each provider based on pattern matches\n for (const [provider, patterns] of Object.entries(this.PROVIDER_PATTERNS)) {\n for (const pattern of patterns) {\n try {\n if (pattern(response)) {\n scores[provider as AIProvider]++;\n }\n } catch (error) {\n // Ignore pattern errors\n }\n }\n }\n\n // Find the provider with the highest score\n let maxScore = 0;\n let detectedProvider: AIProvider | null = null;\n\n for (const [provider, score] of Object.entries(scores)) {\n if (score > maxScore) {\n maxScore = score;\n detectedProvider = provider as AIProvider;\n }\n }\n\n // Return null if no patterns matched\n if (maxScore === 0) return null;\n\n return {\n provider: detectedProvider!,\n confidence: maxScore / Object.values(scores).reduce((a, b) => Math.max(a, b), 1),\n };\n }\n\n /**\n * Get all possible providers with their confidence scores\n */\n static getAllProviders(response: any): DetectedProvider[] {\n const providers: DetectedProvider[] = [];\n\n for (const [provider, patterns] of Object.entries(this.PROVIDER_PATTERNS)) {\n let matches = 0;\n for (const pattern of patterns) {\n try {\n if (pattern(response)) {\n matches++;\n }\n } catch (error) {\n // Ignore pattern errors\n }\n }\n\n if (matches > 0) {\n providers.push({\n provider: provider as AIProvider,\n confidence: matches / patterns.length,\n });\n }\n }\n\n // Sort by confidence (highest first)\n return providers.sort((a, b) => b.confidence - a.confidence);\n }\n\n /**\n * Validate if a detected provider is likely correct\n */\n static validate(response: any, provider: AIProvider): boolean {\n const detected = this.detect(response);\n return detected?.provider === provider && detected.confidence > 0.5;\n }\n}","import { AIProvider, ParsedResponse, ParseOptions, StyleOptions, ContentExtraction } from '../core/types';\n\nexport abstract class BaseProvider {\n abstract readonly name: AIProvider;\n abstract readonly baseUrl: string;\n\n /**\n * Parse an AI response and return structured content\n */\n abstract parse(response: any, options?: ParseOptions): ParsedResponse;\n\n /**\n * Extract content from the response object\n */\n abstract extractContent(response: any): ContentExtraction;\n\n /**\n * Inject provider-specific styles into HTML\n */\n injectStyles(html: string, options?: StyleOptions): string {\n let styledHtml = html;\n\n // Inject base URL if not already present\n if (options?.baseUrl && !html.includes('<base ')) {\n if (styledHtml.includes('<head>')) {\n styledHtml = styledHtml.replace(\n '<head>',\n `<head><base href=\"${options.baseUrl}\">`,\n );\n } else {\n styledHtml = styledHtml.replace(\n /(<html[^>]*>)/i,\n `$1<head><base href=\"${options.baseUrl}\"></head>`,\n );\n }\n }\n\n // Inject styles only if customCSS is provided\n const styles = options?.customCSS || '';\n\n if (styles && styledHtml.includes('<head>')) {\n styledHtml = styledHtml.replace('<head>', `<head><style>${styles}</style>`);\n } else if (styles) {\n styledHtml = styledHtml.replace(\n /(<html[^>]*>)/i,\n `$1<head><style>${styles}</style></head>`,\n );\n }\n\n return styledHtml;\n }\n\n /**\n * Sanitize HTML content\n */\n sanitizeHtml(html: string): string {\n // Remove scripts\n let sanitized = html.replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script>/gim, '');\n\n // Remove noscript tags\n sanitized = sanitized.replace(/<noscript[\\s\\S]*?<\\/noscript>/gi, '');\n\n return sanitized;\n }\n\n /**\n * Check if content is a full HTML document\n */\n isFullDocument(html: string): boolean {\n return /^\\s*(<html|<!DOCTYPE)/i.test(html);\n }\n\n /**\n * Remove all links from HTML, keeping the text content\n */\n removeLinks(html: string): string {\n // Replace anchor tags with their text content\n return html.replace(/<a\\b[^>]*>([\\s\\S]*?)<\\/a>/gim, '$1');\n }\n\n /**\n * Wrap HTML fragment in a document\n */\n wrapFragment(html: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; script-src 'none'; style-src 'unsafe-inline';\">\n <style>\n body {\n margin: 0;\n padding: 16px;\n font-family: system-ui, -apple-system, sans-serif;\n white-space: pre-wrap;\n word-wrap: break-word;\n }\n img { max-width: 100%; height: auto; }\n body {\n pointer-events: none;\n user-select: none;\n }\n </style>\n</head>\n<body>\n ${html}\n</body>\n</html>`;\n }\n}","import { BaseProvider } from \"./base-provider\";\nimport {\n AIProvider,\n ParsedResponse,\n ParseOptions,\n ContentExtraction,\n} from \"../core/types\";\n\nexport class ChatGPTProvider extends BaseProvider {\n readonly name: AIProvider = \"CHATGPT\";\n readonly baseUrl = \"https://chatgpt.com\";\n\n\n extractContent(response: any): ContentExtraction {\n let html = \"\";\n let text = \"\";\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // Extract content\n if (typeof dataToCheck === \"string\") {\n if (dataToCheck.trim().startsWith(\"<\") && dataToCheck.includes(\">\")) {\n html = dataToCheck;\n } else {\n text = dataToCheck;\n }\n } else if (typeof dataToCheck === \"object\" && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith(\"<\")) {\n html = dataToCheck.content;\n } else {\n text = dataToCheck.content;\n }\n } else if (dataToCheck.text) {\n text = dataToCheck.text;\n }\n }\n\n return { html, text };\n }\n\n /**\n * Remove links from HTML, preserving badge styling\n * Converts <a> tags to <span> while keeping class attributes\n */\n removeLinks(html: string): string {\n // Replace anchor tags with span tags, keeping all attributes except href/alt/rel/target\n return html\n .replace(/<a\\b([^>]*)>/gi, (match, attributes) => {\n // Remove link-specific attributes but keep others (class, style, etc.)\n const cleanedAttrs = attributes\n .replace(/\\s+href\\s*=\\s*[\"'][^\"']*[\"']/gi, \"\")\n .replace(/\\s+alt\\s*=\\s*[\"'][^\"']*[\"']/gi, \"\")\n .replace(/\\s+rel\\s*=\\s*[\"'][^\"']*[\"']/gi, \"\")\n .replace(/\\s+target\\s*=\\s*[\"'][^\"']*[\"']/gi, \"\")\n .trim();\n return `<span${cleanedAttrs ? \" \" + cleanedAttrs : \"\"}>`;\n })\n .replace(/<\\/a>/gi, \"</span>\");\n }\n\n /**\n * Remove ChatGPT sidebar from HTML\n */\n removeSidebar(html: string): string {\n let cleaned = html;\n\n // Hide Sidebar\n cleaned = cleaned.replace(/<nav[^>]*>.*?<\\/nav>/gis, \"\");\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*sidebar[^\"]*\"[^>]*>.*?<\\/div>/gis,\n \"\"\n );\n\n return cleaned;\n }\n\n /**\n * Remove ChatGPT footer/composer from HTML\n */\n removeFooter(html: string): string {\n let cleaned = html;\n\n // Remove the thread-bottom-container (scroll button container)\n cleaned = cleaned.replace(\n /<div[^>]*id=\"thread-bottom-container\"[^>]*>.*?<\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove the entire thread-bottom div (contains composer and inputs)\n cleaned = cleaned.replace(\n /<div[^>]*id=\"thread-bottom\"[^>]*>.*?<\\/div><\\/div><\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove composer leading button (plus button)\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*\\[grid-area:leading\\][^\"]*\"[^>]*>.*?<\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove composer footer actions (search button, etc.)\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*\\[grid-area:footer\\][^\"]*\"[^>]*>.*?<\\/div><\\/div><\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove composer trailing buttons (dictate, send buttons)\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*\\[grid-area:trailing\\][^\"]*\"[^>]*>.*?<\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove disclaimer footer (\"ChatGPT can make mistakes...\")\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*text-token-text-secondary[^\"]*\"[^>]*>.*?ChatGPT can make mistakes\\. Check important info\\..*?<\\/div><\\/div>/gis,\n \"\"\n );\n\n return cleaned;\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, text } = this.extractContent(response);\n\n if (!html) {\n throw new Error(\"No HTML content found in ChatGPT response\");\n }\n\n let finalHtml = html;\n\n const removeFooter = options?.removeFooter ?? false;\n\n // Sanitize HTML\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove sidebar if requested\n if (options?.removeSidebar) {\n finalHtml = this.removeSidebar(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // Inject styles - always apply styles for proper theming\n const stylesToInject = options?.invertColors\n ? `\n /* Force Dark Background on main containers */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n }\n `\n : `\n /* Force Light Background on main containers */\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Override ChatGPT token text color classes - targeting text content only */\n p[class*=\"text-token\"], span[class*=\"text-token\"], div[class*=\"text-token\"],\n h1[class*=\"text-token\"], h2[class*=\"text-token\"], h3[class*=\"text-token\"],\n h4[class*=\"text-token\"], h5[class*=\"text-token\"], h6[class*=\"text-token\"],\n li[class*=\"text-token\"], td[class*=\"text-token\"], th[class*=\"text-token\"],\n [class*=\"markdown-\"], strong, b, em, i, h1, h2, h3, h4, h5, h6, p, li, td, th {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*=\"border\"], [class*=\"divider\"], [class*=\"separator\"],\n [class*=\"border-t\"], [class*=\"border-b\"], [class*=\"border-l\"], [class*=\"border-r\"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n `;\n\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: stylesToInject,\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n text,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n sidebarRemoved: options?.removeSidebar || false,\n footerRemoved: removeFooter,\n linksRemoved: options?.removeLinks || false,\n colorsInverted: options?.invertColors || false,\n },\n };\n }\n}\n","import { BaseProvider } from './base-provider';\nimport { AIProvider, ParsedResponse, ParseOptions, ContentExtraction } from '../core/types';\n\nexport class GeminiProvider extends BaseProvider {\n readonly name: AIProvider = 'GEMINI';\n readonly baseUrl = 'https://gemini.google.com';\n\n\n extractContent(response: any): ContentExtraction {\n let html = '';\n let text = '';\n let sources: any[] = [];\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // Extract content\n if (typeof dataToCheck === 'string') {\n if (dataToCheck.trim().startsWith('<') && dataToCheck.includes('>')) {\n html = dataToCheck;\n } else {\n text = dataToCheck;\n }\n } else if (typeof dataToCheck === 'object' && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith('<')) {\n html = dataToCheck.content;\n } else {\n text = dataToCheck.content;\n }\n } else if (dataToCheck.text) {\n text = dataToCheck.text;\n }\n\n // Check for aioverview nested structure\n if (dataToCheck.aioverview) {\n if (dataToCheck.aioverview.text) text = dataToCheck.aioverview.text;\n if (dataToCheck.aioverview.sources) sources = dataToCheck.aioverview.sources;\n }\n }\n\n return { html, text, sources };\n }\n\n /**\n * Remove Gemini header/navbar from HTML\n */\n removeHeader(html: string): string {\n let cleaned = html;\n\n // Remove the Google navbar with boqOnegoogleliteOgbOneGoogleBar class\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*boqOnegoogleliteOgbOneGoogleBar[^\"]*\"[^>]*>.*?<\\/div><\\/div><\\/div><\\/div>/gis, '');\n\n // Remove side-nav-menu-button (hamburger menu)\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*side-nav-menu-button[^\"]*\"[^>]*>.*?<\\/div>/gis, '');\n\n // Remove top-bar-actions (About Gemini, Gemini App, Subscriptions, For Business links)\n cleaned = cleaned.replace(/<top-bar-actions[^>]*>.*?<\\/top-bar-actions>/gis, '');\n\n // Remove desktop-ogb-buffer\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*desktop-ogb-buffer[^\"]*\"[^>]*><\\/div>/gis, '');\n\n return cleaned;\n }\n\n /**\n * Remove Gemini footer/follow-up input box from HTML\n */\n removeFooter(html: string): string {\n // Remove input-container (text input, upload button, send button, disclaimer)\n return html.replace(/<input-container[^>]*>.*?<\\/input-container>/gis, '');\n }\n\n /**\n * Remove Gemini sidebar from HTML\n */\n removeSidebar(html: string): string {\n // Remove bard-sidenav element\n return html.replace(/<bard-sidenav[^>]*>.*?<\\/bard-sidenav>/gis, '');\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, text, sources } = this.extractContent(response);\n\n if (!html) {\n throw new Error('No HTML content found in Gemini response');\n }\n\n let finalHtml = html;\n\n const removeHeader = options?.removeHeader ?? false;\n const removeFooter = options?.removeFooter ?? false;\n\n // Sanitize HTML (remove scripts and noscript)\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove header if requested\n if (removeHeader) {\n finalHtml = this.removeHeader(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove sidebar if requested\n if (options?.removeSidebar) {\n finalHtml = this.removeSidebar(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // For Gemini, we need to extract content from WIZ_global_data for AI Overview/Mode\n // But for regular Gemini, we just inject styles\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: \"\",\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n text,\n sources,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n headerRemoved: removeHeader,\n footerRemoved: removeFooter,\n sidebarRemoved: options?.removeSidebar || false,\n linksRemoved: options?.removeLinks || false,\n },\n };\n }\n}","import { BaseProvider } from \"./base-provider\";\nimport {\n AIProvider,\n ParsedResponse,\n ParseOptions,\n ContentExtraction,\n} from \"../core/types\";\n\nexport class PerplexityProvider extends BaseProvider {\n readonly name: AIProvider = \"PERPLEXITY\";\n readonly baseUrl = \"https://www.perplexity.ai\";\n\n extractContent(response: any): ContentExtraction {\n let html = \"\";\n let text = \"\";\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // Extract content\n if (typeof dataToCheck === \"string\") {\n if (dataToCheck.trim().startsWith(\"<\") && dataToCheck.includes(\">\")) {\n html = dataToCheck;\n } else {\n text = dataToCheck;\n }\n } else if (typeof dataToCheck === \"object\" && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith(\"<\")) {\n html = dataToCheck.content;\n } else {\n text = dataToCheck.content;\n }\n } else if (dataToCheck.text) {\n text = dataToCheck.text;\n }\n }\n\n return { html, text };\n }\n\n /**\n * Remove Perplexity header/navbar from HTML\n */\n removeHeader(html: string): string {\n // Remove navbar by targeting the unique @container/header class\n // Matches from the navbar opening div through the border divider at the bottom\n return html.replace(/<div[^>]*class=\"[^\"]*@container\\/header[^\"]*\"[^>]*>.*?<div[^>]*class=\"[^\"]*absolute bottom-0 inset-x-0 border-b[^\"]*\"[^>]*><\\/div>/gis, '');\n }\n\n /**\n * Remove Perplexity footer/followup input from HTML\n */\n removeFooter(html: string): string {\n // Remove the fixed positioned followup container\n // Matches from erp-sidecar:fixed div through 4 closing divs\n return html.replace(/<div[^>]*class=\"[^\"]*erp-sidecar:fixed[^\"]*\"[^>]*>.*?<\\/div>\\s*<\\/div>\\s*<\\/div>\\s*<\\/div>/gis, '');\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, text } = this.extractContent(response);\n\n if (!html) {\n throw new Error(\"No HTML content found in Perplexity response\");\n }\n\n let finalHtml = html;\n\n const removeHeader = options?.removeHeader ?? false;\n const removeFooter = options?.removeFooter ?? false;\n\n // Sanitize HTML\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove header if requested\n if (removeHeader) {\n finalHtml = this.removeHeader(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // Inject styles with optional color inversion\n const colorInversionStyles = options?.invertColors ? `\n html {\n filter: invert(1) hue-rotate(180deg);\n background-color: white !important;\n }\n ` : \"\";\n\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: colorInversionStyles,\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n text,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n linksRemoved: options?.removeLinks || false,\n colorsInverted: options?.invertColors || false,\n headerRemoved: removeHeader,\n footerRemoved: removeFooter,\n },\n };\n }\n}\n","import { BaseProvider } from './base-provider';\nimport { AIProvider, ParsedResponse, ParseOptions, ContentExtraction } from '../core/types';\n\nexport class CopilotProvider extends BaseProvider {\n readonly name: AIProvider = 'COPILOT';\n readonly baseUrl = 'https://copilot.microsoft.com';\n\n\n extractContent(response: any): ContentExtraction {\n let html = '';\n let text = '';\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // Extract content\n if (typeof dataToCheck === 'string') {\n if (dataToCheck.trim().startsWith('<') && dataToCheck.includes('>')) {\n html = dataToCheck;\n } else {\n text = dataToCheck;\n }\n } else if (typeof dataToCheck === 'object' && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith('<')) {\n html = dataToCheck.content;\n } else {\n text = dataToCheck.content;\n }\n } else if (dataToCheck.text) {\n text = dataToCheck.text;\n }\n }\n\n return { html, text };\n }\n\n /**\n * Remove Copilot header/navbar from HTML\n */\n removeHeader(html: string): string {\n let cleaned = html;\n\n // Remove backstage-chats (hidden navbar element)\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*relative shrink-0 min-h-14[^\"]*\"[^>]*data-testid=\"backstage-chats\"[^>]*>.*?<\\/div>/gis, '');\n\n return cleaned;\n }\n\n /**\n * Remove Copilot footer/followup input box from HTML\n */\n removeFooter(html: string): string {\n let cleaned = html;\n\n // Remove the composer (input box at bottom)\n // Starts with: <div class=\"absolute bottom-0 w-full\">\n // Contains: file input, composer content with textarea, buttons (home, attach, chat mode, audio call)\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*absolute bottom-0 w-full[^\"]*\"[^>]*>.*?<\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div>/gis, '');\n\n // Remove spacer div for composer height\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*mt-\\[var\\(--composer-container-height\\)\\][^\"]*\"[^>]*><\\/div>/gis, '');\n\n // Remove \"scroll to bottom\" button (full container with button and inner content)\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*pointer-events-none absolute flex justify-center z-20 inset-x-0 bottom-\\[calc\\(var\\(--composer-container-height\\)\\+1rem\\)\\][^\"]*\"[^>]*>.*?<button[^>]*data-testid=\"scroll-to-bottom-button\"[^>]*>.*?<\\/button><\\/div>/gis, '');\n\n return cleaned;\n }\n\n /**\n * Remove Copilot sidebar from HTML\n */\n removeSidebar(html: string): string {\n let cleaned = html;\n\n // Remove the inner sidebar div\n // Starts with: <div class=\"absolute h-full w-0 will-change-auto max-md:bg-sidebar-light...\" style=\"width: 52px;\">\n // Ends right before: <main class=\"relative flex w-full min-w-0 will-change-auto...\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*absolute h-full w-0 will-change-auto max-md:bg-sidebar-light[^\"]*\"[^>]*style=\"[^\"]*\"[^>]*>.*?<main/gis, '<main');\n\n return cleaned;\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, text } = this.extractContent(response);\n\n if (!html) {\n throw new Error('No HTML content found in Copilot response');\n }\n\n let finalHtml = html;\n\n const removeHeader = options?.removeHeader ?? false;\n const removeFooter = options?.removeFooter ?? false;\n\n // Sanitize HTML\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove header if requested\n if (removeHeader) {\n finalHtml = this.removeHeader(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove sidebar if requested\n if (options?.removeSidebar) {\n finalHtml = this.removeSidebar(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // Inject styles\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: \"\",\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n text,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n headerRemoved: removeHeader,\n footerRemoved: removeFooter,\n sidebarRemoved: options?.removeSidebar || false,\n linksRemoved: options?.removeLinks || false,\n },\n };\n }\n}","import { BaseProvider } from \"./base-provider\";\nimport {\n AIProvider,\n ParsedResponse,\n ParseOptions,\n ContentExtraction,\n} from \"../core/types\";\n\nexport class AIOverviewProvider extends BaseProvider {\n readonly name: AIProvider = \"AIOVERVIEW\";\n readonly baseUrl = \"https://www.google.com\";\n\n extractContent(response: any): ContentExtraction {\n let html = \"\";\n let sources: any[] = [];\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // For AI Overview, extract sources but never use text field\n if (dataToCheck.aioverview) {\n if (dataToCheck.aioverview.sources) {\n sources = dataToCheck.aioverview.sources;\n }\n }\n\n // Only extract HTML field - never use text to build HTML\n if (typeof dataToCheck === \"string\") {\n if (dataToCheck.trim().startsWith(\"<\") && dataToCheck.includes(\">\")) {\n html = dataToCheck;\n }\n } else if (typeof dataToCheck === \"object\" && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith(\"<\")) {\n html = dataToCheck.content;\n }\n }\n }\n\n return { html, sources };\n }\n\n /**\n * Remove Google Search header/navbar from HTML\n */\n removeHeader(html: string): string {\n // Remove header elements via CSS injection\n return html;\n }\n\n /**\n * Remove Google Search footer from HTML\n */\n removeFooter(html: string): string {\n // Remove footer elements via CSS injection\n return html;\n }\n\n /**\n * Remove Google Search sidebar from HTML\n */\n removeSidebar(html: string): string {\n // Remove sidebar elements via CSS injection\n return html;\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, sources } = this.extractContent(response);\n\n if (!html) {\n throw new Error(\"No HTML content found in AI Overview response\");\n }\n\n let finalHtml = html;\n\n // For AI Overview, default to removing header and footer for cleaner display\n const removeHeader = options?.removeHeader ?? true;\n const removeFooter = options?.removeFooter ?? true;\n\n // Sanitize HTML\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove header if requested\n if (removeHeader) {\n finalHtml = this.removeHeader(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove sidebar if requested\n if (options?.removeSidebar) {\n finalHtml = this.removeSidebar(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // Build CSS based on options\n let stylesToInject = \"\";\n\n // Add background and text colors based on invertColors option\n if (options?.invertColors) {\n stylesToInject += `\n /* Dark theme */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n color: #e3e3e3 !important;\n }\n `;\n } else {\n stylesToInject += `\n /* Light theme - force all backgrounds to white */\n *, div, span, p, a, li, td, th, section, article, main, aside, nav, header, footer {\n background-color: #ffffff !important;\n }\n\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Force dark text color on all text elements */\n *, p, span, div, h1, h2, h3, h4, h5, h6, li, td, th, strong, b, em, i, a {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*=\"border\"], [class*=\"divider\"], [class*=\"separator\"],\n [class*=\"border-t\"], [class*=\"border-b\"], [class*=\"border-l\"], [class*=\"border-r\"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n `;\n }\n\n // Header removal styles\n if (removeHeader) {\n stylesToInject += `\n /* Header hiding */\n header, #header, #searchform, .sfbg, #appbar,\n div[role=\"navigation\"], #leftnav, #sidetogether,\n [role=\"banner\"], .Fgvgjc, #hdtb, .hdtb-msb,\n .DZ13He, .wYq63b, .eT9Cje, .bNg8Rb, .S6VXfe, .Lu57id {\n display: none !important;\n }\n `;\n }\n\n // Sidebar removal styles\n if (options?.removeSidebar) {\n stylesToInject += `\n /* Sidebar hiding */\n #leftnav, #sidetogether {\n display: none !important;\n }\n `;\n }\n\n // Footer removal styles\n if (removeFooter) {\n stylesToInject += `\n /* Footer hiding */\n footer, #footer, .fbar,\n .pdp-nav, [aria-label=\"Main menu\"], .gb_Td, .gb_L {\n display: none !important;\n }\n `;\n }\n\n // Inject base URL and styles\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: stylesToInject,\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n sources,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n headerRemoved: removeHeader,\n footerRemoved: removeFooter,\n sidebarRemoved: options?.removeSidebar || false,\n linksRemoved: options?.removeLinks || false,\n colorsInverted: options?.invertColors || false,\n },\n };\n }\n}\n","import { BaseProvider } from \"./base-provider\";\nimport {\n AIProvider,\n ParsedResponse,\n ParseOptions,\n ContentExtraction,\n} from \"../core/types\";\n\nexport class AIModeProvider extends BaseProvider {\n readonly name: AIProvider = \"AIMODE\";\n readonly baseUrl = \"https://www.google.com\";\n\n extractContent(response: any): ContentExtraction {\n let html = \"\";\n let text = \"\";\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // Extract content\n if (typeof dataToCheck === \"string\") {\n if (dataToCheck.trim().startsWith(\"<\") && dataToCheck.includes(\">\")) {\n html = dataToCheck;\n } else {\n text = dataToCheck;\n }\n } else if (typeof dataToCheck === \"object\" && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith(\"<\")) {\n html = dataToCheck.content;\n } else {\n text = dataToCheck.content;\n }\n } else if (dataToCheck.text) {\n text = dataToCheck.text;\n }\n }\n\n return { html, text };\n }\n\n /**\n * Remove Google Search header/navbar from HTML\n */\n removeHeader(html: string): string {\n let cleaned = html;\n\n // Remove the AI Mode filter/header bar\n // Starts with <div jsname=\"oEQ3x\" class=\"DZ13He YNk70c EjQTId\">\n // Contains the Filters and Topics navigation (AI Mode, All, Images, Videos, News, More)\n // Ends with closing divs after <hr class=\"e9c7U\">\n cleaned = cleaned.replace(\n /<div[^>]*jsname=\"oEQ3x\"[^>]*class=\"[^\"]*DZ13He[^\"]*\"[^>]*>.*?<hr class=\"e9c7U\">.*?<\\/div><\\/div><\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n return cleaned;\n }\n\n /**\n * Remove Google Search footer from HTML\n */\n removeFooter(html: string): string {\n let cleaned = html;\n\n // Remove the AI Mode input bar/footer\n // Target the most unique identifier: data-xid=\"aim-mars-input-plate\"\n // This element is deeply nested, so we need to match many closing divs\n cleaned = cleaned.replace(\n /<div[^>]*data-xid=\"aim-mars-input-plate\"[^>]*>[\\s\\S]*?<\\/div><\\/div><\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Fallback: Target by jscontroller P5gZDb which wraps the input plate\n cleaned = cleaned.replace(\n /<div[^>]*jscontroller=\"P5gZDb\"[^>]*>[\\s\\S]*?<\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Fallback: Target the t0ITR container\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*t0ITR hh3ttd[^\"]*\"[^>]*>[\\s\\S]*?<\\/div><\\/div>/gis,\n \"\"\n );\n\n // Fallback: Target outer y4VEUd vve6Ce wrapper\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*y4VEUd vve6Ce[^\"]*\"[^>]*>[\\s\\S]*?<\\/div><\\/div>/gis,\n \"\"\n );\n\n return cleaned;\n }\n\n /**\n * Remove Google Search sidebar from HTML\n */\n removeSidebar(html: string): string {\n let cleaned = html;\n\n // Remove the AI Mode history panel/slide-over\n // Target by aria-label and class\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*ho072b[^\"]*\"[^>]*aria-label=\"AI Mode history\"[^>]*>[\\s\\S]*?<\\/div><\\/div><\\/div><\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove the floating action button (FAB) with \"Start new search\" and \"AI Mode history\"\n // Target by jsname and class, contains OEwhSe wrapper\n cleaned = cleaned.replace(\n /<div[^>]*jsname=\"NlVIob\"[^>]*class=\"[^\"]*qEn1od[^\"]*\"[^>]*>[\\s\\S]*?<\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Fallback: Target by inner class OEwhSe\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*OEwhSe[^\"]*\"[^>]*>[\\s\\S]*?<\\/div><\\/div>/gis,\n \"\"\n );\n\n // Fallback: Target buttons with aria-label\n cleaned = cleaned.replace(\n /<button[^>]*aria-label=\"Start new search\"[^>]*>[\\s\\S]*?<\\/button>/gis,\n \"\"\n );\n cleaned = cleaned.replace(\n /<button[^>]*aria-label=\"AI Mode history\"[^>]*>[\\s\\S]*?<\\/button>/gis,\n \"\"\n );\n\n return cleaned;\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, text } = this.extractContent(response);\n\n if (!html) {\n throw new Error(\"No HTML content found in AI Mode response\");\n }\n\n let finalHtml = html;\n\n // For AI Mode, default to removing header and footer for cleaner display\n const removeHeader = options?.removeHeader ?? true;\n const removeFooter = options?.removeFooter ?? true;\n\n // Sanitize HTML\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove header if requested\n if (removeHeader) {\n finalHtml = this.removeHeader(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove sidebar if requested\n if (options?.removeSidebar) {\n finalHtml = this.removeSidebar(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // Build CSS based on options\n let stylesToInject = \"\";\n\n // Always inject general layout\n stylesToInject += ``;\n\n // Add background and text colors based on invertColors option\n if (options?.invertColors) {\n stylesToInject += `\n /* Dark theme */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n color: #e3e3e3 !important;\n }\n `;\n } else {\n stylesToInject += `\n /* Light theme - force all backgrounds to white */\n *, div, span, p, a, li, td, th, section, article, main, aside, nav, header, footer {\n background-color: #ffffff !important;\n }\n\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Force dark text color on all text elements */\n *, p, span, div, h1, h2, h3, h4, h5, h6, li, td, th, strong, b, em, i, a {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*=\"border\"], [class*=\"divider\"], [class*=\"separator\"],\n [class*=\"border-t\"], [class*=\"border-b\"], [class*=\"border-l\"], [class*=\"border-r\"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n `;\n }\n\n // Header removal styles\n if (removeHeader) {\n stylesToInject += `\n /* Header hiding */\n header, #header, #searchform, .sfbg, #appbar,\n div[role=\"navigation\"], #leftnav, #sidetogether,\n [role=\"banner\"], .Fgvgjc, #hdtb, .hdtb-msb,\n .DZ13He, .wYq63b, .eT9Cje, .bNg8Rb, .S6VXfe, .Lu57id {\n display: none !important;\n }\n `;\n }\n\n // Sidebar removal styles\n if (options?.removeSidebar) {\n stylesToInject += `\n /* Sidebar hiding */\n #leftnav, #sidetogether {\n display: none !important;\n }\n `;\n }\n\n // Footer removal styles\n if (removeFooter) {\n stylesToInject += `\n /* Footer hiding */\n footer, #footer, .fbar,\n .pdp-nav, [aria-label=\"Main menu\"], .gb_Td, .gb_L {\n display: none !important;\n }\n `;\n }\n\n // Inject base URL and styles\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: stylesToInject,\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n text,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n headerRemoved: removeHeader,\n footerRemoved: removeFooter,\n sidebarRemoved: options?.removeSidebar || false,\n linksRemoved: options?.removeLinks || false,\n colorsInverted: options?.invertColors || false,\n },\n };\n }\n}\n","import { AIProvider, ParsedResponse, ParseOptions } from './types';\nimport { ProviderDetector } from './detector';\nimport { ChatGPTProvider } from '../providers/chatgpt';\nimport { GeminiProvider } from '../providers/gemini';\nimport { PerplexityProvider } from '../providers/perplexity';\nimport { CopilotProvider } from '../providers/copilot';\nimport { AIOverviewProvider } from '../providers/ai-overview';\nimport { AIModeProvider } from '../providers/ai-mode';\n\nexport class AIResponseParser {\n private providers: Map<AIProvider, any> = new Map();\n\n constructor() {\n this.providers.set('CHATGPT', new ChatGPTProvider());\n this.providers.set('GEMINI', new GeminiProvider());\n this.providers.set('PERPLEXITY', new PerplexityProvider());\n this.providers.set('COPILOT', new CopilotProvider());\n this.providers.set('AIOVERVIEW', new AIOverviewProvider());\n this.providers.set('AIMODE', new AIModeProvider());\n }\n\n /**\n * Parse an AI response with auto-detected provider\n */\n parse(response: any, options?: ParseOptions): ParsedResponse | null {\n const detection = ProviderDetector.detect(response);\n\n if (!detection) {\n // Try to parse as generic HTML if no provider detected\n return this.parseGeneric(response, options);\n }\n\n const provider = this.providers.get(detection.provider);\n if (!provider) {\n return null;\n }\n\n try {\n const parsed = provider.parse(response, options);\n return {\n ...parsed,\n metadata: {\n ...parsed.metadata,\n detectionConfidence: detection.confidence,\n },\n };\n } catch (error) {\n console.error(`Failed to parse response with ${detection.provider}:`, error);\n return null;\n }\n }\n\n /**\n * Parse with explicit provider specification\n */\n parseWithProvider(\n response: any,\n provider: AIProvider,\n options?: ParseOptions\n ): ParsedResponse | null {\n const providerInstance = this.providers.get(provider);\n if (!providerInstance) {\n throw new Error(`Unknown provider: ${provider}`);\n }\n\n try {\n return providerInstance.parse(response, options);\n } catch (error) {\n console.error(`Failed to parse response with ${provider}:`, error);\n return null;\n }\n }\n\n /**\n * Detect the provider from a response\n */\n detectProvider(response: any): AIProvider | null {\n const detection = ProviderDetector.detect(response);\n return detection?.provider || null;\n }\n\n /**\n * Get all possible providers with confidence scores\n */\n detectAllProviders(response: any): Array<{ provider: AIProvider; confidence: number }> {\n return ProviderDetector.getAllProviders(response);\n }\n\n /**\n * Parse as generic HTML when no specific provider is detected\n */\n private parseGeneric(response: any, options?: ParseOptions): ParsedResponse | null {\n // Extract HTML or text from response\n let html = '';\n let text = '';\n\n const checkContent = (obj: any) => {\n if (typeof obj === 'string') {\n if (obj.trim().startsWith('<') && obj.includes('>')) {\n html = obj;\n } else {\n text = obj;\n }\n } else if (typeof obj === 'object' && obj !== null) {\n if (obj.html) {\n html = obj.html;\n } else if (obj.content) {\n if (obj.content.trim().startsWith('<')) {\n html = obj.content;\n } else {\n text = obj.content;\n }\n } else if (obj.text) {\n text = obj.text;\n }\n }\n };\n\n // Check nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n checkContent(dataToCheck);\n\n if (!html && !text) {\n return null;\n }\n\n // Basic sanitization\n if (html) {\n html = html.replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script>/gim, '');\n html = html.replace(/<noscript[\\s\\S]*?<\\/noscript>/gi, '');\n }\n\n return {\n provider: 'CHATGPT' as AIProvider, // Default to ChatGPT for unknown\n html,\n text,\n metadata: {\n isGeneric: true,\n },\n };\n }\n\n /**\n * Get list of supported providers\n */\n getSupportedProviders(): AIProvider[] {\n return Array.from(this.providers.keys());\n }\n\n /**\n * Register a custom provider\n */\n registerProvider(provider: AIProvider, instance: any): void {\n this.providers.set(provider, instance);\n }\n}\n\n// Export a singleton instance\nexport const parser = new AIResponseParser();\n\n// Convenience functions\nexport const parseAiResponse = (response: any, options?: ParseOptions) =>\n parser.parse(response, options);\n\nexport const detectProvider = (response: any) =>\n parser.detectProvider(response);"],"names":["ProviderDetector","detect","response","scores","CHATGPT","GEMINI","PERPLEXITY","COPILOT","AIOVERVIEW","AIMODE","provider","patterns","Object","entries","this","PROVIDER_PATTERNS","pattern","error","maxScore","detectedProvider","score","confidence","values","reduce","a","b","Math","max","getAllProviders","providers","matches","push","length","sort","validate","detected","html","_a","result","includes","_b","aioverview","text","_c","_d","BaseProvider","injectStyles","options","styledHtml","baseUrl","replace","styles","customCSS","sanitizeHtml","sanitized","isFullDocument","test","removeLinks","wrapFragment","ChatGPTProvider","constructor","name","extractContent","dataToCheck","trim","startsWith","content","match","attributes","cleanedAttrs","removeSidebar","cleaned","removeFooter","parse","Error","finalHtml","stylesToInject","invertColors","metadata","sidebarRemoved","footerRemoved","linksRemoved","colorsInverted","GeminiProvider","sources","removeHeader","headerRemoved","PerplexityProvider","colorInversionStyles","CopilotProvider","AIOverviewProvider","AIModeProvider","AIResponseParser","Map","set","detection","parseGeneric","get","parsed","detectionConfidence","console","parseWithProvider","providerInstance","detectProvider","detectAllProviders","obj","isGeneric","getSupportedProviders","Array","from","keys","registerProvider","instance","parser","parseAiResponse"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/core/detector.ts","../src/providers/base-provider.ts","../src/providers/chatgpt.ts","../src/providers/gemini.ts","../src/providers/perplexity.ts","../src/providers/copilot.ts","../src/providers/ai-overview.ts","../src/providers/ai-mode.ts","../src/core/parser.ts"],"sourcesContent":["import { AIProvider, DetectedProvider } from './types';\n\nexport class ProviderDetector {\n private static readonly PROVIDER_PATTERNS = {\n CHATGPT: [\n // Check for token-based CSS classes\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('bg-token-bg-primary') || html.includes('text-token-text-secondary');\n },\n // Check for OpenAI-specific patterns\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('chatgpt.com') || html.includes('openai.com');\n },\n ],\n GEMINI: [\n // Check for Material Design components\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('bard-sidenav') || html.includes('mat-sidenav');\n },\n // Check for Gemini-specific classes\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('gemini.google.com') || html.includes('gem-sys-color');\n },\n ],\n PERPLEXITY: [\n // Check for Perplexity-specific structure\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('perplexity.ai') || html.includes('prose');\n },\n ],\n COPILOT: [\n // Check for Copilot-specific elements\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('copilot.microsoft.com') || html.includes('data-testid=\"sidebar-container\"');\n },\n ],\n AIOVERVIEW: [\n // Check for AI Overview specific structure\n (response: any) => {\n // Check for aioverview nested structure\n if (response?.result?.aioverview?.text) return true;\n if (response?.aioverview?.text) return true;\n\n // Check for WIZ_global_data (AI Overview specific)\n const html = response?.result?.html || response?.html || '';\n return html.includes('WIZ_global_data') || html.includes('DnVkpd');\n },\n ],\n AIMODE: [\n // Check for AI Mode specific elements\n (response: any) => {\n const html = response?.result?.html || response?.html || '';\n return html.includes('DZ13He') || html.includes('wYq63b') || html.includes('AI Mode');\n },\n ],\n };\n\n /**\n * Detect the AI provider from the response\n */\n static detect(response: any): DetectedProvider | null {\n const scores: Record<AIProvider, number> = {\n CHATGPT: 0,\n GEMINI: 0,\n PERPLEXITY: 0,\n COPILOT: 0,\n AIOVERVIEW: 0,\n AIMODE: 0,\n };\n\n // Score each provider based on pattern matches\n for (const [provider, patterns] of Object.entries(this.PROVIDER_PATTERNS)) {\n for (const pattern of patterns) {\n try {\n if (pattern(response)) {\n scores[provider as AIProvider]++;\n }\n } catch {\n // Ignore pattern errors\n }\n }\n }\n\n // Find the provider with the highest score\n let maxScore = 0;\n let detectedProvider: AIProvider | null = null;\n\n for (const [provider, score] of Object.entries(scores)) {\n if (score > maxScore) {\n maxScore = score;\n detectedProvider = provider as AIProvider;\n }\n }\n\n // Return null if no patterns matched\n if (maxScore === 0) return null;\n\n return {\n provider: detectedProvider!,\n confidence: maxScore / Object.values(scores).reduce((a, b) => Math.max(a, b), 1),\n };\n }\n\n /**\n * Get all possible providers with their confidence scores\n */\n static getAllProviders(response: any): DetectedProvider[] {\n const providers: DetectedProvider[] = [];\n\n for (const [provider, patterns] of Object.entries(this.PROVIDER_PATTERNS)) {\n let matches = 0;\n for (const pattern of patterns) {\n try {\n if (pattern(response)) {\n matches++;\n }\n } catch {\n // Ignore pattern errors\n }\n }\n\n if (matches > 0) {\n providers.push({\n provider: provider as AIProvider,\n confidence: matches / patterns.length,\n });\n }\n }\n\n // Sort by confidence (highest first)\n return providers.sort((a, b) => b.confidence - a.confidence);\n }\n\n /**\n * Validate if a detected provider is likely correct\n */\n static validate(response: any, provider: AIProvider): boolean {\n const detected = this.detect(response);\n return detected?.provider === provider && detected.confidence > 0.5;\n }\n}","import { AIProvider, ParsedResponse, ParseOptions, StyleOptions, ContentExtraction } from '../core/types';\n\nexport abstract class BaseProvider {\n abstract readonly name: AIProvider;\n abstract readonly baseUrl: string;\n\n /**\n * Parse an AI response and return structured content\n */\n abstract parse(response: any, options?: ParseOptions): ParsedResponse;\n\n /**\n * Extract content from the response object\n */\n abstract extractContent(response: any): ContentExtraction;\n\n /**\n * Inject provider-specific styles into HTML\n */\n injectStyles(html: string, options?: StyleOptions): string {\n let styledHtml = html;\n\n // Inject base URL if not already present\n if (options?.baseUrl && !html.includes('<base ')) {\n if (styledHtml.includes('<head>')) {\n styledHtml = styledHtml.replace(\n '<head>',\n `<head><base href=\"${options.baseUrl}\">`,\n );\n } else {\n styledHtml = styledHtml.replace(\n /(<html[^>]*>)/i,\n `$1<head><base href=\"${options.baseUrl}\"></head>`,\n );\n }\n }\n\n // Inject styles only if customCSS is provided\n const styles = options?.customCSS || '';\n\n if (styles && styledHtml.includes('<head>')) {\n styledHtml = styledHtml.replace('<head>', `<head><style>${styles}</style>`);\n } else if (styles) {\n styledHtml = styledHtml.replace(\n /(<html[^>]*>)/i,\n `$1<head><style>${styles}</style></head>`,\n );\n }\n\n return styledHtml;\n }\n\n /**\n * Sanitize HTML content\n */\n sanitizeHtml(html: string): string {\n // Remove scripts\n let sanitized = html.replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script>/gim, '');\n\n // Remove noscript tags\n sanitized = sanitized.replace(/<noscript[\\s\\S]*?<\\/noscript>/gi, '');\n\n return sanitized;\n }\n\n /**\n * Check if content is a full HTML document\n */\n isFullDocument(html: string): boolean {\n return /^\\s*(<html|<!DOCTYPE)/i.test(html);\n }\n\n /**\n * Remove all links from HTML, keeping the text content\n */\n removeLinks(html: string): string {\n // Replace anchor tags with their text content\n return html.replace(/<a\\b[^>]*>([\\s\\S]*?)<\\/a>/gim, '$1');\n }\n\n /**\n * Wrap HTML fragment in a document\n */\n wrapFragment(html: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; script-src 'none'; style-src 'unsafe-inline';\">\n <style>\n body {\n margin: 0;\n padding: 16px;\n font-family: system-ui, -apple-system, sans-serif;\n white-space: pre-wrap;\n word-wrap: break-word;\n }\n img { max-width: 100%; height: auto; }\n body {\n pointer-events: none;\n user-select: none;\n }\n </style>\n</head>\n<body>\n ${html}\n</body>\n</html>`;\n }\n}","import { BaseProvider } from \"./base-provider\";\nimport {\n AIProvider,\n ParsedResponse,\n ParseOptions,\n ContentExtraction,\n} from \"../core/types\";\n\nexport class ChatGPTProvider extends BaseProvider {\n readonly name: AIProvider = \"CHATGPT\";\n readonly baseUrl = \"https://chatgpt.com\";\n\n\n extractContent(response: any): ContentExtraction {\n let html = \"\";\n let text = \"\";\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // Extract content\n if (typeof dataToCheck === \"string\") {\n if (dataToCheck.trim().startsWith(\"<\") && dataToCheck.includes(\">\")) {\n html = dataToCheck;\n } else {\n text = dataToCheck;\n }\n } else if (typeof dataToCheck === \"object\" && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith(\"<\")) {\n html = dataToCheck.content;\n } else {\n text = dataToCheck.content;\n }\n } else if (dataToCheck.text) {\n text = dataToCheck.text;\n }\n }\n\n return { html, text };\n }\n\n /**\n * Remove links from HTML, preserving badge styling\n * Converts <a> tags to <span> while keeping class attributes\n */\n removeLinks(html: string): string {\n // Replace anchor tags with span tags, keeping all attributes except href/alt/rel/target\n return html\n .replace(/<a\\b([^>]*)>/gi, (match, attributes) => {\n // Remove link-specific attributes but keep others (class, style, etc.)\n const cleanedAttrs = attributes\n .replace(/\\s+href\\s*=\\s*[\"'][^\"']*[\"']/gi, \"\")\n .replace(/\\s+alt\\s*=\\s*[\"'][^\"']*[\"']/gi, \"\")\n .replace(/\\s+rel\\s*=\\s*[\"'][^\"']*[\"']/gi, \"\")\n .replace(/\\s+target\\s*=\\s*[\"'][^\"']*[\"']/gi, \"\")\n .trim();\n return `<span${cleanedAttrs ? \" \" + cleanedAttrs : \"\"}>`;\n })\n .replace(/<\\/a>/gi, \"</span>\");\n }\n\n /**\n * Remove ChatGPT sidebar from HTML\n */\n removeSidebar(html: string): string {\n let cleaned = html;\n\n // Hide Sidebar\n cleaned = cleaned.replace(/<nav[^>]*>.*?<\\/nav>/gis, \"\");\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*sidebar[^\"]*\"[^>]*>.*?<\\/div>/gis,\n \"\"\n );\n\n return cleaned;\n }\n\n /**\n * Remove ChatGPT footer/composer from HTML\n */\n removeFooter(html: string): string {\n let cleaned = html;\n\n // Remove the thread-bottom-container (scroll button container)\n cleaned = cleaned.replace(\n /<div[^>]*id=\"thread-bottom-container\"[^>]*>.*?<\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove the entire thread-bottom div (contains composer and inputs)\n cleaned = cleaned.replace(\n /<div[^>]*id=\"thread-bottom\"[^>]*>.*?<\\/div><\\/div><\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove composer leading button (plus button)\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*\\[grid-area:leading\\][^\"]*\"[^>]*>.*?<\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove composer footer actions (search button, etc.)\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*\\[grid-area:footer\\][^\"]*\"[^>]*>.*?<\\/div><\\/div><\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove composer trailing buttons (dictate, send buttons)\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*\\[grid-area:trailing\\][^\"]*\"[^>]*>.*?<\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove disclaimer footer (\"ChatGPT can make mistakes...\")\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*text-token-text-secondary[^\"]*\"[^>]*>.*?ChatGPT can make mistakes\\. Check important info\\..*?<\\/div><\\/div>/gis,\n \"\"\n );\n\n return cleaned;\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, text } = this.extractContent(response);\n\n if (!html) {\n throw new Error(\"No HTML content found in ChatGPT response\");\n }\n\n let finalHtml = html;\n\n const removeFooter = options?.removeFooter ?? false;\n\n // Sanitize HTML\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove sidebar if requested\n if (options?.removeSidebar) {\n finalHtml = this.removeSidebar(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // Inject styles - always apply styles for proper theming\n const stylesToInject = options?.invertColors\n ? `\n /* Force Dark Background on main containers */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n }\n `\n : `\n /* Force Light Background on main containers */\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Override ChatGPT token text color classes - targeting text content only */\n p[class*=\"text-token\"], span[class*=\"text-token\"], div[class*=\"text-token\"],\n h1[class*=\"text-token\"], h2[class*=\"text-token\"], h3[class*=\"text-token\"],\n h4[class*=\"text-token\"], h5[class*=\"text-token\"], h6[class*=\"text-token\"],\n li[class*=\"text-token\"], td[class*=\"text-token\"], th[class*=\"text-token\"],\n [class*=\"markdown-\"], strong, b, em, i, h1, h2, h3, h4, h5, h6, p, li, td, th {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*=\"border\"], [class*=\"divider\"], [class*=\"separator\"],\n [class*=\"border-t\"], [class*=\"border-b\"], [class*=\"border-l\"], [class*=\"border-r\"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n `;\n\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: stylesToInject,\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n text,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n sidebarRemoved: options?.removeSidebar || false,\n footerRemoved: removeFooter,\n linksRemoved: options?.removeLinks || false,\n colorsInverted: options?.invertColors || false,\n },\n };\n }\n}\n","import { BaseProvider } from './base-provider';\nimport { AIProvider, ParsedResponse, ParseOptions, ContentExtraction } from '../core/types';\n\nexport class GeminiProvider extends BaseProvider {\n readonly name: AIProvider = 'GEMINI';\n readonly baseUrl = 'https://gemini.google.com';\n\n\n extractContent(response: any): ContentExtraction {\n let html = '';\n let text = '';\n let sources: any[] = [];\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // Extract content\n if (typeof dataToCheck === 'string') {\n if (dataToCheck.trim().startsWith('<') && dataToCheck.includes('>')) {\n html = dataToCheck;\n } else {\n text = dataToCheck;\n }\n } else if (typeof dataToCheck === 'object' && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith('<')) {\n html = dataToCheck.content;\n } else {\n text = dataToCheck.content;\n }\n } else if (dataToCheck.text) {\n text = dataToCheck.text;\n }\n\n // Check for aioverview nested structure\n if (dataToCheck.aioverview) {\n if (dataToCheck.aioverview.text) text = dataToCheck.aioverview.text;\n if (dataToCheck.aioverview.sources) sources = dataToCheck.aioverview.sources;\n }\n }\n\n return { html, text, sources };\n }\n\n /**\n * Remove Gemini header/navbar from HTML\n */\n removeHeader(html: string): string {\n let cleaned = html;\n\n // Remove the Google navbar with boqOnegoogleliteOgbOneGoogleBar class\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*boqOnegoogleliteOgbOneGoogleBar[^\"]*\"[^>]*>.*?<\\/div><\\/div><\\/div><\\/div>/gis, '');\n\n // Remove side-nav-menu-button (hamburger menu)\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*side-nav-menu-button[^\"]*\"[^>]*>.*?<\\/div>/gis, '');\n\n // Remove top-bar-actions (About Gemini, Gemini App, Subscriptions, For Business links)\n cleaned = cleaned.replace(/<top-bar-actions[^>]*>.*?<\\/top-bar-actions>/gis, '');\n\n // Remove desktop-ogb-buffer\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*desktop-ogb-buffer[^\"]*\"[^>]*><\\/div>/gis, '');\n\n return cleaned;\n }\n\n /**\n * Remove Gemini footer/follow-up input box from HTML\n */\n removeFooter(html: string): string {\n // Remove input-container (text input, upload button, send button, disclaimer)\n return html.replace(/<input-container[^>]*>.*?<\\/input-container>/gis, '');\n }\n\n /**\n * Remove Gemini sidebar from HTML\n */\n removeSidebar(html: string): string {\n // Remove bard-sidenav element\n return html.replace(/<bard-sidenav[^>]*>.*?<\\/bard-sidenav>/gis, '');\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, text, sources } = this.extractContent(response);\n\n if (!html) {\n throw new Error('No HTML content found in Gemini response');\n }\n\n let finalHtml = html;\n\n const removeHeader = options?.removeHeader ?? false;\n const removeFooter = options?.removeFooter ?? false;\n\n // Sanitize HTML (remove scripts and noscript)\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove header if requested\n if (removeHeader) {\n finalHtml = this.removeHeader(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove sidebar if requested\n if (options?.removeSidebar) {\n finalHtml = this.removeSidebar(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // For Gemini, we need to extract content from WIZ_global_data for AI Overview/Mode\n // But for regular Gemini, we just inject styles\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: \"\",\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n text,\n sources,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n headerRemoved: removeHeader,\n footerRemoved: removeFooter,\n sidebarRemoved: options?.removeSidebar || false,\n linksRemoved: options?.removeLinks || false,\n },\n };\n }\n}","import { BaseProvider } from \"./base-provider\";\nimport {\n AIProvider,\n ParsedResponse,\n ParseOptions,\n ContentExtraction,\n} from \"../core/types\";\n\nexport class PerplexityProvider extends BaseProvider {\n readonly name: AIProvider = \"PERPLEXITY\";\n readonly baseUrl = \"https://www.perplexity.ai\";\n\n extractContent(response: any): ContentExtraction {\n let html = \"\";\n let text = \"\";\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // Extract content\n if (typeof dataToCheck === \"string\") {\n if (dataToCheck.trim().startsWith(\"<\") && dataToCheck.includes(\">\")) {\n html = dataToCheck;\n } else {\n text = dataToCheck;\n }\n } else if (typeof dataToCheck === \"object\" && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith(\"<\")) {\n html = dataToCheck.content;\n } else {\n text = dataToCheck.content;\n }\n } else if (dataToCheck.text) {\n text = dataToCheck.text;\n }\n }\n\n return { html, text };\n }\n\n /**\n * Remove Perplexity header/navbar from HTML\n */\n removeHeader(html: string): string {\n // Remove navbar by targeting the unique @container/header class\n // Matches from the navbar opening div through the border divider at the bottom\n return html.replace(\n /<div[^>]*class=\"[^\"]*@container\\/header[^\"]*\"[^>]*>.*?<div[^>]*class=\"[^\"]*absolute bottom-0 inset-x-0 border-b[^\"]*\"[^>]*><\\/div>/gis,\n \"\"\n );\n }\n\n /**\n * Remove Perplexity footer/followup input from HTML\n */\n removeFooter(html: string): string {\n let cleaned = html;\n const regex =\n /<div[^>]*class=\"[^\"]*erp-sidecar:fixed erp-sidecar:w-full bottom-safeAreaInsetBottom p-md pointer-events-none z-10 absolute border-subtlest ring-subtlest divide-subtlest bg-transparent[^\"]*\"[^>]*>[\\s\\S]*?(?=<div[^>]*class=\"[^\"]*fixed inset-y-0 right-0[^\"]*\")/gis;\n cleaned = cleaned.replace(regex, \"\");\n return cleaned;\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, text } = this.extractContent(response);\n\n if (!html) {\n throw new Error(\"No HTML content found in Perplexity response\");\n }\n\n let finalHtml = html;\n\n const removeHeader = options?.removeHeader ?? false;\n const removeFooter = options?.removeFooter ?? false;\n\n // Sanitize HTML\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove header if requested\n if (removeHeader) {\n finalHtml = this.removeHeader(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // Inject styles with optional color inversion\n const colorInversionStyles = options?.invertColors\n ? `\n html {\n filter: invert(1) hue-rotate(180deg);\n background-color: white !important;\n }\n `\n : \"\";\n\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: colorInversionStyles,\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n text,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n linksRemoved: options?.removeLinks || false,\n colorsInverted: options?.invertColors || false,\n headerRemoved: removeHeader,\n footerRemoved: removeFooter,\n },\n };\n }\n}\n","import { BaseProvider } from './base-provider';\nimport { AIProvider, ParsedResponse, ParseOptions, ContentExtraction } from '../core/types';\n\nexport class CopilotProvider extends BaseProvider {\n readonly name: AIProvider = 'COPILOT';\n readonly baseUrl = 'https://copilot.microsoft.com';\n\n\n extractContent(response: any): ContentExtraction {\n let html = '';\n let text = '';\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // Extract content\n if (typeof dataToCheck === 'string') {\n if (dataToCheck.trim().startsWith('<') && dataToCheck.includes('>')) {\n html = dataToCheck;\n } else {\n text = dataToCheck;\n }\n } else if (typeof dataToCheck === 'object' && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith('<')) {\n html = dataToCheck.content;\n } else {\n text = dataToCheck.content;\n }\n } else if (dataToCheck.text) {\n text = dataToCheck.text;\n }\n }\n\n return { html, text };\n }\n\n /**\n * Remove Copilot header/navbar from HTML\n */\n removeHeader(html: string): string {\n let cleaned = html;\n\n // Remove backstage-chats (hidden navbar element)\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*relative shrink-0 min-h-14[^\"]*\"[^>]*data-testid=\"backstage-chats\"[^>]*>.*?<\\/div>/gis, '');\n\n return cleaned;\n }\n\n /**\n * Remove Copilot footer/followup input box from HTML\n */\n removeFooter(html: string): string {\n let cleaned = html;\n\n // Remove the composer (input box at bottom)\n // Starts with: <div class=\"absolute bottom-0 w-full\">\n // Contains: file input, composer content with textarea, buttons (home, attach, chat mode, audio call)\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*absolute bottom-0 w-full[^\"]*\"[^>]*>.*?<\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div><\\/div>/gis, '');\n\n // Remove spacer div for composer height\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*mt-\\[var\\(--composer-container-height\\)\\][^\"]*\"[^>]*><\\/div>/gis, '');\n\n // Remove \"scroll to bottom\" button (full container with button and inner content)\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*pointer-events-none absolute flex justify-center z-20 inset-x-0 bottom-\\[calc\\(var\\(--composer-container-height\\)\\+1rem\\)\\][^\"]*\"[^>]*>.*?<button[^>]*data-testid=\"scroll-to-bottom-button\"[^>]*>.*?<\\/button><\\/div>/gis, '');\n\n return cleaned;\n }\n\n /**\n * Remove Copilot cookie banner from HTML\n */\n removeCookieBanner(html: string): string {\n let cleaned = html;\n\n // Remove cookie banner by id\n cleaned = cleaned.replace(/<div[^>]*id=\"cookie-banner\"[^>]*>.*?<\\/div>.*?<\\/div>.*?<\\/div>.*?<\\/div>/gis, '');\n\n // Remove cookie banner by max-w-cookie-banner class (outer container)\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*max-w-cookie-banner[^\"]*\"[^>]*>.*?<\\/div>.*?<\\/div>.*?<\\/div>.*?<\\/div>/gis, '');\n\n return cleaned;\n }\n\n /**\n * Remove Copilot sidebar from HTML\n */\n removeSidebar(html: string): string {\n let cleaned = html;\n\n // Remove the inner sidebar div\n // Starts with: <div class=\"absolute h-full w-0 will-change-auto max-md:bg-sidebar-light...\" style=\"width: 52px;\">\n // Ends right before: <main class=\"relative flex w-full min-w-0 will-change-auto...\n cleaned = cleaned.replace(/<div[^>]*class=\"[^\"]*absolute h-full w-0 will-change-auto max-md:bg-sidebar-light[^\"]*\"[^>]*style=\"[^\"]*\"[^>]*>.*?<main/gis, '<main');\n\n return cleaned;\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, text } = this.extractContent(response);\n\n if (!html) {\n throw new Error('No HTML content found in Copilot response');\n }\n\n let finalHtml = html;\n\n const removeHeader = options?.removeHeader ?? false;\n const removeFooter = options?.removeFooter ?? false;\n\n // Sanitize HTML\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove cookie banner (always)\n finalHtml = this.removeCookieBanner(finalHtml);\n\n // Remove header if requested\n if (removeHeader) {\n finalHtml = this.removeHeader(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove sidebar if requested\n if (options?.removeSidebar) {\n finalHtml = this.removeSidebar(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // Inject styles\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: \"\",\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n text,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n cookieBannerRemoved: true,\n headerRemoved: removeHeader,\n footerRemoved: removeFooter,\n sidebarRemoved: options?.removeSidebar || false,\n linksRemoved: options?.removeLinks || false,\n },\n };\n }\n}","import { BaseProvider } from \"./base-provider\";\nimport {\n AIProvider,\n ParsedResponse,\n ParseOptions,\n ContentExtraction,\n} from \"../core/types\";\n\nexport class AIOverviewProvider extends BaseProvider {\n readonly name: AIProvider = \"AIOVERVIEW\";\n readonly baseUrl = \"https://www.google.com\";\n\n extractContent(response: any): ContentExtraction {\n let html = \"\";\n let sources: any[] = [];\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // For AI Overview, extract sources but never use text field\n if (dataToCheck.aioverview) {\n if (dataToCheck.aioverview.sources) {\n sources = dataToCheck.aioverview.sources;\n }\n }\n\n // Only extract HTML field - never use text to build HTML\n if (typeof dataToCheck === \"string\") {\n if (dataToCheck.trim().startsWith(\"<\") && dataToCheck.includes(\">\")) {\n html = dataToCheck;\n }\n } else if (typeof dataToCheck === \"object\" && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith(\"<\")) {\n html = dataToCheck.content;\n }\n }\n }\n\n return { html, sources };\n }\n\n /**\n * Remove Google Search header/navbar from HTML\n */\n removeHeader(html: string): string {\n // Remove header elements via CSS injection\n return html;\n }\n\n /**\n * Remove Google Search footer from HTML\n */\n removeFooter(html: string): string {\n // Remove footer elements via CSS injection\n return html;\n }\n\n /**\n * Remove Google Search sidebar from HTML\n */\n removeSidebar(html: string): string {\n // Remove sidebar elements via CSS injection\n return html;\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, sources } = this.extractContent(response);\n\n if (!html) {\n throw new Error(\"No HTML content found in AI Overview response\");\n }\n\n let finalHtml = html;\n\n // For AI Overview, default to removing header and footer for cleaner display\n const removeHeader = options?.removeHeader ?? true;\n const removeFooter = options?.removeFooter ?? true;\n\n // Sanitize HTML\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove header if requested\n if (removeHeader) {\n finalHtml = this.removeHeader(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove sidebar if requested\n if (options?.removeSidebar) {\n finalHtml = this.removeSidebar(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // Build CSS based on options\n let stylesToInject = \"\";\n\n // Add background and text colors based on invertColors option\n if (options?.invertColors) {\n stylesToInject += `\n /* Dark theme */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n color: #e3e3e3 !important;\n }\n `;\n } else {\n stylesToInject += `\n /* Light theme - force all backgrounds to white */\n *, div, span, p, a, li, td, th, section, article, main, aside, nav, header, footer {\n background-color: #ffffff !important;\n }\n\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Force dark text color on all text elements */\n *, p, span, div, h1, h2, h3, h4, h5, h6, li, td, th, strong, b, em, i, a {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*=\"border\"], [class*=\"divider\"], [class*=\"separator\"],\n [class*=\"border-t\"], [class*=\"border-b\"], [class*=\"border-l\"], [class*=\"border-r\"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n `;\n }\n\n // Header removal styles\n if (removeHeader) {\n stylesToInject += `\n /* Header hiding */\n header, #header, #searchform, .sfbg, #appbar,\n div[role=\"navigation\"], #leftnav, #sidetogether,\n [role=\"banner\"], .Fgvgjc, #hdtb, .hdtb-msb,\n .DZ13He, .wYq63b, .eT9Cje, .bNg8Rb, .S6VXfe, .Lu57id {\n display: none !important;\n }\n `;\n }\n\n // Sidebar removal styles\n if (options?.removeSidebar) {\n stylesToInject += `\n /* Sidebar hiding */\n #leftnav, #sidetogether {\n display: none !important;\n }\n `;\n }\n\n // Footer removal styles\n if (removeFooter) {\n stylesToInject += `\n /* Footer hiding */\n footer, #footer, .fbar,\n .pdp-nav, [aria-label=\"Main menu\"], .gb_Td, .gb_L {\n display: none !important;\n }\n `;\n }\n\n // Inject base URL and styles\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: stylesToInject,\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n sources,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n headerRemoved: removeHeader,\n footerRemoved: removeFooter,\n sidebarRemoved: options?.removeSidebar || false,\n linksRemoved: options?.removeLinks || false,\n colorsInverted: options?.invertColors || false,\n },\n };\n }\n}\n","import { BaseProvider } from \"./base-provider\";\nimport {\n AIProvider,\n ParsedResponse,\n ParseOptions,\n ContentExtraction,\n} from \"../core/types\";\n\nexport class AIModeProvider extends BaseProvider {\n readonly name: AIProvider = \"AIMODE\";\n readonly baseUrl = \"https://www.google.com\";\n\n extractContent(response: any): ContentExtraction {\n let html = \"\";\n let text = \"\";\n\n // Handle nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n // Extract content\n if (typeof dataToCheck === \"string\") {\n if (dataToCheck.trim().startsWith(\"<\") && dataToCheck.includes(\">\")) {\n html = dataToCheck;\n } else {\n text = dataToCheck;\n }\n } else if (typeof dataToCheck === \"object\" && dataToCheck !== null) {\n if (dataToCheck.html) {\n html = dataToCheck.html;\n } else if (dataToCheck.content) {\n if (dataToCheck.content.trim().startsWith(\"<\")) {\n html = dataToCheck.content;\n } else {\n text = dataToCheck.content;\n }\n } else if (dataToCheck.text) {\n text = dataToCheck.text;\n }\n }\n\n return { html, text };\n }\n\n /**\n * Remove Google Search header/navbar from HTML\n */\n removeHeader(html: string): string {\n let cleaned = html;\n\n // Remove the AI Mode filter/header bar\n // Starts with <div jsname=\"oEQ3x\" class=\"DZ13He YNk70c EjQTId\">\n // Contains the Filters and Topics navigation (AI Mode, All, Images, Videos, News, More)\n // Ends with closing divs after <hr class=\"e9c7U\">\n cleaned = cleaned.replace(\n /<div[^>]*jsname=\"oEQ3x\"[^>]*class=\"[^\"]*DZ13He[^\"]*\"[^>]*>.*?<hr class=\"e9c7U\">.*?<\\/div><\\/div><\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n return cleaned;\n }\n\n /**\n * Remove Google Search footer from HTML\n */\n removeFooter(html: string): string {\n let cleaned = html;\n\n // Remove the AI Mode input bar/footer\n // Target the most unique identifier: data-xid=\"aim-mars-input-plate\"\n // This element is deeply nested, so we need to match many closing divs\n cleaned = cleaned.replace(\n /<div[^>]*data-xid=\"aim-mars-input-plate\"[^>]*>[\\s\\S]*?<\\/div><\\/div><\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Fallback: Target by jscontroller P5gZDb which wraps the input plate\n cleaned = cleaned.replace(\n /<div[^>]*jscontroller=\"P5gZDb\"[^>]*>[\\s\\S]*?<\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Fallback: Target the t0ITR container\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*t0ITR hh3ttd[^\"]*\"[^>]*>[\\s\\S]*?<\\/div><\\/div>/gis,\n \"\"\n );\n\n // Fallback: Target outer y4VEUd vve6Ce wrapper\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*y4VEUd vve6Ce[^\"]*\"[^>]*>[\\s\\S]*?<\\/div><\\/div>/gis,\n \"\"\n );\n\n return cleaned;\n }\n\n /**\n * Remove Google Search sidebar from HTML\n */\n removeSidebar(html: string): string {\n let cleaned = html;\n\n // Remove the AI Mode history panel/slide-over\n // Target by aria-label and class\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*ho072b[^\"]*\"[^>]*aria-label=\"AI Mode history\"[^>]*>[\\s\\S]*?<\\/div><\\/div><\\/div><\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Remove the floating action button (FAB) with \"Start new search\" and \"AI Mode history\"\n // Target by jsname and class, contains OEwhSe wrapper\n cleaned = cleaned.replace(\n /<div[^>]*jsname=\"NlVIob\"[^>]*class=\"[^\"]*qEn1od[^\"]*\"[^>]*>[\\s\\S]*?<\\/div><\\/div><\\/div>/gis,\n \"\"\n );\n\n // Fallback: Target by inner class OEwhSe\n cleaned = cleaned.replace(\n /<div[^>]*class=\"[^\"]*OEwhSe[^\"]*\"[^>]*>[\\s\\S]*?<\\/div><\\/div>/gis,\n \"\"\n );\n\n // Fallback: Target buttons with aria-label\n cleaned = cleaned.replace(\n /<button[^>]*aria-label=\"Start new search\"[^>]*>[\\s\\S]*?<\\/button>/gis,\n \"\"\n );\n cleaned = cleaned.replace(\n /<button[^>]*aria-label=\"AI Mode history\"[^>]*>[\\s\\S]*?<\\/button>/gis,\n \"\"\n );\n\n return cleaned;\n }\n\n parse(response: any, options?: ParseOptions): ParsedResponse {\n const { html, text } = this.extractContent(response);\n\n if (!html) {\n throw new Error(\"No HTML content found in AI Mode response\");\n }\n\n let finalHtml = html;\n\n // For AI Mode, default to removing header and footer for cleaner display\n const removeHeader = options?.removeHeader ?? true;\n const removeFooter = options?.removeFooter ?? true;\n\n // Sanitize HTML\n finalHtml = this.sanitizeHtml(finalHtml);\n\n // Remove header if requested\n if (removeHeader) {\n finalHtml = this.removeHeader(finalHtml);\n }\n\n // Remove footer if requested\n if (removeFooter) {\n finalHtml = this.removeFooter(finalHtml);\n }\n\n // Remove sidebar if requested\n if (options?.removeSidebar) {\n finalHtml = this.removeSidebar(finalHtml);\n }\n\n // Remove links if requested\n if (options?.removeLinks) {\n finalHtml = this.removeLinks(finalHtml);\n }\n\n // Build CSS based on options\n let stylesToInject = \"\";\n\n // Always inject general layout\n /* Remove cookie consent banner */\n stylesToInject += `\n .KxvlWc, #CXQnmb {\n display: none !important;\n }\n `;\n\n // Add background and text colors based on invertColors option\n if (options?.invertColors) {\n stylesToInject += `\n /* Dark theme */\n html, body, main, article, footer, form {\n background-color: #131314 !important;\n color: #e3e3e3 !important;\n }\n `;\n } else {\n stylesToInject += `\n /* Light theme - force all backgrounds to white */\n *, div, span, p, a, li, td, th, section, article, main, aside, nav, header, footer {\n background-color: #ffffff !important;\n }\n\n html, body, main, article, footer, form {\n background-color: #ffffff !important;\n }\n\n /* Force dark text color on all text elements */\n *, p, span, div, h1, h2, h3, h4, h5, h6, li, td, th, strong, b, em, i, a {\n color: #1a1a1a !important;\n }\n\n /* Force borders and dividers to be visible */\n hr, [class*=\"border\"], [class*=\"divider\"], [class*=\"separator\"],\n [class*=\"border-t\"], [class*=\"border-b\"], [class*=\"border-l\"], [class*=\"border-r\"],\n table, tr, td, th {\n border-color: #d4d4d4 !important;\n }\n `;\n }\n\n // Header removal styles\n if (removeHeader) {\n stylesToInject += `\n /* Header hiding */\n header, #header, #searchform, .sfbg, #appbar,\n div[role=\"navigation\"], #leftnav, #sidetogether,\n [role=\"banner\"], .Fgvgjc, #hdtb, .hdtb-msb,\n .DZ13He, .wYq63b, .eT9Cje, .bNg8Rb, .S6VXfe, .Lu57id {\n display: none !important;\n }\n `;\n }\n\n // Sidebar removal styles\n if (options?.removeSidebar) {\n stylesToInject += `\n /* Sidebar hiding */\n #leftnav, #sidetogether {\n display: none !important;\n }\n `;\n }\n\n // Footer removal styles\n if (removeFooter) {\n stylesToInject += `\n /* Footer hiding */\n footer, #footer, .fbar,\n .pdp-nav, [aria-label=\"Main menu\"], .gb_Td, .gb_L {\n display: none !important;\n }\n `;\n }\n\n // Inject base URL and styles\n finalHtml = this.injectStyles(finalHtml, {\n baseUrl: this.baseUrl,\n customCSS: stylesToInject,\n });\n\n return {\n provider: this.name,\n html: finalHtml,\n text,\n metadata: {\n isFullDocument: this.isFullDocument(finalHtml),\n headerRemoved: removeHeader,\n footerRemoved: removeFooter,\n sidebarRemoved: options?.removeSidebar || false,\n linksRemoved: options?.removeLinks || false,\n colorsInverted: options?.invertColors || false,\n },\n };\n }\n}\n","import { AIProvider, ParsedResponse, ParseOptions } from './types';\nimport { ProviderDetector } from './detector';\nimport { ChatGPTProvider } from '../providers/chatgpt';\nimport { GeminiProvider } from '../providers/gemini';\nimport { PerplexityProvider } from '../providers/perplexity';\nimport { CopilotProvider } from '../providers/copilot';\nimport { AIOverviewProvider } from '../providers/ai-overview';\nimport { AIModeProvider } from '../providers/ai-mode';\n\nexport class AIResponseParser {\n private providers: Map<AIProvider, any> = new Map();\n\n constructor() {\n this.providers.set('CHATGPT', new ChatGPTProvider());\n this.providers.set('GEMINI', new GeminiProvider());\n this.providers.set('PERPLEXITY', new PerplexityProvider());\n this.providers.set('COPILOT', new CopilotProvider());\n this.providers.set('AIOVERVIEW', new AIOverviewProvider());\n this.providers.set('AIMODE', new AIModeProvider());\n }\n\n /**\n * Parse an AI response with auto-detected provider\n */\n parse(response: any, options?: ParseOptions): ParsedResponse | null {\n const detection = ProviderDetector.detect(response);\n\n if (!detection) {\n // Try to parse as generic HTML if no provider detected\n return this.parseGeneric(response);\n }\n\n const provider = this.providers.get(detection.provider);\n if (!provider) {\n return null;\n }\n\n try {\n const parsed = provider.parse(response, options);\n return {\n ...parsed,\n metadata: {\n ...parsed.metadata,\n detectionConfidence: detection.confidence,\n },\n };\n } catch (error) {\n console.error(`Failed to parse response with ${detection.provider}:`, error);\n return null;\n }\n }\n\n /**\n * Parse with explicit provider specification\n */\n parseWithProvider(\n response: any,\n provider: AIProvider,\n options?: ParseOptions\n ): ParsedResponse | null {\n const providerInstance = this.providers.get(provider);\n if (!providerInstance) {\n throw new Error(`Unknown provider: ${provider}`);\n }\n\n try {\n return providerInstance.parse(response, options);\n } catch (error) {\n console.error(`Failed to parse response with ${provider}:`, error);\n return null;\n }\n }\n\n /**\n * Detect the provider from a response\n */\n detectProvider(response: any): AIProvider | null {\n const detection = ProviderDetector.detect(response);\n return detection?.provider || null;\n }\n\n /**\n * Get all possible providers with confidence scores\n */\n detectAllProviders(response: any): Array<{ provider: AIProvider; confidence: number }> {\n return ProviderDetector.getAllProviders(response);\n }\n\n /**\n * Parse as generic HTML when no specific provider is detected\n */\n private parseGeneric(response: any): ParsedResponse | null {\n // Extract HTML or text from response\n let html = '';\n let text = '';\n\n const checkContent = (obj: any) => {\n if (typeof obj === 'string') {\n if (obj.trim().startsWith('<') && obj.includes('>')) {\n html = obj;\n } else {\n text = obj;\n }\n } else if (typeof obj === 'object' && obj !== null) {\n if (obj.html) {\n html = obj.html;\n } else if (obj.content) {\n if (obj.content.trim().startsWith('<')) {\n html = obj.content;\n } else {\n text = obj.content;\n }\n } else if (obj.text) {\n text = obj.text;\n }\n }\n };\n\n // Check nested structure\n let dataToCheck = response;\n if (response.result) {\n dataToCheck = response.result;\n }\n\n checkContent(dataToCheck);\n\n if (!html && !text) {\n return null;\n }\n\n // Basic sanitization\n if (html) {\n html = html.replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script>/gim, '');\n html = html.replace(/<noscript[\\s\\S]*?<\\/noscript>/gi, '');\n }\n\n return {\n provider: 'CHATGPT' as AIProvider, // Default to ChatGPT for unknown\n html,\n text,\n metadata: {\n isGeneric: true,\n },\n };\n }\n\n /**\n * Get list of supported providers\n */\n getSupportedProviders(): AIProvider[] {\n return Array.from(this.providers.keys());\n }\n\n /**\n * Register a custom provider\n */\n registerProvider(provider: AIProvider, instance: any): void {\n this.providers.set(provider, instance);\n }\n}\n\n// Export a singleton instance\nexport const parser = new AIResponseParser();\n\n// Convenience functions\nexport const parseAiResponse = (response: any, options?: ParseOptions) =>\n parser.parse(response, options);\n\nexport const detectProvider = (response: any) =>\n parser.detectProvider(response);"],"names":["ProviderDetector","detect","response","scores","CHATGPT","GEMINI","PERPLEXITY","COPILOT","AIOVERVIEW","AIMODE","provider","patterns","Object","entries","this","PROVIDER_PATTERNS","pattern","_a","maxScore","detectedProvider","score","confidence","values","reduce","a","b","Math","max","getAllProviders","providers","matches","push","length","sort","validate","detected","html","result","includes","_b","aioverview","text","_c","_d","BaseProvider","injectStyles","options","styledHtml","baseUrl","replace","styles","customCSS","sanitizeHtml","sanitized","isFullDocument","test","removeLinks","wrapFragment","ChatGPTProvider","constructor","name","extractContent","dataToCheck","trim","startsWith","content","match","attributes","cleanedAttrs","removeSidebar","cleaned","removeFooter","parse","Error","finalHtml","stylesToInject","invertColors","metadata","sidebarRemoved","footerRemoved","linksRemoved","colorsInverted","GeminiProvider","sources","removeHeader","headerRemoved","PerplexityProvider","colorInversionStyles","CopilotProvider","removeCookieBanner","cookieBannerRemoved","AIOverviewProvider","AIModeProvider","AIResponseParser","Map","set","detection","parseGeneric","get","parsed","detectionConfidence","error","console","parseWithProvider","providerInstance","detectProvider","detectAllProviders","obj","isGeneric","getSupportedProviders","Array","from","keys","registerProvider","instance","parser","parseAiResponse"],"mappings":"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"}
|