Skip to content

Commit 709da43

Browse files
authored
feat: enable prompt template on all urls (#7)
1 parent d45390c commit 709da43

File tree

6 files changed

+108
-67
lines changed

6 files changed

+108
-67
lines changed

apps/chrome-extension/src/manifest.json

+12
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@
2222
"js": ["bardContentScript.bundle.js"],
2323
"css": ["bard.content.styles.css"],
2424
"run_at": "document_end"
25+
},
26+
{
27+
"matches": ["<all_urls>"],
28+
"exclude_matches": [
29+
"https://chat.openai.com/*",
30+
"https://bard.google.com/*"
31+
],
32+
"js": ["defaultContentScript.bundle.js"]
2533
}
2634
],
2735
"web_accessible_resources": [
@@ -36,6 +44,10 @@
3644
{
3745
"resources": ["chatgpt.content.styles.css"],
3846
"matches": ["https://chat.openai.com/*"]
47+
},
48+
{
49+
"resources": ["bard.content.styles.css"],
50+
"matches": ["https://bard.google.com/*"]
3951
}
4052
],
4153
"permissions": ["storage"]

apps/chrome-extension/src/pages/Content/base.dom.tsx

+31-54
Original file line numberDiff line numberDiff line change
@@ -7,95 +7,72 @@ import PromptsView from './PromptsView';
77

