Skip to content
This repository was archived by the owner on Sep 28, 2023. It is now read-only.

Commit 0303d4d

Browse files
committed
V0.6 release
This release includes the fixes from the 0.5.9 pre-release plus: - Shortcut support - A bug fix for the buttons not showing up sometimes - Several bug fixes & improvements for the user input box.
1 parent 8780f25 commit 0303d4d

File tree

14 files changed

+353
-98
lines changed

14 files changed

+353
-98
lines changed

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
# ChatGPT for Jupyter
22

3-
A browser extension to provide various helper functions in Jupyter Notebooks and Jupyter Lab, powered by ChatGPT.
3+
A browser extension to provide various helper functions in Jupyter Notebooks and Jupyter Lab, powered by ChatGPT or GPT-4.
44

5-
Primary functions, have ChatGPT:
5+
Primary functions, have ChatGPT/GPT-4:
66

77
- **Format** - Automatically add comments, docstrings, and formatting to your code cell.
88
- **Explain** - Explain the content of your code cell, ELI5 style.
99
- **Debug** - Help you debug an error message in your code cell.
1010
- **Complete** - Help you complete a code snippet in your code cell.
1111
- **Review** - Provide a code review of your code cell.
12+
- **Ask a question** - Ask ChatGPT a custom question.
13+
- **Voice command** - Ask ChatGPT a custom question through your microphone.
14+
- Note: this requires an OpenAI API key and uses the OpenAI Whisper API, which charges $0.006 per minute.
1215

13-
**Project status:** Stable - but ChatGPT changes frequently - if you run into problems or have feature suggestions --> Submit them as an issue!
16+
**Note: *Ask a question* and *Voice command* are a work-in-progress.**
17+
18+
**Project status:** Stable - but developments happen frequently - if you run into problems or have feature suggestions --> Submit them as an issue!
1419

1520
**Major acknowledgements:**
1621

17-
- [ChatGPT](https://openai.com/blog/chatgpt/) for doing all the heavy lifting.
18-
- [wong2/chat-gpt-google-extension](https://github.com/wong2/chat-gpt-google-extension) for serving as a base for the extension. The entire backend integration with ChatGPT is built by them and I use it as-is (including the readme instructions below). It is phenomenal work and I highly recommend checking it out.
22+
- [ChatGPT/GPT-4](https://openai.com/blog/chatgpt/) for doing all the heavy lifting.
23+
- [wong2/chat-gpt-google-extension](https://github.com/wong2/chat-gpt-google-extension) for serving as the original base for the extension.
1924

2025
**Caveats & Warnings:**
2126

build.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ async function runEsbuild() {
2525
bundle: true,
2626
outdir: outdir,
2727
treeShaking: true,
28-
minify: false,
28+
minify: true,
2929
legalComments: 'none',
3030
jsxFactory: 'h',
3131
jsxFragment: 'Fragment',

src/background/index.ts

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ async function generateAnswers(port: Browser.Runtime.Port, question: string) {
3030
prompt: question,
3131
signal: controller.signal,
3232
onEvent(event) {
33-
console.log("debug")
3433
if (event.type === 'done') {
3534
port.postMessage({ event: 'DONE' })
3635
return
@@ -86,7 +85,6 @@ port.onMessage.addListener(async (msg) => {
8685
if (msg.type === 'GENERATE_ANSWERS') {
8786
await generateAnswers(port, msg.question);
8887
} else if (msg.type === 'TRANSCRIBE_AUDIO') {
89-
console.log("received", msg.dataUrl)
9088
await transcribeAudio(port, msg.dataUrl);
9189
}
9290
} catch (err: any) {
@@ -115,4 +113,71 @@ Browser.runtime.onInstalled.addListener((details) => {
115113
if (details.reason === 'install') {
116114
Browser.runtime.openOptionsPage()
117115
}
118-
})
116+
})
117+
118+
Browser.commands.onCommand.addListener((command) => {
119+
if (command === 'format_code') {
120+
Browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
121+
if (tabs.length === 0 || !tabs[0].id) {
122+
return;
123+
}
124+
Browser.tabs.sendMessage(tabs[0].id, { action: 'formatCode' });
125+
});
126+
}
127+
128+
if (command === 'explain_code') {
129+
Browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
130+
if (tabs.length === 0 || !tabs[0].id) {
131+
return;
132+
}
133+
Browser.tabs.sendMessage(tabs[0].id, { action: 'explainCode' });
134+
});
135+
}
136+
137+
if (command === 'debug_code') {
138+
Browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
139+
if (tabs.length === 0 || !tabs[0].id) {
140+
return;
141+
}
142+
Browser.tabs.sendMessage(tabs[0].id, { action: 'debugCode' });
143+
});
144+
}
145+
146+
if (command === 'complete_code') {
147+
Browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
148+
if (tabs.length === 0 || !tabs[0].id) {
149+
return;
150+
}
151+
Browser.tabs.sendMessage(tabs[0].id, { action: 'completeCode' });
152+
});
153+
}
154+
155+
if (command === 'review_code') {
156+
Browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
157+
if (tabs.length === 0 || !tabs[0].id) {
158+
return;
159+
}
160+
Browser.tabs.sendMessage(tabs[0].id, { action: 'reviewCode' });
161+
});
162+
}
163+
164+
if (command === 'ask_question') {
165+
Browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
166+
if (tabs.length === 0 || !tabs[0].id) {
167+
return;
168+
}
169+
Browser.tabs.sendMessage(tabs[0].id, { action: 'askQuestion' });
170+
});
171+
}
172+
173+
if (command === 'voice_command') {
174+
Browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
175+
if (tabs.length === 0 || !tabs[0].id) {
176+
return;
177+
}
178+
Browser.tabs.sendMessage(tabs[0].id, { action: 'voiceCommand' });
179+
});
180+
}
181+
182+
});
183+

