From 709da431f01e66c5746ded35204101259af9a586 Mon Sep 17 00:00:00 2001 From: Abhishek Maharjan Date: Fri, 12 May 2023 00:20:35 +0200 Subject: [PATCH] feat: enable prompt template on all urls (#7) --- apps/chrome-extension/src/manifest.json | 12 +++ .../src/pages/Content/base.dom.tsx | 85 +++++++------------ .../src/pages/Content/generic.dom.ts | 28 ++++++ .../src/pages/Content/llms/bard/bard.dom.ts | 12 +-- .../pages/Content/llms/chatgpt/chatgpt.dom.ts | 31 ++++++- apps/chrome-extension/webpack.config.js | 7 ++ 6 files changed, 108 insertions(+), 67 deletions(-) create mode 100644 apps/chrome-extension/src/pages/Content/generic.dom.ts diff --git a/apps/chrome-extension/src/manifest.json b/apps/chrome-extension/src/manifest.json index 3bc8da5..bd6c1fb 100755 --- a/apps/chrome-extension/src/manifest.json +++ b/apps/chrome-extension/src/manifest.json @@ -22,6 +22,14 @@ "js": ["bardContentScript.bundle.js"], "css": ["bard.content.styles.css"], "run_at": "document_end" + }, + { + "matches": [""], + "exclude_matches": [ + "https://chat.openai.com/*", + "https://bard.google.com/*" + ], + "js": ["defaultContentScript.bundle.js"] } ], "web_accessible_resources": [ @@ -36,6 +44,10 @@ { "resources": ["chatgpt.content.styles.css"], "matches": ["https://chat.openai.com/*"] + }, + { + "resources": ["bard.content.styles.css"], + "matches": ["https://bard.google.com/*"] } ], "permissions": ["storage"] diff --git a/apps/chrome-extension/src/pages/Content/base.dom.tsx b/apps/chrome-extension/src/pages/Content/base.dom.tsx index 2fc40af..b8be0e1 100644 --- a/apps/chrome-extension/src/pages/Content/base.dom.tsx +++ b/apps/chrome-extension/src/pages/Content/base.dom.tsx @@ -7,95 +7,72 @@ import PromptsView from './PromptsView'; export abstract class BaseDom { protected abstract name: string; - protected abstract textAreaSelector: string; - templatesView!: HTMLDivElement; - isTemplatesViewOpen = false; + promptsView!: HTMLDivElement; + isPromptsViewOpen = false; constructor() { this.init = this.init.bind(this); - this.handleTemplateSelected = this.handleTemplateSelected.bind(this); - this.hideTemplates = this.hideTemplates.bind(this); + this.handlePromptSelected = this.handlePromptSelected.bind(this); + this.hidePrompts = this.hidePrompts.bind(this); } - protected abstract setText(text: string): void; + protected abstract addCustomTrigger(): void; + protected abstract usePrompt(prompt: IPrompt): void; init() { console.log(`Initializing ${this.name} DOM`); - this.templatesView = this.createTemplatesElement(); - this.addEventListeners(); + this.promptsView = this.createPromptsElement(); + this.addTriggers(); } - protected getTextArea(): HTMLTextAreaElement { - const textArea = document.querySelector( - this.textAreaSelector - ); - - if (!textArea) throw new Error('Could not find text area'); - - return textArea; - } - - private handleTemplateSelected(template: IPrompt): void { - this.isTemplatesViewOpen = false; - this.setText(template.content); - this.hideTemplates(); + private handlePromptSelected(prompt: IPrompt): void { + this.isPromptsViewOpen = false; + this.usePrompt(prompt); + this.hidePrompts(); } - private createTemplatesElement(): HTMLDivElement { - const templatesView = document.createElement('div'); - document.body.appendChild(templatesView); - return templatesView; + private createPromptsElement(): HTMLDivElement { + const promptsView = document.createElement('div'); + document.body.appendChild(promptsView); + return promptsView; } private async render() { ReactDOM.render( , - this.templatesView + this.promptsView ); } - private async showTemplates() { - this.isTemplatesViewOpen = true; + protected async showPrompts() { + this.isPromptsViewOpen = true; await this.render(); - this.templatesView.focus(); + this.promptsView.focus(); } - private async hideTemplates() { - this.isTemplatesViewOpen = false; + protected async hidePrompts() { + this.isPromptsViewOpen = false; await this.render(); } - private addHotKeysEventListener() { + private addHotKeysTrigger() { document.addEventListener('keydown', (event) => { if (event.key === 'Escape' || event.key === 'q') { - this.hideTemplates(); + this.hidePrompts(); } else if (event.ctrlKey && event.key === 't') { - this.showTemplates(); - } - }); - } - - private addTextAreaEventListener() { - this.getTextArea().addEventListener('input', (event) => { - const input = event.target as HTMLTextAreaElement; - const text = input.value; - - if (text === '/templates') { - this.showTemplates(); - } else if (this.isTemplatesViewOpen) { - this.hideTemplates(); + this.showPrompts(); } }); } - private addEventListeners() { - this.addHotKeysEventListener(); - this.addTextAreaEventListener(); + private addTriggers() { + this.addHotKeysTrigger(); + this.addCustomTrigger(); } } diff --git a/apps/chrome-extension/src/pages/Content/generic.dom.ts b/apps/chrome-extension/src/pages/Content/generic.dom.ts new file mode 100644 index 0000000..8769e06 --- /dev/null +++ b/apps/chrome-extension/src/pages/Content/generic.dom.ts @@ -0,0 +1,28 @@ +import { IPrompt } from '@rpidanny/llm-prompt-templates'; +import { message } from 'antd'; + +import { BaseDom } from './base.dom'; + +export class GenericDom extends BaseDom { + protected name = 'Generic'; + + // eslint-disable-next-line @typescript-eslint/no-empty-function + protected addCustomTrigger() {} + + protected usePrompt(prompt: IPrompt) { + const clipboardItem = new ClipboardItem({ + 'text/plain': new Blob([prompt.content], { type: 'text/plain' }), + }); + navigator.clipboard.write([clipboardItem]); + + message.info(`${prompt.name} prompt copied to clipboard`); + } +} + +function init() { + const genericDom = new GenericDom(); + + setTimeout(genericDom.init, 1000); +} + +init(); diff --git a/apps/chrome-extension/src/pages/Content/llms/bard/bard.dom.ts b/apps/chrome-extension/src/pages/Content/llms/bard/bard.dom.ts index 1dfbe3a..8df307c 100644 --- a/apps/chrome-extension/src/pages/Content/llms/bard/bard.dom.ts +++ b/apps/chrome-extension/src/pages/Content/llms/bard/bard.dom.ts @@ -1,14 +1,6 @@ -import { BaseDom } from '../../base.dom'; +import { ChatGPTDom } from '../chatgpt/chatgpt.dom'; -export class BardDom extends BaseDom { +export class BardDom extends ChatGPTDom { protected name = 'Bard'; protected textAreaSelector = 'div > textarea'; - - protected setText(text: string) { - const textArea = this.getTextArea(); - console.log('Setting text', text); - textArea.focus(); - textArea.value = text; - textArea.style.height = textArea.scrollHeight + 'px'; - } } diff --git a/apps/chrome-extension/src/pages/Content/llms/chatgpt/chatgpt.dom.ts b/apps/chrome-extension/src/pages/Content/llms/chatgpt/chatgpt.dom.ts index 7e2d816..a6419c2 100644 --- a/apps/chrome-extension/src/pages/Content/llms/chatgpt/chatgpt.dom.ts +++ b/apps/chrome-extension/src/pages/Content/llms/chatgpt/chatgpt.dom.ts @@ -1,14 +1,39 @@ +import { IPrompt } from '@rpidanny/llm-prompt-templates'; + import { BaseDom } from '../../base.dom'; export class ChatGPTDom extends BaseDom { protected name = 'ChatGPT'; protected textAreaSelector = 'div.relative > textarea'; - protected setText(text: string) { + private getTextArea(): HTMLTextAreaElement { + const textArea = document.querySelector( + this.textAreaSelector + ); + + if (!textArea) throw new Error('Could not find text area'); + + return textArea; + } + + protected addCustomTrigger() { + this.getTextArea().addEventListener('input', (event) => { + const input = event.target as HTMLTextAreaElement; + const text = input.value; + + if (text === '/templates') { + this.showPrompts(); + } else if (this.isPromptsViewOpen) { + this.hidePrompts(); + } + }); + } + + protected usePrompt(prompt: IPrompt) { const textArea = this.getTextArea(); - console.log('Setting text', text); + console.log('Setting text', prompt.content); textArea.focus(); - textArea.value = text; + textArea.value = prompt.content; textArea.style.height = textArea.scrollHeight + 'px'; } } diff --git a/apps/chrome-extension/webpack.config.js b/apps/chrome-extension/webpack.config.js index 4f0012a..a4fcd4e 100644 --- a/apps/chrome-extension/webpack.config.js +++ b/apps/chrome-extension/webpack.config.js @@ -55,6 +55,13 @@ module.exports = composePlugins(withNx(), withReact(), (config) => { 'bard', 'index.ts' ), + defaultContentScript: path.join( + config.context, + 'src', + 'pages', + 'Content', + 'generic.dom.ts' + ), }, output: { filename: '[name].bundle.js',