88
export abstract class BaseDom {
99
protected abstract name: string;
10-
protected abstract textAreaSelector: string;
1110

12-
templatesView!: HTMLDivElement;
13-
isTemplatesViewOpen = false;
11+
promptsView!: HTMLDivElement;
12+
isPromptsViewOpen = false;
1413

1514
constructor() {
1615
this.init = this.init.bind(this);
17-
this.handleTemplateSelected = this.handleTemplateSelected.bind(this);
18-
this.hideTemplates = this.hideTemplates.bind(this);
16+
this.handlePromptSelected = this.handlePromptSelected.bind(this);
17+
this.hidePrompts = this.hidePrompts.bind(this);
1918
}
2019

21-
protected abstract setText(text: string): void;
20+
protected abstract addCustomTrigger(): void;
21+
protected abstract usePrompt(prompt: IPrompt): void;
2222

2323
init() {
2424
console.log(`Initializing ${this.name} DOM`);
25-
this.templatesView = this.createTemplatesElement();
26-
this.addEventListeners();
25+
this.promptsView = this.createPromptsElement();
26+
this.addTriggers();
2727
}
2828

29-
protected getTextArea(): HTMLTextAreaElement {
30-
const textArea = document.querySelector<HTMLTextAreaElement>(
31-
this.textAreaSelector
32-
);
33-
34-
if (!textArea) throw new Error('Could not find text area');
35-
36-
return textArea;
37-
}
38-
39-
private handleTemplateSelected(template: IPrompt): void {
40-
this.isTemplatesViewOpen = false;
41-
this.setText(template.content);
42-
this.hideTemplates();
29+
private handlePromptSelected(prompt: IPrompt): void {
30+
this.isPromptsViewOpen = false;
31+
this.usePrompt(prompt);
32+
this.hidePrompts();
4333
}
4434

45-
private createTemplatesElement(): HTMLDivElement {
46-
const templatesView = document.createElement('div');
47-
document.body.appendChild(templatesView);
48-
return templatesView;
35+
private createPromptsElement(): HTMLDivElement {
36+
const promptsView = document.createElement('div');
37+
document.body.appendChild(promptsView);
38+
return promptsView;
4939
}
5040

5141
private async render() {
5242
ReactDOM.render(
5343
<PromptsView
5444
groupedPrompts={groupedPrompts}
55-
visible={this.isTemplatesViewOpen}
56-
onItemSelected={this.handleTemplateSelected}
57-
onCancel={this.hideTemplates}
45+
visible={this.isPromptsViewOpen}
46+
onItemSelected={this.handlePromptSelected}
47+
onCancel={this.hidePrompts}
5848
/>,
59-
this.templatesView
49+
this.promptsView
6050
);
6151
}
6252

63-
private async showTemplates() {
64-
this.isTemplatesViewOpen = true;
53+
protected async showPrompts() {
54+
this.isPromptsViewOpen = true;
6555
await this.render();
66-
this.templatesView.focus();
56+
this.promptsView.focus();
6757
}
6858

69-
private async hideTemplates() {
70-
this.isTemplatesViewOpen = false;
59+
protected async hidePrompts() {
60+
this.isPromptsViewOpen = false;
7161
await this.render();
7262
}
7363

74-
private addHotKeysEventListener() {
64+
private addHotKeysTrigger() {
7565
document.addEventListener('keydown', (event) => {
7666
if (event.key === 'Escape' || event.key === 'q') {
77-
this.hideTemplates();
67+
this.hidePrompts();
7868
} else if (event.ctrlKey && event.key === 't') {
79-
this.showTemplates();
80-
}
81-
});
82-
}
83-
84-
private addTextAreaEventListener() {
85-
this.getTextArea().addEventListener('input', (event) => {
86-
const input = event.target as HTMLTextAreaElement;
87-
const text = input.value;
88-
89-
if (text === '/templates') {
90-
this.showTemplates();
91-
} else if (this.isTemplatesViewOpen) {
92-
this.hideTemplates();
69+
this.showPrompts();
9370
}
9471
});
9572
}
9673

97-
private addEventListeners() {
98-
this.addHotKeysEventListener();
99-
this.addTextAreaEventListener();
74+
private addTriggers() {
75+
this.addHotKeysTrigger();
76+
this.addCustomTrigger();
10077
}
10178
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { IPrompt } from '@rpidanny/llm-prompt-templates';
2+
import { message } from 'antd';
3+
4+
import { BaseDom } from './base.dom';
5+
6+
export class GenericDom extends BaseDom {
7+
protected name = 'Generic';
8+
9+
// eslint-disable-next-line @typescript-eslint/no-empty-function
10+
protected addCustomTrigger() {}
11+
12+
protected usePrompt(prompt: IPrompt) {
13+
const clipboardItem = new ClipboardItem({
14+
'text/plain': new Blob([prompt.content], { type: 'text/plain' }),
15+
});
16+
navigator.clipboard.write([clipboardItem]);
17+
18+
message.info(`${prompt.name} prompt copied to clipboard`);
19+
}
20+
}
21+
22+
function init() {
23+
const genericDom = new GenericDom();
24+
25+
setTimeout(genericDom.init, 1000);
26+
}
27+
28+
init();
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
1-
import { BaseDom } from '../../base.dom';
1+
import { ChatGPTDom } from '../chatgpt/chatgpt.dom';
22

3-
export class BardDom extends BaseDom {
3+
export class BardDom extends ChatGPTDom {
44
protected name = 'Bard';
55
protected textAreaSelector = 'div > textarea';
6-
7-
protected setText(text: string) {
8-
const textArea = this.getTextArea();
9-
console.log('Setting text', text);
10-
textArea.focus();
11-
textArea.value = text;
12-
textArea.style.height = textArea.scrollHeight + 'px';
13-
}
146
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,39 @@
1+
import { IPrompt } from '@rpidanny/llm-prompt-templates';
2+
13
import { BaseDom } from '../../base.dom';
24

35
export class ChatGPTDom extends BaseDom {
46
protected name = 'ChatGPT';
57
protected textAreaSelector = 'div.relative > textarea';
68

7-
protected setText(text: string) {
9+
private getTextArea(): HTMLTextAreaElement {
10+
const textArea = document.querySelector<HTMLTextAreaElement>(
11+
this.textAreaSelector
12+
);
13+
14+
if (!textArea) throw new Error('Could not find text area');
15+
16+
return textArea;
17+
}
18+
19+
protected addCustomTrigger() {
20+
this.getTextArea().addEventListener('input', (event) => {
21+
const input = event.target as HTMLTextAreaElement;
22+
const text = input.value;
23+
24+
if (text === '/templates') {
25+
this.showPrompts();
26+
} else if (this.isPromptsViewOpen) {
27+
this.hidePrompts();
28+
}
29+
});
30+
}
31+
32+
protected usePrompt(prompt: IPrompt) {
833
const textArea = this.getTextArea();
9-
console.log('Setting text', text);
34+
console.log('Setting text', prompt.content);
1035
textArea.focus();
11-
textArea.value = text;
36+
textArea.value = prompt.content;
1237
textArea.style.height = textArea.scrollHeight + 'px';
1338
}
1439
}

apps/chrome-extension/webpack.config.js

+7
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
5555
'bard',
5656
'index.ts'
5757
),
58+
defaultContentScript: path.join(
59+
config.context,
60+
'src',
61+
'pages',
62+
'Content',
63+
'generic.dom.ts'
64+
),
5865
},
5966
output: {
6067
filename: '[name].bundle.js',

0 commit comments

Comments
 (0)