src/content-script/ChatGPTQueryBuilder.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ function BuildQuery(
3737
return ""
3838
}
3939
} else {
40+
console.log("ChatGPT Jupyter: Warning - Unknown site name")
4041
return ""
4142
}
4243

@@ -141,7 +142,7 @@ function BuildQuery(
141142
/* Third, build the query string and return it */
142143
/* -------------------------------------------------------------------------- */
143144
let query = ""
144-
if (code && code) {
145+
if (code || (type == "question" && userInput)) {
145146
if (type == "complete") {
146147
query = promptTemplates["complete"](prev_cell_text, code)
147148
} else if (type == "explain") {
@@ -156,14 +157,13 @@ function BuildQuery(
156157
if (userInput) {
157158
query = promptTemplates["question"](userInput)
158159
} else {
159-
// If no user input, then error
160160
console.log("ChatGPT Jupyter: Error - No user input provided")
161+
return ""
161162
}
162163
}
163-
164-
//console.log(query)
165164
return query
166165
} else {
166+
console.log("ChatGPT Jupyter: Error - No code found")
167167
return ""
168168
}
169169
}

src/content-script/FloatingInput.tsx

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,90 @@
1-
// FloatingInput.tsx
2-
import React, { useImperativeHandle, useRef, useState } from 'react';
1+
import React, { useImperativeHandle, useRef, useState, useEffect } from 'react';
32
import { Rnd } from 'react-rnd';
43
import { GrabberIcon, XIcon } from '@primer/octicons-react';
54

6-
import { submit_and_add_question } from './ChatGPTRender';
7-
8-
95
interface FloatingInputProps {
106
onSubmit: (inputValue: string) => void;
117
}
128

139
const FloatingInput = React.forwardRef((props: FloatingInputProps, ref) => {
14-
const textareaRef = useRef<HTMLTextAreaElement>(null);
10+
const textareaRef = useRef<HTMLTextAreaElement>(null);
1511
const [isVisible, setIsVisible] = useState(false);
1612

1713
const handleSubmit = () => {
1814
if (textareaRef.current) {
1915
props.onSubmit(textareaRef.current.value);
2016
textareaRef.current.value = '';
21-
setIsVisible(false);
17+
setIsVisible(false);
2218
}
2319
};
2420

2521
const handleClose = () => {
2622
setIsVisible(false);
2723
};
2824

29-
const handleOpen = () => { // Add this function
25+
const handleOpen = () => {
3026
setIsVisible(true);
3127
};
3228

33-
// Expose the handleOpen function to the parent component
34-
useImperativeHandle(ref, () => ({
29+
useImperativeHandle(ref, () => ({
3530
openFloatingInput: handleOpen,
36-
}));
31+
}));
3732

33+
// Focus textarea when isVisible changes to true
34+
useEffect(() => {
35+
if (isVisible && textareaRef.current) {
36+
textareaRef.current.focus();
37+
}
38+
}, [isVisible]);
39+
40+
// Add event listener for Ctrl+Enter
41+
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
42+
if (e.key === 'Enter' && e.ctrlKey) {
43+
handleSubmit();
44+
} else if (e.key === 'Escape') {
45+
handleClose();
46+
}
47+
e.stopPropagation();
48+
};
49+
3850
const windowWidth = window.innerWidth;
3951
const windowHeight = window.innerHeight;
4052
const boxWidth = 600;
4153
const boxHeight = 300;
42-
const initialX = (windowWidth - boxWidth) / 2;
43-
const initialY = (windowHeight - boxHeight) / 2;
54+
const topPercentage = (windowHeight - boxHeight) / (2 * windowHeight) * 100;
55+
const leftPercentage = (windowWidth - boxWidth) / (2 * windowWidth) * 100;
4456

4557
if (!isVisible) {
4658
return null;
4759
}
60+
4861

4962
return (
50-
<Rnd
51-
default={{
52-
x: initialX,
53-
y: initialY,
54-
width: boxWidth,
55-
height: boxHeight,
63+
<div>
64+
<div className="backdrop" onClick={handleClose}></div>
65+
<div
66+
style={{
67+
display: 'flex',
68+
justifyContent: 'center',
69+
alignItems: 'center',
70+
position: 'fixed',
71+
top: `${topPercentage}%`,
72+
left: `${leftPercentage}%`,
73+
right: 0,
74+
bottom: 0,
75+
zIndex: 9999998,
5676
}}
57-
minWidth={boxWidth/2}
58-
minHeight={boxHeight/2}
59-
dragHandleClassName="drag-handle"
77+
>
78+
<Rnd
79+
default={{
80+
x: 0,
81+
y: 0,
82+
width: boxWidth,
83+
height: boxHeight,
84+
}}
85+
minWidth={boxWidth / 2}
86+
minHeight={boxHeight / 2}
87+
dragHandleClassName='drag-handle'
6088
enableResizing={{
6189
top: true,
6290
right: true,
@@ -69,7 +97,8 @@ useImperativeHandle(ref, () => ({
6997
}}
7098
cancel="textarea, button"
7199
dragGrid={[40, 40]}
72-
>
100+
style = {{zIndex: 9999999}}
101+
>
73102
<div className="floating-input-container">
74103
<div className="top-bar">
75104
<div className="drag-handle">
@@ -85,11 +114,14 @@ useImperativeHandle(ref, () => ({
85114
ref={textareaRef}
86115
style={{ flex: 1 }}
87116
placeholder="Type your input"
117+
onKeyDown={handleKeyDown}
88118
/>
89119
</div>
90-
<button onClick={handleSubmit}>Submit</button>
120+
<button onClick={handleSubmit}>Submit (ctrl+Enter)</button>
91121
</div>
92-
</Rnd>
122+
</Rnd>
123+
</div>
124+
</div>
93125
);
94126
});
95127

src/content-script/Interface.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Icon } from '@primer/octicons-react'
22

33
interface Props {
4+
id : string,
45
name : string,
56
onClick : () => void,
67
icon : Icon,
@@ -11,7 +12,7 @@ interface Props {
1112
export function Button(props: Props) {
1213
const isNotebook = props.siteName == "notebook"
1314
return (
14-
<button className="btn btn-default btn-xs chat-gpt-button" onClick={props.onClick } disabled={props.disabled} style= {{marginTop: isNotebook ? '-0.5px' : '4px' }} title={props.name} >
15+
<button id={props.id} className="btn btn-default btn-xs chat-gpt-button" onClick={props.onClick } disabled={props.disabled} style= {{marginTop: isNotebook ? '-0.5px' : '4px' }} title={props.name} >
1516
<props.icon size='small' className="icon" />
1617
</button>
1718
)

0 commit comments

Comments
 (0)