├── .gitignore ├── LICENSE ├── README.md ├── background.js ├── chats ├── ChatGPT-Completing_insertSuggestion_Function.md ├── ChatGPT-Jupyter_data_capture_extension.md └── ChatGPT-Managing_Chrome_Extension_Requests.md ├── examples ├── denoisingwav_2_wavelet_2d.ipynb ├── fixing_bug_example.ipynb ├── sklearn_digits.ipynb └── starcoder_gpu_usage.ipynb ├── icons ├── flow.png ├── flow_128.png ├── flow_256.png └── flow_64.png ├── manifest.json ├── options ├── options.html ├── options.js └── styles.css └── src ├── animation.js ├── api.js ├── content.js ├── diff.js ├── preferences.js ├── state.js └── utility.js /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jupyter Coder 2 | Jupyter Coder is a jupyter plugin based on [Starcoder](https://github.com/bigcode-project/starcoder) 3 | Starcoder has its unique capacity to leverage the jupyter notebook structure to produce code under instruction. This plugin enable you to use starcoder in your notebook. 4 | 5 | ## Key features 6 | ### code completition 7 | In a cell, press "ctrl + space" to trigger 8 | ![image](https://github.com/bigcode-project/jupytercoder/assets/6381544/14331c44-59b9-4a86-9f75-1238f3da4854) 9 | 10 | Press "ctrl" to accpet the proposition. 11 | 12 | ### Bug fix 13 | In a cell with error message, press "ctrl + `" to trigger the bug fix mode 14 | ![image](https://github.com/bigcode-project/jupytercoder/assets/6381544/f2c630b7-c767-40a9-96de-78b766a29a93) 15 | 16 | Press ctrl to accept 17 | 18 | ## Install the chrome extension 19 | https://chrome.google.com/webstore/detail/jupyter-coder/kbedojkmlknhepcaggkdimefcbiifkjf 20 | 21 | ## Installation from github (skip if you install from chrome store) 22 | ```bash 23 | git clone https://github.com/bigcode-project/jupytercoder.git 24 | ``` 25 | 26 | Open chrome://extensions/ in your browser and enable developer mode. 27 | ![devmode](https://user-images.githubusercontent.com/6381544/236060575-1fbb4024-165d-491f-8ae6-450c50b7a66d.png) 28 | 29 | Then click on "Load unpacked" and select the folder where you cloned this repository. 30 | ![unpack](https://user-images.githubusercontent.com/6381544/236060695-c432a612-bfeb-4708-909a-f1bb8a64f732.png) 31 | 32 | ## Add API key 33 | Then click on the extension icon to enable it, you will need to enter the bigcode model endpoint url https://api-inference.huggingface.co/models/bigcode/starcoderbase/ and your hf readonlys api key. Click save to activate this mode. 34 | ![image](https://user-images.githubusercontent.com/6381544/236060887-5c7fd3b1-d5f7-4b86-8282-8925ed867825.png) 35 | 36 | ## Demo 37 | ```bash 38 | cd examples 39 | jupyter notebook 40 | ``` 41 | 42 | In case you encounter issue, you might want to install the same jupyter notebook version 43 | ```bash 44 | jupyter notebook --version 45 | >>> 6.5.2 46 | ``` 47 | 48 | Open the notebook sklearn_digits.ipynb and try it out. 49 | Type ctrl+space to trigger the code completion when you are in a code cell, then ctrl to accept it (welcome your thought on the key binding!). 50 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { 2 | if (request.type === "getOpenAIKey") { 3 | chrome.storage.sync.get("openAIKey", (data) => { 4 | sendResponse({ openAIKey: data.openAIKey }); 5 | }); 6 | return true; // Required to use sendResponse asynchronously 7 | } 8 | else if (request.type === "getBigcodeServiceUrl"){ 9 | chrome.storage.sync.get("bigcodeServiceUrl", (data) => { 10 | sendResponse({ bigcodeServiceUrl: data.bigcodeServiceUrl }); 11 | }); 12 | return true; // Required to use sendResponse asynchronously 13 | } 14 | else if(request.type === "getCheckedMode"){ 15 | chrome.storage.sync.get("checkedMode", (data) => { 16 | sendResponse({ checkedMode: data.checkedMode }); 17 | }); 18 | return true; // Required to use sendResponse asynchronously 19 | } 20 | else if(request.type === "getGPTModelType"){ 21 | chrome.storage.sync.get("modelType", (data) => { 22 | sendResponse({ modelType: data.modelType }); 23 | }); 24 | return true; // Required to use sendResponse asynchronously 25 | } 26 | else if(request.type === "getHuggingfaceAccessToken"){ 27 | chrome.storage.sync.get("huggingfaceAccessToken", (data) => { 28 | sendResponse({ huggingfaceAccessToken : data.huggingfaceAccessToken}); 29 | }) 30 | return true; 31 | } 32 | }); 33 | -------------------------------------------------------------------------------- /chats/ChatGPT-Completing_insertSuggestion_Function.md: -------------------------------------------------------------------------------- 1 | # Completing insertSuggestion Function 2 | 3 | #### You: 4 | I am trying to insert suggestion into a jupyter notebook. Here is the js code 5 | // Check if the current page is a Jupyter Notebook 6 | if (document.querySelector('body.notebook_app')) { 7 | document.addEventListener('keydown', (event) => { 8 | // Check if the Ctrl + Space keys were pressed 9 | if (event.ctrlKey && event.code === 'Space') { 10 | event.preventDefault(); 11 | 12 | // Find the active cell in the Jupyter Notebook 13 | const activeCell = document.querySelector('.cell.selected .input_area .CodeMirror'); 14 | if (activeCell) { 15 | // Retrieve the content of the active cell 16 | const cellContent = getCellContent(activeCell); 17 | 18 | // Log the cell content to the console 19 | console.log("[CELL CONTENT]", cellContent); 20 | console.log(activeCell); 21 | 22 | // Suggestion handling 23 | const suggestion = 'your_suggestion_here'; // Replace with your actual suggestion 24 | insertSuggestion(activeCell, suggestion); 25 | } 26 | } 27 | 28 | }); 29 | 30 | function getCellContent(cell) { 31 | const codeMirrorLines = cell.querySelectorAll('.CodeMirror-code pre'); 32 | const content = []; 33 | 34 | codeMirrorLines.forEach((line) => { 35 | content.push(line.textContent); 36 | }); 37 | 38 | return content.join('\n'); 39 | } 40 | } 41 | 42 | function insertSuggestion(cell, suggestion) { 43 | 44 | } 45 | please complete the insertSuggestion function 46 | 47 | #### ChatGPT: 48 | To complete the `insertSuggestion` function, you need to access the CodeMirror instance associated with the given cell and then insert the suggestion at the current cursor position. Here's the modified code: 49 | 50 | ```javascript 51 | // Check if the current page is a Jupyter Notebook 52 | if (document.querySelector('body.notebook_app')) { 53 | document.addEventListener('keydown', (event) => { 54 | // Check if the Ctrl + Space keys were pressed 55 | if (event.ctrlKey && event.code === 'Space') { 56 | event.preventDefault(); 57 | 58 | // Find the active cell in the Jupyter Notebook 59 | const activeCell = document.querySelector('.cell.selected .input_area .CodeMirror'); 60 | if (activeCell) { 61 | // Retrieve the content of the active cell 62 | const cellContent = getCellContent(activeCell); 63 | 64 | // Log the cell content to the console 65 | console.log("[CELL CONTENT]", cellContent); 66 | console.log(activeCell); 67 | 68 | // Suggestion handling 69 | const suggestion = 'your_suggestion_here'; // Replace with your actual suggestion 70 | insertSuggestion(activeCell, suggestion); 71 | } 72 | } 73 | 74 | }); 75 | 76 | function getCellContent(cell) { 77 | const codeMirrorLines = cell.querySelectorAll('.CodeMirror-code pre'); 78 | const content = []; 79 | 80 | codeMirrorLines.forEach((line) => { 81 | content.push(line.textContent); 82 | }); 83 | 84 | return content.join('\n'); 85 | } 86 | } 87 | 88 | function insertSuggestion(cell, suggestion) { 89 | // Get the CodeMirror instance from the cell element 90 | const codeMirrorInstance = cell.CodeMirror; 91 | 92 | // Get the current cursor position 93 | const cursorPosition = codeMirrorInstance.getCursor(); 94 | 95 | // Insert the suggestion at the cursor position 96 | codeMirrorInstance.replaceRange(suggestion, cursorPosition); 97 | 98 | // Refresh the CodeMirror instance 99 | codeMirrorInstance.refresh(); 100 | } 101 | ``` 102 | 103 | Now, when you press Ctrl + Space, the `insertSuggestion` function will insert the specified suggestion at the current cursor position in the active cell of the Jupyter Notebook. 104 | 105 | 106 | #### You: 107 | I do not believe we have access to getCursor. Why do you think that ? 108 | 109 | #### ChatGPT: 110 | I apologize for the confusion. I mistakenly assumed that you have access to CodeMirror's API directly. Since you are working with the DOM, we need to find an alternative way to insert the suggestion at the cursor position. Here's the modified `insertSuggestion` function that should work in this case: 111 | 112 | ```javascript 113 | function insertSuggestion(cell, suggestion) { 114 | // Get the line element with the cursor 115 | const cursorLine = cell.querySelector('.CodeMirror-cursor').parentNode; 116 | 117 | // Get the index of the cursor within the line element 118 | const cursorIndex = Array.from(cursorLine.childNodes).findIndex( 119 | (child) => child.classList && child.classList.contains('CodeMirror-cursor') 120 | ); 121 | 122 | // Create a new text node with the suggestion 123 | const suggestionNode = document.createTextNode(suggestion); 124 | 125 | // Insert the suggestion node at the cursor index 126 | if (cursorIndex >= 0) { 127 | cursorLine.insertBefore(suggestionNode, cursorLine.childNodes[cursorIndex]); 128 | } else { 129 | cursorLine.appendChild(suggestionNode); 130 | } 131 | 132 | // Trigger a change event on the CodeMirror instance to update its internal state 133 | const event = new Event('change', { bubbles: true, cancelable: true }); 134 | cell.dispatchEvent(event); 135 | } 136 | ``` 137 | 138 | This modified function should insert the suggestion at the cursor position without using the CodeMirror API. However, this approach is less robust than using the API directly, and it might not work correctly in all situations. It would be better to use the CodeMirror API if possible. To do that, you can try to access the `Jupyter.notebook` object and find the CodeMirror instance from there. 139 | 140 | Here's an example of how to access the CodeMirror instance from the `Jupyter.notebook` object: 141 | 142 | ```javascript 143 | function insertSuggestion(cell, suggestion) { 144 | // Get the active cell in the Jupyter Notebook 145 | const activeCell = Jupyter.notebook.get_selected_cell(); 146 | 147 | // Check if the cell type is 'code' 148 | if (activeCell.cell_type === 'code') { 149 | // Get the CodeMirror instance from the active cell 150 | const codeMirrorInstance = activeCell.code_mirror; 151 | 152 | // Get the current cursor position 153 | const cursorPosition = codeMirrorInstance.getCursor(); 154 | 155 | // Insert the suggestion at the cursor position 156 | codeMirrorInstance.replaceRange(suggestion, cursorPosition); 157 | 158 | // Refresh the CodeMirror instance 159 | codeMirrorInstance.refresh(); 160 | } 161 | } 162 | ``` 163 | 164 | This approach requires you to have access to the `Jupyter.notebook` object and is more robust than working with the DOM directly. 165 | 166 | 167 | #### You: 168 |
169 | This is the div with textarea. Can we simulate the user behavior to insert suggestion 170 | 171 | #### ChatGPT: 172 | Given the structure of the div containing the textarea, you can simulate user behavior by inserting the suggestion text directly into the textarea. Here's an updated `insertSuggestion` function that inserts the suggestion into the textarea: 173 | 174 | ```javascript 175 | function insertSuggestion(cell, suggestion) { 176 | // Get the textarea inside the div 177 | const textarea = cell.querySelector('textarea'); 178 | 179 | if (textarea) { 180 | // Get the current cursor position 181 | const cursorPosition = textarea.selectionStart; 182 | 183 | // Insert the suggestion at the cursor position 184 | const newValue = textarea.value.slice(0, cursorPosition) + suggestion + textarea.value.slice(cursorPosition); 185 | textarea.value = newValue; 186 | 187 | // Update the cursor position after inserting the suggestion 188 | textarea.selectionStart = textarea.selectionEnd = cursorPosition + suggestion.length; 189 | 190 | // Trigger a change event on the textarea to update the CodeMirror instance 191 | const event = new Event('input', { bubbles: true, cancelable: true }); 192 | textarea.dispatchEvent(event); 193 | } 194 | } 195 | ``` 196 | 197 | This function should work for the provided div structure. It inserts the suggestion text at the current cursor position in the textarea and updates the cursor position accordingly. Finally, it dispatches an 'input' event to notify the CodeMirror instance of the change. 198 | 199 | 200 | #### You: 201 | Here is the updated script 202 | // Check if the current page is a Jupyter Notebook 203 | if (document.querySelector('body.notebook_app')) { 204 | document.addEventListener('keydown', (event) => { 205 | // Check if the Ctrl + Space keys were pressed 206 | if (event.ctrlKey && event.code === 'Space') { 207 | event.preventDefault(); 208 | 209 | // Find the active cell in the Jupyter Notebook 210 | const activeCell = document.querySelector('.cell.selected .input_area .CodeMirror'); 211 | if (activeCell) { 212 | // Retrieve the content of the active cell 213 | const cellContent = getCellContent(activeCell); 214 | const contextContent = getPreviousCellsContent(...); 215 | // Log the cell content to the console 216 | console.log(contextContent); 217 | 218 | // Suggestion handling 219 | const suggestion = 'your_suggestion_here'; // Replace with your actual suggestion 220 | insertSuggestion(activeCell, suggestion); 221 | } 222 | } 223 | }); 224 | 225 | function getCellContent(cell) { 226 | const codeMirrorLines = cell.querySelectorAll('.CodeMirror-code pre'); 227 | const content = []; 228 | 229 | codeMirrorLines.forEach((line) => { 230 | content.push(line.textContent); 231 | }); 232 | 233 | return content.join('\n'); 234 | } 235 | } 236 | 237 | function insertSuggestion(cell, suggestion) { 238 | // Get the textarea inside the div 239 | const textarea = cell.querySelector('textarea'); 240 | 241 | if (textarea) { 242 | // Get the current cursor position 243 | const cursorPosition = textarea.selectionStart; 244 | 245 | // Insert the suggestion at the cursor position 246 | const newValue = textarea.value.slice(0, cursorPosition) + suggestion + textarea.value.slice(cursorPosition); 247 | textarea.value = newValue; 248 | 249 | // Update the cursor position after inserting the suggestion 250 | textarea.selectionStart = textarea.selectionEnd = cursorPosition + suggestion.length; 251 | 252 | // Trigger a change event on the textarea to update the CodeMirror instance 253 | const event = new Event('input', { bubbles: true, cancelable: true }); 254 | textarea.dispatchEvent(event); 255 | } 256 | } 257 | now write the getPreviousCellsContent function 258 | 259 | #### ChatGPT: 260 | The `getPreviousCellsContent` function should collect the content of all code cells before the active cell in the Jupyter Notebook. Here's the function: 261 | 262 | ```javascript 263 | function getPreviousCellsContent() { 264 | const activeCell = document.querySelector('.cell.selected .input_area .CodeMirror'); 265 | const allCells = document.querySelectorAll('.cell .input_area .CodeMirror'); 266 | const previousCellsContent = []; 267 | 268 | for (const cell of allCells) { 269 | // Stop when the active cell is reached 270 | if (cell === activeCell) break; 271 | 272 | // Check if the cell is a code cell 273 | const isCodeCell = cell.closest('.cell').classList.contains('code_cell'); 274 | if (isCodeCell) { 275 | const cellContent = getCellContent(cell); 276 | previousCellsContent.push(cellContent); 277 | } 278 | } 279 | 280 | return previousCellsContent.join('\n'); 281 | } 282 | ``` 283 | 284 | This function first selects the active cell and all cells in the notebook. Then, it iterates through all cells and checks if each cell is a code cell. If the cell is a code cell, it collects its content and adds it to the `previousCellsContent` array. The iteration stops when the active cell is reached. The function returns the content of all previous code cells as a single string, joined by newlines. 285 | 286 | 287 | #### You: 288 | change it to get only the three previous cells 289 | 290 | #### ChatGPT: 291 | To modify the `getPreviousCellsContent` function to get only the three previous cells, you can use a counter variable to track how many code cells have been processed. Here's the updated function: 292 | 293 | ```javascript 294 | function getPreviousCellsContent() { 295 | const activeCell = document.querySelector('.cell.selected .input_area .CodeMirror'); 296 | const allCells = document.querySelectorAll('.cell .input_area .CodeMirror'); 297 | const previousCellsContent = []; 298 | let codeCellCount = 0; 299 | 300 | for (const cell of allCells) { 301 | // Stop when the active cell is reached or when three code cells have been processed 302 | if (cell === activeCell || codeCellCount >= 3) break; 303 | 304 | // Check if the cell is a code cell 305 | const isCodeCell = cell.closest('.cell').classList.contains('code_cell'); 306 | if (isCodeCell) { 307 | const cellContent = getCellContent(cell); 308 | previousCellsContent.unshift(cellContent); // Add the content to the beginning of the array 309 | codeCellCount++; 310 | } 311 | } 312 | 313 | // If there are more than three code cells, get only the last three 314 | const lastThreeCellsContent = previousCellsContent.slice(-3); 315 | 316 | return lastThreeCellsContent.join('\n'); 317 | } 318 | ``` 319 | 320 | This function now uses the `codeCellCount` variable to keep track of how many code cells have been processed. It stops when the active cell is reached or when three code cells have been processed. The cell content is added to the beginning of the `previousCellsContent` array using the `unshift()` method. Finally, if there are more than three code cells, the function returns only the content of the last three code cells. 321 | 322 | 323 | #### You: 324 | It is giving me the first three cells 325 | 326 | #### ChatGPT: 327 | Apologies for the confusion. I made a mistake in the previous implementation. To get the content of the last three cells before the active cell, you can iterate through the cells in reverse order. Here's the corrected `getPreviousCellsContent` function: 328 | 329 | ```javascript 330 | function getPreviousCellsContent() { 331 | const activeCell = document.querySelector('.cell.selected .input_area .CodeMirror'); 332 | const allCells = Array.from(document.querySelectorAll('.cell .input_area .CodeMirror')).reverse(); 333 | const previousCellsContent = []; 334 | let codeCellCount = 0; 335 | 336 | for (const cell of allCells) { 337 | // Stop when the active cell is reached or when three code cells have been processed 338 | if (cell === activeCell || codeCellCount >= 3) break; 339 | 340 | // Check if the cell is a code cell 341 | const isCodeCell = cell.closest('.cell').classList.contains('code_cell'); 342 | if (isCodeCell) { 343 | const cellContent = getCellContent(cell); 344 | previousCellsContent.push(cellContent); // Add the content to the end of the array 345 | codeCellCount++; 346 | } 347 | } 348 | 349 | // Reverse the array to have the content in the correct order 350 | const lastThreeCellsContent = previousCellsContent.reverse(); 351 | 352 | return lastThreeCellsContent.join('\n'); 353 | } 354 | ``` 355 | 356 | In this updated implementation, I've converted the `NodeList` returned by `querySelectorAll` into an array using `Array.from()` and reversed the order of the cells using the `reverse()` method. This way, the loop iterates through the cells in reverse order. After collecting the content of the last three code cells, the `previousCellsContent` array is reversed again to have the content in the correct order. 357 | -------------------------------------------------------------------------------- /chats/ChatGPT-Jupyter_data_capture_extension.md: -------------------------------------------------------------------------------- 1 | # Jupyter data capture extension. 2 | 3 | #### You: 4 | Write a chrome extension to catch data in a jupyter notebook: 5 | - each time user hit ctrl + space, the extension catch the content of the current cell, then post the content to a remote server 6 | 7 | #### ChatGPT: 8 | To create a Chrome extension that captures data from a Jupyter Notebook cell when the user presses Ctrl + Space, you'll need to follow these steps: 9 | 10 | 1. Set up the extension's structure 11 | 2. Create a content script that listens for key events in Jupyter Notebook cells 12 | 3. Send the captured data to a remote server using an XMLHttpRequest 13 | 14 | Here's the structure and code for the Chrome extension: 15 | 16 | 1. Extension structure: 17 | 18 | ``` 19 | jupyter-data-capture/ 20 | │ 21 | ├── manifest.json 22 | └── content.js 23 | ``` 24 | 25 | 2. Create a `manifest.json` file with the following content: 26 | 27 | ```json 28 | { 29 | "manifest_version": 3, 30 | "name": "Jupyter Data Capture", 31 | "version": "1.0", 32 | "description": "Capture data in Jupyter Notebook cells and send to a remote server.", 33 | "permissions": [ 34 | "activeTab", 35 | "https://your-remote-server-url.com/" 36 | ], 37 | "content_scripts": [ 38 | { 39 | "matches": ["*://*/*"], 40 | "js": ["content.js"], 41 | "run_at": "document_end" 42 | } 43 | ] 44 | } 45 | ``` 46 | 47 | Make sure to replace `your-remote-server-url.com` with the actual URL of your remote server. 48 | 49 | 3. Create a `content.js` file with the following content: 50 | 51 | ```javascript 52 | // Check if the current page is a Jupyter Notebook 53 | if (document.querySelector('body.notebook_app')) { 54 | // 55 | ``` 56 | 57 | 58 | #### You: 59 | write the content.js 60 | 61 | #### ChatGPT: 62 | Here's the `content.js` file for the Chrome extension: 63 | 64 | ```javascript 65 | // Check if the current page is a Jupyter Notebook 66 | if (document.querySelector('body.notebook_app')) { 67 | document.addEventListener('keydown', (event) => { 68 | // Check if the Ctrl + Space keys were pressed 69 | if (event.ctrlKey && event.code === 'Space') { 70 | event.preventDefault(); 71 | 72 | // Find the active cell in the Jupyter Notebook 73 | const activeCell = document.querySelector('.cell.selected .CodeMirror'); 74 | if (activeCell) { 75 | // Retrieve the content of the active cell 76 | const cellContent = activeCell.CodeMirror.getValue(); 77 | 78 | // Send the cell content to the remote server 79 | sendToServer(cellContent); 80 | } 81 | } 82 | }); 83 | } 84 | 85 | function sendToServer(cellContent) { 86 | const xhr = new XMLHttpRequest(); 87 | const serverURL = 'https://your-remote-server-url.com/endpoint'; 88 | 89 | // Configure the request 90 | xhr.open('POST', serverURL, true); 91 | xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); 92 | 93 | // Set up the request's callback 94 | xhr.onreadystatechange = function () { 95 | if (xhr.readyState === 4 && xhr.status === 200) { 96 | console.log('Data sent successfully'); 97 | } else if (xhr.readyState === 4) { 98 | console.error('Error sending data to the server'); 99 | } 100 | }; 101 | 102 | // Send the request 103 | const data = JSON.stringify({ cellContent: cellContent }); 104 | xhr.send(data); 105 | } 106 | ``` 107 | 108 | Make sure to replace `your-remote-server-url.com/endpoint` with the actual URL and endpoint of 109 | 110 | 111 | #### You: 112 | for now just do a console.log instead of actually sending the data to server 113 | 114 | #### ChatGPT: 115 | Here's the updated `content.js` file that logs the cell content to the console instead of sending it to a server: 116 | 117 | ```javascript 118 | // Check if the current page is a Jupyter Notebook 119 | if (document.querySelector('body.notebook_app')) { 120 | document.addEventListener('keydown', (event) => { 121 | // Check if the Ctrl + Space keys were pressed 122 | if (event.ctrlKey && event.code === 'Space') { 123 | event.preventDefault(); 124 | 125 | // Find the active cell in the Jupyter Notebook 126 | const activeCell = document.querySelector('.cell.selected .CodeMirror'); 127 | if (activeCell) { 128 | // Retrieve the content of the active cell 129 | const cellContent = activeCell.CodeMirror.getValue(); 130 | 131 | // Log the cell content to the console 132 | console.log(cellContent); 133 | } 134 | } 135 | }); 136 | } 137 | ``` 138 | 139 | This script checks if the current page is a Jupyter Notebook and listens for the Ctrl + Space key combination. When the key combination is detected, it retrieves the content of the currently active cell and logs it to the console. 140 | 141 | 142 | #### You: 143 | content.js:12 Uncaught TypeError: Cannot read properties of undefined (reading 'getValue') 144 | at HTMLDocument. (content.js:12:53) 145 | 146 | #### ChatGPT: 147 | Apologies for the oversight. It seems that the `CodeMirror` instance is not directly accessible from the content script. To resolve this issue, we can inject a script into the page, which will have access to the `CodeMirror` instance. 148 | 149 | Update the `content.js` file with the following content: 150 | 151 | ```javascript 152 | // Check if the current page is a Jupyter Notebook 153 | if (document.querySelector('body.notebook_app')) { 154 | // Inject script into the page 155 | const injectedScript = document.createElement('script'); 156 | injectedScript.textContent = '(' + main.toString() + ')();'; 157 | document.body.appendChild(injectedScript); 158 | 159 | // Remove the injected script after execution 160 | document.body.removeChild(injectedScript); 161 | } 162 | 163 | function main() { 164 | document.addEventListener('keydown', (event) => { 165 | // Check if the Ctrl + Space keys were pressed 166 | if (event.ctrlKey && event.code === 'Space') { 167 | event.preventDefault(); 168 | 169 | // Find the active cell in the Jupyter Notebook 170 | const activeCell = document.querySelector('.cell.selected .CodeMirror'); 171 | if (activeCell) { 172 | // Retrieve the content of the active cell 173 | const cellContent = activeCell.CodeMirror.getValue(); 174 | 175 | // Log the cell content to the console 176 | console.log(cellContent); 177 | } 178 | } 179 | }); 180 | } 181 | ``` 182 | 183 | By using this approach, the `main()` function will be injected into the page and executed, gaining access to the `CodeMirror` instance. It should no longer throw an error, and the active cell content will be logged to the console when the Ctrl + Space keys are pressed. 184 | 185 | 186 | #### You: 187 | content.js:12 (anonymous function) 188 | 189 | #### ChatGPT: 190 | My apologies again. I overlooked the fact that injecting the script this way would create a separate JavaScript scope. To communicate between the injected script and the content script, we can use the `window.postMessage()` method and a message event listener. Here's the updated `content.js` file: 191 | 192 | ```javascript 193 | // Check if the current page is a Jupyter Notebook 194 | if (document.querySelector('body.notebook_app')) { 195 | // Inject script into the page 196 | const injectedScript = document.createElement('script'); 197 | injectedScript.textContent = '(' + main.toString() + ')();'; 198 | document.body.appendChild(injectedScript); 199 | 200 | // Set up message event listener 201 | window.addEventListener('message', (event) => { 202 | if (event.source !== window) return; 203 | 204 | if (event.data.type === 'JUPYTER_CELL_CONTENT') { 205 | console.log(event.data.cellContent); 206 | } 207 | }); 208 | } 209 | 210 | function main() { 211 | document.addEventListener('keydown', (event) => { 212 | // Check if the Ctrl + Space keys were pressed 213 | if (event.ctrlKey && event.code === 'Space') { 214 | event.preventDefault(); 215 | 216 | // Find the active cell in the Jupyter Notebook 217 | const activeCell = document.querySelector('.cell.selected .CodeMirror'); 218 | if (activeCell) { 219 | // Retrieve the content of the active cell 220 | const cellContent = activeCell.CodeMirror.getValue(); 221 | 222 | // Post a message to the content script 223 | window.postMessage({ type: 'JUPYTER_CELL_CONTENT', cellContent }, '*'); 224 | } 225 | } 226 | }); 227 | } 228 | ``` 229 | 230 | In this updated version, the injected script (`main()` function) posts a message to the content script containing the active cell content. The content script listens for these messages and logs the cell content to the console. This should resolve the issue and allow you to log the cell content when 231 | 232 | 233 | #### You: 234 | content.js:12 (anonymous function) 235 | 236 | #### ChatGPT: 237 | I apologize for the confusion. There is another approach we can take using a custom event to communicate between the injected script and the content script. Here's the updated `content.js` file: 238 | 239 | ```javascript 240 | // Check if the current page is a Jupyter Notebook 241 | if (document.querySelector('body.notebook_app')) { 242 | // Inject script into the page 243 | const injectedScript = document.createElement('script'); 244 | injectedScript.textContent = '(' + main.toString() + ')();'; 245 | (document.head || 246 | ``` 247 | 248 | 249 | #### You: 250 | What does CodeMirror means here 251 | 252 | #### ChatGPT: 253 | `CodeMirror` is a versatile, open-source text editor implemented in JavaScript for the browser. It is often used for coding and markup languages, and it provides various features like syntax highlighting, code folding, and autocompletion. 254 | 255 | In the context of Jupyter Notebook, `CodeMirror` is the text editor used for editing the content within each code and markdown cell. Each cell in a Jupyter Notebook contains an instance of the `CodeMirror` editor, which allows users to interact with and edit the content within the cell. 256 | 257 | In the provided script, `activeCell.CodeMirror` is an attempt to access the `CodeMirror` instance associated with the currently active (or selected) cell in the Jupyter Notebook. However, due to the JavaScript scope limitations mentioned earlier, the `CodeMirror` instance is not directly accessible from the content script. That's why we used the injected script approach to access the `CodeMirror` instance and capture the content of the active cell. 258 | 259 | 260 | #### You: 261 |
In [25]:
262 | Here is what happen when I select a cell 263 | 264 | #### ChatGPT: 265 | Thank you for providing the cell HTML. Based on the provided information, I have revised the solution to get the active cell content. The updated `content.js` file is as follows: 266 | 267 | ```javascript 268 | // Check if the current page is a Jupyter Notebook 269 | if (document.querySelector('body.notebook_app')) { 270 | // Inject script into the page 271 | const injectedScript = document.createElement('script'); 272 | injectedScript.textContent = '(' + main.toString() + ')();'; 273 | document.body.appendChild(injectedScript); 274 | } 275 | 276 | function main() { 277 | document.addEventListener('keydown', (event) => { 278 | // Check if the Ctrl + Space keys were pressed 279 | if (event.ctrlKey && event.code === 'Space') { 280 | event.preventDefault(); 281 | 282 | // Find the active cell in the Jupyter Notebook 283 | const activeCell = document.querySelector('.cell.selected .input_area .CodeMirror'); 284 | if (activeCell) { 285 | // Retrieve the content of the active cell 286 | const cellContent = getCellContent(activeCell); 287 | 288 | // Log the cell content to the console 289 | console.log(cellContent); 290 | } 291 | } 292 | }); 293 | 294 | function getCellContent(cell) { 295 | const codeMirrorLines = cell.querySelectorAll('.CodeMirror-code pre'); 296 | const content = []; 297 | 298 | codeMirrorLines.forEach((line) => { 299 | content.push(line.textContent); 300 | }); 301 | 302 | return content.join('\n'); 303 | } 304 | } 305 | ``` 306 | 307 | In this updated version, we have added a new function called \`getCellContent 308 | 309 | 310 | #### You: 311 | document.body.appendChild(injectedScript); 312 | Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:*". Either the 'unsafe-inline' keyword, a hash ('sha256-Rg2jywNs3hMTge7mogjQX1qMQbM/+kN8mP5Dh9ZKHKU='), or a nonce ('nonce-...') is required to enable inline execution. 313 | 314 | 315 | #### ChatGPT: 316 | I apologize for the oversight. Content Security Policy (CSP) is blocking the injection of inline scripts. In this case, we can use a different approach to avoid injecting the script directly into the document body. Instead, we will create a separate JavaScript file and inject it as an external script. 317 | 318 | 1. Create a new file named `injectedScript.js` in your extension folder with the following content: 319 | 320 | ```javascript 321 | (function () { 322 | document.addEventListener('keydown', (event) => { 323 | // Check if the Ctrl + Space keys were pressed 324 | if (event.ctrlKey && event.code === 'Space') { 325 | event.preventDefault(); 326 | 327 | // Find the active cell in the Jupyter Notebook 328 | const activeCell = document.querySelector('.cell.selected .input_area .CodeMirror'); 329 | if (activeCell) { 330 | // Retrieve the content of the active cell 331 | const cellContent = getCellContent(activeCell); 332 | 333 | // Log the cell content to the console 334 | console.log(cellContent); 335 | } 336 | } 337 | }); 338 | 339 | function getCellContent(cell) { 340 | const codeMirrorLines = cell.querySelectorAll('.CodeMirror-code pre'); 341 | const content = []; 342 | 343 | codeMirrorLines.forEach((line) => { 344 | content.push(line.textContent); 345 | }); 346 | 347 | return content.join('\n'); 348 | } 349 | })(); 350 | ``` 351 | 352 | 2. Update your `manifest.json` file to include the new `injectedScript.js` file in the `web_accessible_resources`: 353 | 354 | ```json 355 | { 356 | "manifest_version": 2, 357 | "name": "Jupyter Notebook Cell Catcher", 358 | "version": "1.0", 359 | "permissions": ["activeTab"], 360 | "content_scripts": [ 361 | { 362 | "matches": ["*://*/*"], 363 | 364 | ``` 365 | 366 | 367 | #### You: 368 | // Check if the current page is a Jupyter Notebook 369 | if (document.querySelector('body.notebook_app')) { 370 | document.addEventListener('keydown', (event) => { 371 | // Check if the Ctrl + Space keys were pressed 372 | if (event.ctrlKey && event.code === 'Space') { 373 | event.preventDefault(); 374 | 375 | // Find the active cell in the Jupyter Notebook 376 | const activeCell = document.querySelector('.cell.selected .CodeMirror'); 377 | if (activeCell) { 378 | // Retrieve the content of the active cell 379 | const cellContent = activeCell.CodeMirror.getValue(); 380 | 381 | // Log the cell content to the console 382 | console.log(cellContent); 383 | } 384 | } 385 | }); 386 | } 387 | Can we just reuse this script ? 388 | 389 | 390 | #### ChatGPT: 391 | Yes, you can reuse the script. The issue with the previous approach was the inability to access the `CodeMirror` instance due to JavaScript scope limitations. However, since you are able to access the `CodeMirror` instance with `activeCell.CodeMirror`, you can modify the script as follows: 392 | 393 | ```javascript 394 | // Check if the current page is a Jupyter Notebook 395 | if (document.querySelector('body.notebook_app')) { 396 | document.addEventListener('keydown', (event) => { 397 | // Check if the Ctrl + Space keys were pressed 398 | if (event.ctrlKey && event.code === 'Space') { 399 | event.preventDefault(); 400 | 401 | // Find the active cell in the Jupyter Notebook 402 | const activeCell = document.querySelector('.cell.selected .CodeMirror'); 403 | if (activeCell) { 404 | // Retrieve the content of the active cell 405 | const cellContent = activeCell.CodeMirror.getValue(); 406 | 407 | // Log the cell content to the console 408 | console.log(cellContent); 409 | } 410 | } 411 | }); 412 | } 413 | ``` 414 | 415 | If you have tested the script and confirmed that it works as intended, you can use this updated script for your Chrome extension. 416 | 417 | 418 | #### You: 419 | It does not work, but can you make it work based on the html content I shared before ? 420 | 421 | #### ChatGPT: 422 | I apologize for the confusion. We can use an alternative approach to get the active cell content from the HTML content you shared. Here's the updated `content.js`: 423 | 424 | ```javascript 425 | // Check if the current page is a Jupyter Notebook 426 | if (document.querySelector('body.notebook_app')) { 427 | document.addEventListener('keydown', (event) => { 428 | // Check if the Ctrl + Space keys were pressed 429 | if (event.ctrlKey && event.code === 'Space') { 430 | event.preventDefault(); 431 | 432 | // Find the active cell in the Jupyter Notebook 433 | const activeCell = document.querySelector('.cell.selected .input_area .CodeMirror'); 434 | if (activeCell) { 435 | // Retrieve the content of the active cell 436 | const cellContent = getCellContent(activeCell); 437 | 438 | // Log the cell content to the console 439 | console.log(cellContent); 440 | } 441 | } 442 | }); 443 | 444 | function getCellContent(cell) { 445 | const codeMirrorLines = cell.querySelectorAll('.CodeMirror-code pre'); 446 | const content = []; 447 | 448 | codeMirrorLines.forEach((line) => { 449 | content.push(line.textContent); 450 | }); 451 | 452 | return content.join('\n'); 453 | } 454 | } 455 | ``` 456 | 457 | This script will work with the provided HTML content by directly accessing the lines within the CodeMirror instance. It will log the cell content in the console when the user presses Ctrl + Space. 458 | 459 | 460 | #### You: 461 | What does event.preventDefault(); do 462 | 463 | #### ChatGPT: 464 | `event.preventDefault();` is a method that is called on the event object to cancel the default action associated with the event. In the context of this script, it is used to prevent the default behavior that occurs when the user presses the Ctrl + Space key combination. 465 | 466 | For example, in some browsers or applications, Ctrl + Space might be used for language switching or other functionalities. By calling `event.preventDefault();`, we ensure that our script does not interfere with or trigger these default actions, allowing the script to focus solely on capturing the content of the active cell in the Jupyter Notebook when the user presses Ctrl + Space. 467 | 468 | 469 | #### You: 470 | Is it possible to add some input suggestion throm this chrome extention ? 471 | 472 | #### ChatGPT: 473 | While it's possible to add input suggestions through a Chrome extension, it's not recommended to do so directly in Jupyter Notebook because it already has its built-in autocompletion system that works quite well. 474 | 475 | However, if you still want to implement custom input suggestions through the Chrome extension, you can follow these steps: 476 | 477 | 1. Create a function that listens for user input and shows suggestions when appropriate. 478 | 2. Inject custom CSS styles to create a visually appealing suggestions box. 479 | 3. Add event listeners to handle keyboard and mouse interactions with the suggestions box. 480 | 481 | Here's a simple example to demonstrate the concept: 482 | 483 | 1. Update `content.js`: 484 | 485 | ```javascript 486 | if (document.querySelector('body.notebook_app')) { 487 | const suggestionBox = createSuggestionBox(); 488 | document.body.appendChild(suggestionBox); 489 | 490 | document.addEventListener('keyup', (event) => { 491 | const activeCell = document.querySelector('.cell.selected .input_area .CodeMirror'); 492 | if (activeCell) { 493 | const cellContent = getCellContent(activeCell); 494 | showSuggestions(event, cellContent, suggestionBox); 495 | } 496 | }); 497 | 498 | // Add other event listeners and functions you previously had, such as the Ctrl + Space functionality 499 | } 500 | 501 | function createSuggestionBox() { 502 | const div = document.createElement('div'); 503 | div.id = 'custom-suggestion-box'; 504 | div.style.display = 'none'; 505 | div.style.position = 'absolute'; 506 | div.style.zIndex = '9999'; 507 | div.style.backgroundColor = 'white'; 508 | div.style.border = '1px solid black'; 509 | div.style.padding = '5px'; 510 | return div; 511 | } 512 | 513 | function showSuggestions(event, cellContent, suggestionBox) { 514 | // Implement your logic to show suggestions based on the cellContent 515 | // For example, if the last word is "pd", you can show "pd.DataFrame", "pd.Series", etc. 516 | // In this example, we're showing suggestions when the user types "pd." 517 | 518 | if (cellContent.endsWith('pd.')) { 519 | const suggestions = ['pd.DataFrame', 'pd.Series', 'pd.read_csv']; 520 | suggestionBox.innerHTML = suggestions.map((s) => `
${s}
`).join(''); 521 | suggestionBox.style.display = 'block'; 522 | 523 | const rect = event.target.getBoundingClientRect(); 524 | suggestionBox.style.top = rect.top + rect.height + 'px'; 525 | suggestionBox.style.left = rect.left + 'px'; 526 | } else { 527 | suggestionBox.style.display = 'none'; 528 | } 529 | } 530 | 531 | // Add other functions you previously had, such as getCellContent() 532 | ``` 533 | 534 | 2. Add custom CSS to your extension to style the suggestions box and its items: 535 | 536 | ```css 537 | #custom-suggestion-box { 538 | font-family: Arial, sans-serif; 539 | font-size: 14px; 540 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 541 | max-height: 200px; 542 | overflow-y: auto; 543 | } 544 | 545 | #custom-suggestion-box div { 546 | padding: 5px; 547 | } 548 | 549 | #custom-suggestion-box div:hover { 550 | background-color: #f0f0f0; 551 | cursor: pointer; 552 | } 553 | ``` 554 | 555 | Please note that this is a basic example to demonstrate the concept. You may need to further develop the logic to show context-aware suggestions and handle interactions with the suggestion box. 556 | 557 | In conclusion, while it's possible to add input suggestions through a Chrome extension, it's not recommended for Jupyter Notebook due to its built-in autocompletion system. However, the provided example should give you a starting point to experiment with custom suggestions if you still want to proceed. 558 | 559 | 560 | #### You: 561 | is the manifest.json is going to change ? 562 | Here is the current manifest 563 | { 564 | "manifest_version": 3, 565 | "name": "Jupyter Data Capture", 566 | "version": "1.0", 567 | "description": "Capture data in Jupyter Notebook cells and send to a remote server.", 568 | "permissions": [ 569 | "activeTab" 570 | ], 571 | "content_scripts": [ 572 | { 573 | "matches": ["*://*/*"], 574 | "js": ["content.js"], 575 | "run_at": "document_end" 576 | } 577 | ] 578 | } 579 | 580 | 581 | #### ChatGPT: 582 | To include the custom CSS for the suggestion box in your Chrome extension, you need to update your `manifest.json`. You should add a `css` field to the `content_scripts` object to include your custom CSS file. If you haven't created the CSS file yet, you can name it `styles.css` and put it in the same directory as your `content.js` file. 583 | 584 | Here's the updated `manifest.json`: 585 | 586 | ```json 587 | { 588 | "manifest_version": 3, 589 | "name": "Jupyter Data Capture", 590 | "version": "1.0", 591 | "description": "Capture data in Jupyter Notebook cells and send to a remote server.", 592 | "permissions": [ 593 | "activeTab" 594 | ], 595 | "content_scripts": [ 596 | { 597 | "matches": ["*://*/*"], 598 | "js": ["content.js"], 599 | "css": ["styles.css"], 600 | "run_at": "document_end" 601 | } 602 | ] 603 | } 604 | ``` 605 | 606 | With this update, your custom CSS file (`styles.css`) will be injected into the pages where the content script is executed. This will allow you to style the suggestion box and its items as described in the previous response. 607 | 608 | 609 | #### You: 610 | Could not load css 'styles.css' for content script. 611 | Could not load manifest. 612 | 613 | #### ChatGPT: 614 | I apologize for the confusion. There is an error in the manifest file I provided. The correct key for injecting CSS files in a content script is `css` and not `styles`. Here is the corrected `manifest.json` file: 615 | 616 | ```json 617 | { 618 | "manifest_version": 3, 619 | "name": "Jupyter Data Capture", 620 | "version": "1.0", 621 | "description": "Capture data in Jupyter Notebook cells and send to a remote server.", 622 | "permissions": [ 623 | "activeTab" 624 | ], 625 | "content_scripts": [ 626 | { 627 | " 628 | ``` 629 | 630 | 631 | #### You: 632 | what does keyup and keydown here mean 633 | 634 | #### ChatGPT: 635 | `keydown` and `keyup` are JavaScript DOM (Document Object Model) events that represent the pressing and releasing of a key on a keyboard. They are triggered when a user interacts with an input element or other interactive elements on a webpage. 636 | 637 | - `keydown`: This event is fired when a key on the keyboard is first pressed down. It occurs before the actual input is registered by the input element. The event object contains information about the key that was pressed, any modifier keys (like Ctrl, Shift, or Alt), and other related data. You can use this event to detect when a specific key or combination of keys is pressed and execute custom functionality. 638 | 639 | - `keyup`: This event is fired when a key on the keyboard is released after being pressed. Like the `keydown` event, the event object contains information about the key that was released and any modifier keys. You can use this event to detect when a specific key is released and perform actions based on that. 640 | 641 | In the context of a Chrome extension or web application, you can use `keydown` and `keyup` events to handle keyboard input, provide keyboard shortcuts, or create interactive features that respond to user input. 642 | 643 | 644 | #### You: 645 | I do not want to do drop list for the suggestion. Instead, prefille the cell with grey charater, and user could validate the suggestion use arrow right 646 | 647 | #### ChatGPT: 648 | To implement this functionality, you'll need to update your content script to include a few changes. You can create a function to handle the suggestions and update the active cell with the greyed-out suggestion text. You can also listen for the `ArrowRight` key to validate the suggestion. 649 | 650 | Here's the updated content script (`content.js`): 651 | 652 | ```javascript 653 | // Check if the current page is a Jupyter Notebook 654 | if (document.querySelector('body.notebook_app')) { 655 | document.addEventListener('keydown', (event) => { 656 | // Check if the Ctrl + Space keys were pressed 657 | if (event.ctrlKey && event.code === 'Space') { 658 | event.preventDefault(); 659 | 660 | // Find the active cell in the Jupyter Notebook 661 | const activeCell = document.querySelector('.cell.selected .CodeMirror'); 662 | if (activeCell) { 663 | // Retrieve the content of the active cell 664 | const cellContent = activeCell.CodeMirror.getValue(); 665 | 666 | // Log the cell content to the console 667 | console.log(cellContent); 668 | 669 | // Suggestion handling 670 | const suggestion = 'your_suggestion_here'; // Replace with your actual suggestion 671 | handleSuggestion(activeCell.CodeMirror, suggestion); 672 | } 673 | } 674 | 675 | // Check if the ArrowRight key was pressed 676 | if (event.code === 'ArrowRight') { 677 | const activeCell = document.querySelector('.cell.selected .CodeMirror'); 678 | if (activeCell) { 679 | validateSuggestion(activeCell.CodeMirror); 680 | } 681 | } 682 | }); 683 | } 684 | 685 | function handleSuggestion(cm, suggestion) { 686 | const cursor = cm.getCursor(); 687 | cm.replaceRange(suggestion, cursor, null, '+suggest'); 688 | const endPos = cm.getCursor(); 689 | cm.markText(cursor, endPos, { 690 | className: 'greyed-suggestion', 691 | atomic: true, 692 | inclusiveRight: true, 693 | }); 694 | } 695 | 696 | function validateSuggestion(cm) { 697 | const marks = cm.getAllMarks(); 698 | if (marks.length) { 699 | const mark = marks[0]; 700 | mark.clear(); 701 | cm.setCursor(cm.getCursor()); 702 | } 703 | } 704 | ``` 705 | 706 | Then, create a CSS file (`styles.css`) to style the greyed-out suggestion text: 707 | 708 | ```css 709 | .greyed-suggestion { 710 | color: #888; 711 | } 712 | ``` 713 | 714 | Don't forget to include the `styles.css` file in your `manifest.json`: 715 | 716 | ```json 717 | { 718 | "manifest_version": 3, 719 | "name": "Jupyter Data Capture", 720 | "version": "1.0", 721 | "description": "Capture data in Jupyter Notebook cells and send to a remote server.", 722 | "permissions": [ 723 | "activeTab" 724 | ], 725 | "content_scripts": [ 726 | { 727 | "matches": ["*://*/*"], 728 | "js": ["content.js"], 729 | "css": ["styles.css"], 730 | "run_at": "document_end" 731 | } 732 | ] 733 | } 734 | ``` 735 | 736 | With these changes, your extension will pre-fill the cell with grey characters as a suggestion, and the user can validate the suggestion by pressing the right arrow key. Remember to replace `'your_suggestion_here'` with the actual suggestion you want to provide. 737 | 738 | 739 | #### You: 740 | Two issues: 741 | - const cellContent = activeCell.CodeMirror.getValue(); is already replaced by const cellContent = getCellContent(activeCell); 742 | - activeCell.CodeMirror is undefined 743 | 744 | #### ChatGPT: 745 | Apologies for the confusion. I realize that I missed the previous changes we made to the content script. Here's the corrected version of `content.js`: 746 | 747 | ```javascript 748 | // Check if the current page is a Jupyter Notebook 749 | if (document.querySelector('body.notebook_app')) { 750 | document.addEventListener('keydown', (event) => { 751 | // Check if the Ctrl + Space keys were pressed 752 | if (event.ctrlKey && event.code === 'Space') { 753 | event.preventDefault(); 754 | 755 | // Find the active cell in the Jupyter Notebook 756 | const activeCell = document.querySelector('.cell.selected .CodeMirror'); 757 | if (activeCell) { 758 | // Retrieve the content of the active cell 759 | const cellContent = getCellContent(activeCell); 760 | 761 | // Log the cell content to the console 762 | console.log(cellContent); 763 | 764 | // Suggestion handling 765 | const suggestion = 'your_suggestion_here'; // Replace with your actual suggestion 766 | handleSuggestion(activeCell.CodeMirror, suggestion); 767 | } 768 | } 769 | 770 | // Check if the ArrowRight key was pressed 771 | if (event.code === 'ArrowRight') { 772 | const activeCell = document.querySelector('.cell.selected .CodeMirror'); 773 | if (activeCell) { 774 | validateSuggestion(activeCell.CodeMirror); 775 | } 776 | } 777 | }); 778 | } 779 | 780 | function getCellContent(cell) { 781 | const codeMirror = cell.CodeMirror || cell; 782 | return codeMirror.getValue(); 783 | } 784 | 785 | function handleSuggestion(cm, suggestion) { 786 | const cursor = cm.getCursor(); 787 | cm.replaceRange(suggestion, cursor, null, '+suggest'); 788 | const endPos = cm.getCursor(); 789 | cm.markText(cursor, endPos, { 790 | className: 'greyed-suggestion', 791 | atomic: true, 792 | inclusiveRight: true, 793 | }); 794 | } 795 | 796 | function validateSuggestion(cm) { 797 | const marks = cm.getAllMarks(); 798 | if (marks.length) { 799 | const mark = marks[0]; 800 | mark.clear(); 801 | cm.setCursor(cm.getCursor()); 802 | } 803 | } 804 | ``` 805 | 806 | In this updated script, I have replaced `const cellContent = activeCell.CodeMirror.getValue();` with `const cellContent = getCellContent(activeCell);`. 807 | 808 | If `activeCell.CodeMirror` is still undefined, you can try changing the following lines: 809 | 810 | From: 811 | 812 | ```javascript 813 | handleSuggestion(activeCell.CodeMirror, suggestion); 814 | ``` 815 | 816 | To: 817 | 818 | ```javascript 819 | handleSuggestion(activeCell.CodeMirror || activeCell, suggestion); 820 | ``` 821 | 822 | And: 823 | 824 | From: 825 | 826 | ```javascript 827 | validateSuggestion(activeCell.CodeMirror); 828 | ``` 829 | 830 | To: 831 | 832 | ```javascript 833 | validateSuggestion(activeCell.CodeMirror || activeCell); 834 | ``` 835 | 836 | These changes will use the `activeCell` if `activeCell.CodeMirror` is undefined. 837 | 838 | 839 | #### You: 840 | getCursor is not define. Here is what cm looks like 841 |
842 | 843 | #### ChatGPT: 844 | It seems that the `activeCell` variable contains the CodeMirror DOM element instead of the actual CodeMirror instance. Let's modify the `handleSuggestion` and `validateSuggestion` functions to accept the DOM element and then obtain the CodeMirror instance from it. 845 | 846 | Update the `handleSuggestion` and `validateSuggestion` functions as follows: 847 | 848 | ```javascript 849 | function handleSuggestion(cell, suggestion) { 850 | const cm = cell.CodeMirror || cell; 851 | const cursor = cm.getCursor(); 852 | cm.replaceRange(suggestion, cursor, null, '+suggest'); 853 | const endPos = cm.getCursor(); 854 | cm.markText(cursor, endPos, { 855 | className: 'greyed-suggestion', 856 | atomic: true, 857 | inclusiveRight: true, 858 | }); 859 | } 860 | 861 | function validateSuggestion(cell) { 862 | const cm = cell.CodeMirror || cell; 863 | const marks = cm.getAllMarks(); 864 | if (marks.length) { 865 | const mark = marks[0]; 866 | mark.clear(); 867 | cm.setCursor(cm.getCursor()); 868 | } 869 | } 870 | ``` 871 | 872 | This should work with the provided `cm` DOM element, and the `getCursor` function should no longer be undefined. 873 | 874 | 875 | #### You: 876 | cell.CodeMirror is undefined 877 | 878 | #### ChatGPT: 879 | I apologize for the confusion. I misunderstood the structure of the DOM element. It appears that the CodeMirror instance is not directly attached to the DOM element. 880 | 881 | Here's a new approach: we can use the `Jupyter.notebook` object to interact with the active cell, which will have the correct CodeMirror instance. Please replace the entire content of the content script with the following code: 882 | 883 | ```javascript 884 | // Check if the current page is a Jupyter Notebook 885 | if (document.querySelector('body.notebook_app')) { 886 | document.addEventListener('keydown', (event) => { 887 | // Check if the Ctrl + Space keys were pressed 888 | if (event.ctrlKey && event.code === 'Space') { 889 | event.preventDefault(); 890 | 891 | // Find the active cell in the Jupyter Notebook 892 | const activeCell = Jupyter.notebook.get_selected_cell(); 893 | if (activeCell) { 894 | // Retrieve the content of the active cell 895 | const cellContent = activeCell.get_text(); 896 | 897 | // Log the cell content to the console 898 | console.log(cellContent); 899 | 900 | // Example suggestion 901 | const suggestion = 'df.head()'; 902 | 903 | // Add suggestion to the cell 904 | handleSuggestion(activeCell.code_mirror, suggestion); 905 | } 906 | } else if (event.code === 'ArrowRight') { 907 | const activeCell = Jupyter.notebook.get_selected_cell(); 908 | if (activeCell) { 909 | validateSuggestion(activeCell.code_mirror); 910 | } 911 | } 912 | }); 913 | } 914 | 915 | function handleSuggestion(cm, suggestion) { 916 | const cursor = cm.getCursor(); 917 | cm.replaceRange(suggestion, cursor, null, '+suggest'); 918 | const endPos = cm.getCursor(); 919 | cm.markText(cursor, endPos, { 920 | className: 'greyed-suggestion', 921 | atomic: true, 922 | inclusiveRight: true, 923 | }); 924 | } 925 | 926 | function validateSuggestion(cm) { 927 | const marks = cm.getAllMarks(); 928 | if (marks.length) { 929 | const mark = marks[0]; 930 | mark.clear(); 931 | cm.setCursor(cm.getCursor()); 932 | } 933 | } 934 | ``` 935 | 936 | This code uses the `Jupyter.notebook` object to get the selected cell and its CodeMirror instance. 937 | -------------------------------------------------------------------------------- /chats/ChatGPT-Managing_Chrome_Extension_Requests.md: -------------------------------------------------------------------------------- 1 | # Managing Chrome Extension Requests 2 | 3 | #### You: 4 | how to manage request in chrome extension 5 | 6 | #### ChatGPT: 7 | To manage HTTP requests in a Chrome extension, you can use the `chrome.webRequest` API. This API allows you to observe and manipulate web requests made by the browser. You can use it to intercept, block, modify, or redirect requests. Here's a basic example of how to use the `chrome.webRequest` API to manage requests in a Chrome extension: 8 | 9 | 1. Declare the necessary permissions in the `manifest.json` file of your extension: 10 | 11 | ```json 12 | { 13 | "manifest_version": 3, 14 | "name": "WebRequest Example", 15 | "version": "1.0", 16 | "permissions": ["webRequest", "webRequestBlocking", "*://*.example.com/*"], 17 | "action": { 18 | "default_popup": "popup.html" 19 | }, 20 | "background": { 21 | "service_worker": "background.js" 22 | } 23 | } 24 | ``` 25 | 26 | In this example, we declare permissions for the `webRequest` and `webRequestBlocking` APIs, as well as the URL pattern `*://*.example.com/*` to specify the URLs that the extension can access. 27 | 28 | 2. Create a background script (`background.js`) to manage requests: 29 | 30 | ```js 31 | chrome.webRequest.onBeforeRequest.addListener( 32 | (details) => { 33 | // You can manage the request here, e.g., block, redirect, or modify it 34 | console.log('Request URL:', details.url); 35 | 36 | // Example: Block requests to a specific URL 37 | if (details.url.includes('block-this-url')) { 38 | return { cancel: true }; 39 | } 40 | 41 | // Example: Redirect requests to a different URL 42 | if (details.url.includes('redirect-this-url')) { 43 | return { redirectUrl: 'https://www.example.com/redirected-url' }; 44 | } 45 | }, 46 | { urls: ['*://*.example.com/*'] }, // URL patterns to observe 47 | ['blocking'] // Use the "blocking" option to block or modify requests 48 | ); 49 | ``` 50 | 51 | In this example, we use the `onBeforeRequest` event to listen for outgoing web requests. We then check the URL of each request and decide whether to block it, redirect it, or allow it to proceed as usual. The `blocking` option allows us to block or modify requests synchronously. 52 | 53 | Note: The example above uses Manifest V3, which is the latest version of the Chrome extension manifest format as of my knowledge cutoff date in September 2021. If you are using an older version of the manifest (e.g., Manifest V2), you may need to make adjustments to the code to ensure compatibility. 54 | 55 | 56 | #### You: 57 | how to do a http get request, then show it 58 | 59 | #### ChatGPT: 60 | In a Chrome extension, you can perform an HTTP GET request using the `fetch` API, which is a modern and convenient way to make HTTP requests. After making the request, you can display the response in the extension's popup or background page. Here's an example of how to do this: 61 | 62 | 1. Create an HTML file (`popup.html`) for the extension's popup: 63 | 64 | ```html 65 | 66 | 67 | 68 | HTTP GET Request Example 69 | 75 | 76 | 77 | 78 |

 79 |   
 80 | 
 81 | 
 82 | ```
 83 | 
 84 | In this example, we create a button that the user can click to make the HTTP GET request. We also create a `
` element to display the response.
 85 | 
 86 | 2. Create a JavaScript file (`popup.js`) to handle the button click and make the HTTP GET request:
 87 | 
 88 | ```js
 89 | document.getElementById('getRequestButton').addEventListener('click', () => {
 90 |   // URL to make the GET request to
 91 |   const url = 'https://jsonplaceholder.typicode.com/todos/1';
 92 | 
 93 |   // Make the GET request using the fetch API
 94 |   fetch(url)
 95 |     .then((response) => {
 96 |       // Check if the response is successful
 97 |       if (!response.ok) {
 98 |         throw new Error('Network response was not ok');
 99 |       }
100 |       // Parse the response as JSON
101 |       return response.json();
102 |     })
103 |     .then((data) => {
104 |       // Display the response data as a JSON string
105 |       document.getElementById('responseContainer').textContent = JSON.stringify(data, null, 2);
106 |     })
107 |     .catch((error) => {
108 |       // Handle any errors that occurred during the fetch
109 |       console.error('There was a problem with the fetch operation:', error);
110 |       document.getElementById('responseContainer').textContent = 'Error: ' + error.message;
111 |     });
112 | });
113 | ```
114 | 
115 | In this example, we add an event listener to the button to handle the click event. When the button is clicked, we use the `fetch` API to make an HTTP GET request to the specified URL. We then parse the response as JSON and display it in the `responseContainer` element. If any errors occur, we handle them and display an error message.
116 | 
117 | 3. Update the `manifest.json` file to include the popup and permissions:
118 | 
119 | ```json
120 | {
121 |   "manifest_version": 3,
122 |   "name": "HTTP GET Request Example",
123 |   "version": "1.0",
124 |   "permissions": ["https://jsonplaceholder.typicode.com/*"],
125 |   "action": {
126 |     "default_popup": "popup.html"
127 |   }
128 | }
129 | ```
130 | 
131 | In this example, we specify the `default_popup` property to point to the `popup.html` file we created earlier. We also declare the necessary permissions for the URL we are making the request to.
132 | 
133 | After creating these files, you can load the extension into Chrome, and when you click the extension's icon, the popup will appear with the "Make GET Request" button. Clicking the button will make the HTTP GET request and display the response in the popup.
134 | 
135 | 
136 | #### You:
137 | how to handle promise in chrome extension
138 | 
139 | #### ChatGPT:
140 | In a Chrome extension, handling promises is similar to handling promises in regular JavaScript code. Promises are used to represent the eventual completion (or failure) of an asynchronous operation and its resulting value. Here's how you can handle promises in a Chrome extension:
141 | 
142 | 1. Creating and Using a Promise:
143 | 
144 | ```js
145 | // Create a function that returns a promise
146 | function getData() {
147 |   return new Promise((resolve, reject) => {
148 |     // Simulate an asynchronous operation (e.g., fetching data)
149 |     setTimeout(() => {
150 |       const data = 'Sample data';
151 |       resolve(data); // Resolve the promise with the data
152 |     }, 1000);
153 |   });
154 | }
155 | 
156 | // Use the promise
157 | getData()
158 |   .then((data) => {
159 |     // Handle the resolved data
160 |     console.log('Data:', data);
161 |   })
162 |   .catch((error) => {
163 |     // Handle any errors
164 |     console.error('Error:', error);
165 |   });
166 | ```
167 | 
168 | 2. Using `async/await` with Promises:
169 | 
170 | ```js
171 | // Create an async function that uses await to handle the promise
172 | async function fetchData() {
173 |   try {
174 |     const data = await getData(); // Use await to handle the promise
175 |     console.log('Data:', data);
176 |   } catch (error) {
177 |     console.error('Error:', error);
178 |   }
179 | }
180 | 
181 | // Call the async function
182 | fetchData();
183 | ```
184 | 
185 | 3. Handling Promises Returned by APIs:
186 |    Many APIs return promises, and you can handle them in a similar way. For example, the `fetch` API returns a promise that resolves with the Response object representing the response to the request:
187 | 
188 | ```js
189 | // Make a GET request using the fetch API
190 | fetch('https://jsonplaceholder.typicode.com/todos/1')
191 |   .then((response) => {
192 |     // Check if the response is successful
193 |     if (!response.ok) {
194 |       throw new Error('Network response was not ok');
195 |     }
196 |     // Parse the response as JSON
197 |     return response.json();
198 |   })
199 |   .then((data) => {
200 |     // Handle the resolved data
201 |     console.log('Data:', data);
202 |   })
203 |   .catch((error) => {
204 |     // Handle any errors
205 |     console.error('Error:', error);
206 |   });
207 | ```
208 | 
209 | 4. Using `async/await` with APIs Returning Promises:
210 | 
211 | ```js
212 | // Create an async function to fetch data using the fetch API
213 | async function fetchData() {
214 |   try {
215 |     const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
216 |     if (!response.ok) {
217 |       throw new Error('Network response was not ok');
218 |     }
219 |     const data = await response.json();
220 |     console.log('Data:', data);
221 |   } catch (error) {
222 |     console.error('Error:', error);
223 |   }
224 | }
225 | 
226 | // Call the async function
227 | fetchData();
228 | ```
229 | 
230 | These examples demonstrate how to handle promises in a Chrome extension, whether you create your own promises, use promises returned by APIs, or use the `async/await` syntax for more concise and readable code.
231 | 
232 | 
233 | #### You:
234 | I write this
235 |         getCodeCompletion(code).then((suggestion) => {
236 |           console.log(suggestion);
237 |           insertSuggestion(activeCell, suggestion);
238 |         }
239 | suggestion is undefined
240 | 
241 | #### ChatGPT:
242 | If `suggestion` is `undefined` in the `.then()` block, it means that the promise returned by the `getCodeCompletion(code)` function is resolving with an `undefined` value. There are a few possible reasons for this:
243 | 
244 | 1. The `getCodeCompletion(code)` function is not returning a value, or it is explicitly returning `undefined`.
245 | 2. The function that resolves the promise inside `getCodeCompletion(code)` is not providing a value when calling the `resolve` function.
246 | 3. There is an error or exception occurring inside `getCodeCompletion(code)` that prevents the promise from being resolved with a value.
247 | 
248 | To troubleshoot this issue, you need to examine the implementation of the `getCodeCompletion(code)` function. Here's what you should look for:
249 | 
250 | 1. Check the `getCodeCompletion(code)` function to ensure that it returns a promise.
251 | 2. If the promise is created using the `Promise` constructor, make sure that the `resolve` function is called with the desired value.
252 | 3. If the promise is created using an API or another function that returns a promise, make sure that the value is being passed correctly to the `.then()` block.
253 | 
254 | Here's an example of a correctly implemented `getCodeCompletion(code)` function:
255 | 
256 | ```js
257 | function getCodeCompletion(code) {
258 |   return new Promise((resolve, reject) => {
259 |     // Simulate an asynchronous operation
260 |     setTimeout(() => {
261 |       // Example: Generate a suggestion based on the input code
262 |       const suggestion = code + ' SUGGESTION';
263 |       resolve(suggestion); // Resolve the promise with the suggestion
264 |     }, 1000);
265 |   });
266 | }
267 | 
268 | // Usage
269 | getCodeCompletion('Sample code').then((suggestion) => {
270 |   console.log(suggestion);
271 |   // insertSuggestion(activeCell, suggestion);
272 | });
273 | ```
274 | 
275 | In this example, the `getCodeCompletion(code)` function returns a promise that resolves with a suggestion based on the input code. The suggestion is then logged to the console in the `.then()` block.
276 | 
277 | If you're still having trouble, please provide more context or share the implementation of the `getCodeCompletion(code)` function so that I can provide more specific guidance.
278 | 
279 | 
280 | #### You:
281 | Here is getcodecompletion
282 | async function getCodeCompletion(code) {
283 |   const prompt = `
284 | I have the following code:
285 | 
286 | \`\`\`
287 | ${code}
288 | \`\`\`
289 | 
290 | Please provide the missing code to complete the task.
291 | `;
292 | 
293 |   return await sendToOpenAI(prompt);
294 | }
295 | 
296 | #### ChatGPT:
297 | The `getCodeCompletion` function is an `async` function that takes `code` as an input, constructs a prompt, and then calls the `sendToOpenAI` function with the prompt. The function returns the result of `sendToOpenAI`.
298 | 
299 | To troubleshoot the issue of `suggestion` being `undefined`, we need to examine the implementation of the `sendToOpenAI` function. Specifically, we need to check whether `sendToOpenAI` returns a value (the suggestion) when it resolves.
300 | 
301 | Here's what you should look for in the `sendToOpenAI` function:
302 | 
303 | 1. Ensure that `sendToOpenAI` returns a promise that resolves with the desired suggestion value.
304 | 2. Check how the response from the OpenAI API is parsed and how the suggestion is extracted from the response.
305 | 
306 | If you can provide the implementation of the `sendToOpenAI` function, I can provide more specific guidance. In the meantime, here's a simplified example of how the `sendToOpenAI` function might be implemented:
307 | 
308 | ```js
309 | async function sendToOpenAI(prompt) {
310 |   // Simulate an API call to OpenAI
311 |   const response = await fetch('https://api.openai.com/v1/...', {
312 |     method: 'POST',
313 |     headers: { 'Content-Type': 'application/json' },
314 |     body: JSON.stringify({ prompt: prompt })
315 |   });
316 | 
317 |   // Check if the response is successful
318 |   if (!response.ok) {
319 |     throw new Error('API response was not ok');
320 |   }
321 | 
322 |   // Parse the response as JSON
323 |   const data = await response.json();
324 | 
325 |   // Extract the suggestion from the API response (this depends on the API's response structure)
326 |   const suggestion = data.choices[0]?.text;
327 | 
328 |   return suggestion;
329 | }
330 | 
331 | // Usage
332 | getCodeCompletion('Sample code').then((suggestion) => {
333 |   console.log(suggestion);
334 |   // insertSuggestion(activeCell, suggestion);
335 | });
336 | ```
337 | 
338 | In this example, the `sendToOpenAI` function makes an API call to OpenAI, parses the response, extracts the suggestion, and returns it. The `getCodeCompletion` function then uses this suggestion in the `.then()` block. Note that the structure of the API response and the way the suggestion is extracted may vary depending on the actual OpenAI API you are using.
339 | 
340 | 
341 | #### You:
342 | Here is sendtoopenai
343 | 
344 | // Function to send request to OpenAI API
345 | async function sendToOpenAI(prompt) {
346 |   const response = await fetch("https://api.openai.com/v1/chat/completions", {
347 |     method: "POST",
348 |     headers: {
349 |       "Content-Type": "application/json",
350 |       "Authorization": `Bearer ${OPENAI_API_KEY}`,
351 |     },
352 |     body: JSON.stringify({
353 |       model: "gpt-3.5-turbo",
354 |       messages: [{ role: "user", content: prompt }],
355 |     }),
356 |   });
357 |   const data = await response.json();
358 |   return data.choices && data.choices[0] && data.choices[0].message.text;
359 | }
360 | 
361 | #### ChatGPT:
362 | The `sendToOpenAI` function sends a request to the OpenAI API using the GPT-3.5-turbo model and returns the text of the first choice from the API response. The function appears to be implemented correctly, and it returns the text of the first choice if it exists.
363 | 
364 | If `suggestion` is `undefined` in the `.then()` block, it could be because the API response does not contain any choices, or the first choice does not have a `message.text` property. To handle this case, you can add a check to ensure that `suggestion` is defined before using it:
365 | 
366 | ```js
367 | getCodeCompletion(code).then((suggestion) => {
368 |   if (suggestion) {
369 |     console.log(suggestion);
370 |     insertSuggestion(activeCell, suggestion);
371 |   } else {
372 |     console.log('No suggestion received from the API.');
373 |   }
374 | }).catch((error) => {
375 |   console.error('Error:', error);
376 | });
377 | ```
378 | 
379 | In this updated code, we check if `suggestion` is defined before using it. If it is not defined, we log a message indicating that no suggestion was received from the API. Additionally, we added a `.catch()` block to handle any errors that may occur during the process.
380 | 
381 | If you are still experiencing issues, you may want to log the entire API response (`data`) inside the `sendToOpenAI` function to inspect its structure and verify that it contains the expected information:
382 | 
383 | ```js
384 | async function sendToOpenAI(prompt) {
385 |   // ...
386 |   const data = await response.json();
387 |   console.log('API response:', data); // Log the entire API response
388 |   return data.choices && data.choices[0] && data.choices[0].message.text;
389 | }
390 | ```
391 | 
392 | By logging the entire API response, you can check whether the response contains the expected `choices` array and whether the first choice contains a `message.text` property.
393 | 
394 | 
395 | #### You:
396 | Here is the response
397 | {"id":"chatcmpl-72Zc1o5foPTSCqz4FA2UR0PArwDiw","object":"chat.completion","created":1680847905,"model":"gpt-3.5-turbo-0301","usage":{"prompt_tokens":201,"completion_tokens":232,"total_tokens":433},"choices":[{"message":{"role":"assistant","content":"```\n\"\"\"read the csv usage_df.csv\n\"\"\"\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\n​\n# read the csv\ndf = pd.read_csv('usage_df.csv', index_col=0)\n# df.index = df.date\ndf\n\"\"\"usage_df has two column:\n1.date: a string like \"2022-03-01\"\n2.gpu_usage: a float indicating the average gpu uage\nData columns (total 2 columns):\n #   Column     Non-Null Count  Dtype  \n---  ------     --------------  -----  \n 0   date       365 non-null    object \n 1   gpu_usage  365 non-null    float64\nplot the daily usage of month 2022-03\n\"\"\"\n# plot the daily usage of month 2022-03\ndf['date'] = pd.to_datetime(df['date'])\ndf.set_index('date', inplace=True)\ndf.loc['2022-03']['gpu_usage'].plot()\nplt.title('GPU Usage in March 2022')\nplt.xlabel('Date')\nplt.ylabel('GPU Usage')\nplt.show()\n```"},"finish_reason":"stop","index":0}]}
398 | 
399 | 
400 | #### ChatGPT:
401 | The response from the OpenAI API contains a `choices` array with one element, and the `message` object inside the choice has a `role` property set to `"assistant"` and a `content` property that contains the generated text. The `content` property is what you want to extract as the suggestion.
402 | 
403 | Based on the response structure, you can extract the suggestion as follows:
404 | 
405 | ```js
406 | async function sendToOpenAI(prompt) {
407 |   const response = await fetch("https://api.openai.com/v1/chat/completions", {
408 |     method: "POST",
409 |     headers: {
410 |       "Content-Type": "application/json",
411 |       "Authorization": `Bearer ${OPENAI_API_KEY}`,
412 |     },
413 |     body: JSON.stringify({
414 |       model: "gpt-3.5-turbo",
415 |       messages: [{ role: "user", content: prompt }],
416 |     }),
417 |   });
418 |   const data = await response.json();
419 |   return data.choices && data.choices[0] && data.choices[0].message.content;
420 | }
421 | ```
422 | 
423 | The key change here is to use `data.choices[0].message.content` instead of `data.choices[0].message.text` to extract the suggestion from the API response.
424 | 
425 | With this change, the `suggestion` variable in the `.then()` block should contain the generated text from the API:
426 | 
427 | ```js
428 | getCodeCompletion(code).then((suggestion) => {
429 |   if (suggestion) {
430 |     console.log(suggestion);
431 |     insertSuggestion(activeCell, suggestion);
432 |   } else {
433 |     console.log('No suggestion received from the API.');
434 |   }
435 | }).catch((error) => {
436 |   console.error('Error:', error);
437 | });
438 | ```
439 | 
440 | This should resolve the issue of `suggestion` being `undefined`, and you should now be able to see the generated text in the console and use it in the `insertSuggestion` function.
441 | 
442 | 
443 | #### You:
444 |         getCodeCompletion(code).then((suggestion) => {
445 |           if (suggestion) {
446 |             console.log("[PROMPT]\n" + code)
447 |             console.log("[RESPONSE]\n" + suggestion);
448 |             insertSuggestion(activeCell, suggestion);
449 |           } else {
450 |             console.log('No suggestion received from the API.');
451 |           }
452 |         }).catch((error) => {
453 |           console.error('Error:', error);
454 |         });
455 | Parse the suggestion to identify code block and remove ```
456 | Here is an example
457 | ```
458 | # convert the date column to datetime object
459 | df['date'] = pd.to_datetime(df['date'])
460 | 
461 | # sort the dataframe by date
462 | df = df.sort_values('date')
463 | 
464 | # plot the data
465 | plt.plot(df['date'], df['gpu_usage'])
466 | plt.xlabel('Date')
467 | plt.ylabel('GPU Usage')
468 | plt.title('Daily GPU Usage in March 2022')
469 | plt.show()
470 | ```
471 | 
472 | #### ChatGPT:
473 | To parse the suggestion and extract the code block while removing the triple backticks (\`\`\`), you can use a regular expression to match the content between the backticks. Here's how you can do it:
474 | 
475 | ````js
476 | getCodeCompletion(code).then((suggestion) => {
477 |   if (suggestion) {
478 |     console.log("[PROMPT]\n" + code);
479 |     console.log("[RESPONSE]\n" + suggestion);
480 | 
481 |     // Use a regular expression to match the content between triple backticks
482 |     const codeBlockRegex = /```([\s\S]*?)```/g;
483 |     const match = codeBlockRegex.exec(suggestion);
484 |     const extractedCode = match && match[1] ? match[1].trim() : '';
485 | 
486 |     // Log the extracted code block
487 |     console.log("[EXTRACTED CODE]\n" + extractedCode);
488 | 
489 |     insertSuggestion(activeCell, extractedCode);
490 |   } else {
491 |     console.log('No suggestion received from the API.');
492 |   }
493 | }).catch((error) => {
494 |   console.error('Error:', error);
495 | });
496 | ````
497 | 
498 | In this code, we use the regular expression `/```([\s\S]*?)```/g` to match the content between triple backticks. The expression `([\s\S]*?)` captures any characters, including newlines, between the backticks. We then use the `exec` method to find the match and extract the captured group (the code block) using `match[1]`. We also use the `trim` method to remove any leading or trailing whitespace from the extracted code block.
499 | 
500 | The variable `extractedCode` contains the code block without the triple backticks, and you can use it in the `insertSuggestion` function or for any other purpose.
501 | 


--------------------------------------------------------------------------------
/examples/fixing_bug_example.ipynb:
--------------------------------------------------------------------------------
  1 | {
  2 |  "cells": [
  3 |   {
  4 |    "cell_type": "code",
  5 |    "execution_count": 4,
  6 |    "id": "1114df89",
  7 |    "metadata": {},
  8 |    "outputs": [
  9 |     {
 10 |      "ename": "NameError",
 11 |      "evalue": "name 'printa' is not defined",
 12 |      "output_type": "error",
 13 |      "traceback": [
 14 |       "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
 15 |       "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
 16 |       "Cell \u001b[1;32mIn[4], line 2\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;66;03m# print \"Hello, World!\"\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m \u001b[43mprinta\u001b[49m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHello, World!\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
 17 |       "\u001b[1;31mNameError\u001b[0m: name 'printa' is not defined"
 18 |      ]
 19 |     }
 20 |    ],
 21 |    "source": [
 22 |     "# print \"Hello, World!\"\n",
 23 |     "printa(\"Hello, World!\")"
 24 |    ]
 25 |   },
 26 |   {
 27 |    "cell_type": "code",
 28 |    "execution_count": 5,
 29 |    "id": "88fe6fb4",
 30 |    "metadata": {},
 31 |    "outputs": [
 32 |     {
 33 |      "ename": "SyntaxError",
 34 |      "evalue": "expected ':' (3930893725.py, line 1)",
 35 |      "output_type": "error",
 36 |      "traceback": [
 37 |       "\u001b[1;36m  Cell \u001b[1;32mIn[5], line 1\u001b[1;36m\u001b[0m\n\u001b[1;33m    if x == 5\u001b[0m\n\u001b[1;37m             ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m expected ':'\n"
 38 |      ]
 39 |     }
 40 |    ],
 41 |    "source": [
 42 |     "if x == 5\n",
 43 |     "    print(\"x == 5\")"
 44 |    ]
 45 |   },
 46 |   {
 47 |    "cell_type": "code",
 48 |    "execution_count": 30,
 49 |    "id": "b73b834e",
 50 |    "metadata": {},
 51 |    "outputs": [
 52 |     {
 53 |      "ename": "NameError",
 54 |      "evalue": "name 'y' is not defined",
 55 |      "output_type": "error",
 56 |      "traceback": [
 57 |       "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
 58 |       "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
 59 |       "Cell \u001b[1;32mIn[30], line 2\u001b[0m\n\u001b[0;32m      1\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m10\u001b[39m\n\u001b[1;32m----> 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43my\u001b[49m)\n",
 60 |       "\u001b[1;31mNameError\u001b[0m: name 'y' is not defined"
 61 |      ]
 62 |     }
 63 |    ],
 64 |    "source": [
 65 |     "x = 10\n",
 66 |     "print(y)"
 67 |    ]
 68 |   },
 69 |   {
 70 |    "cell_type": "code",
 71 |    "execution_count": 39,
 72 |    "id": "72409629",
 73 |    "metadata": {},
 74 |    "outputs": [
 75 |     {
 76 |      "ename": "NameError",
 77 |      "evalue": "name 'calculate_sum' is not defined",
 78 |      "output_type": "error",
 79 |      "traceback": [
 80 |       "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
 81 |       "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
 82 |       "Cell \u001b[1;32mIn[39], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mcalculate_sum\u001b[49m(\u001b[38;5;241m5\u001b[39m, \u001b[38;5;241m10\u001b[39m)\n",
 83 |       "\u001b[1;31mNameError\u001b[0m: name 'calculate_sum' is not defined"
 84 |      ]
 85 |     }
 86 |    ],
 87 |    "source": [
 88 |     "calculate_sum(5, 10)"
 89 |    ]
 90 |   },
 91 |   {
 92 |    "cell_type": "code",
 93 |    "execution_count": 40,
 94 |    "id": "c0c74cd8",
 95 |    "metadata": {},
 96 |    "outputs": [
 97 |     {
 98 |      "ename": "TypeError",
 99 |      "evalue": "can only concatenate str (not \"int\") to str",
100 |      "output_type": "error",
101 |      "traceback": [
102 |       "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
103 |       "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
104 |       "Cell \u001b[1;32mIn[40], line 3\u001b[0m\n\u001b[0;32m      1\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHello\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m      2\u001b[0m y \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m5\u001b[39m\n\u001b[1;32m----> 3\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m)\n",
105 |       "\u001b[1;31mTypeError\u001b[0m: can only concatenate str (not \"int\") to str"
106 |      ]
107 |     }
108 |    ],
109 |    "source": [
110 |     "x = \"Hello\"\n",
111 |     "y = 5\n",
112 |     "print(x + y)"
113 |    ]
114 |   },
115 |   {
116 |    "cell_type": "code",
117 |    "execution_count": 44,
118 |    "id": "3315c57c",
119 |    "metadata": {},
120 |    "outputs": [
121 |     {
122 |      "ename": "AttributeError",
123 |      "evalue": "'dict' object has no attribute 'name'",
124 |      "output_type": "error",
125 |      "traceback": [
126 |       "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
127 |       "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
128 |       "Cell \u001b[1;32mIn[44], line 2\u001b[0m\n\u001b[0;32m      1\u001b[0m person \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mname\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAlice\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mage\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;241m30\u001b[39m}\n\u001b[1;32m----> 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43mperson\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mname\u001b[49m)\n",
129 |       "\u001b[1;31mAttributeError\u001b[0m: 'dict' object has no attribute 'name'"
130 |      ]
131 |     }
132 |    ],
133 |    "source": [
134 |     "person = {\"name\": \"Alice\", \"age\": 30}\n",
135 |     "print(person.name)"
136 |    ]
137 |   },
138 |   {
139 |    "cell_type": "code",
140 |    "execution_count": 42,
141 |    "id": "bcd5e7b4",
142 |    "metadata": {},
143 |    "outputs": [
144 |     {
145 |      "ename": "ZeroDivisionError",
146 |      "evalue": "division by zero",
147 |      "output_type": "error",
148 |      "traceback": [
149 |       "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
150 |       "\u001b[1;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)",
151 |       "Cell \u001b[1;32mIn[42], line 3\u001b[0m\n\u001b[0;32m      1\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m10\u001b[39m\n\u001b[0;32m      2\u001b[0m y \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m----> 3\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\n",
152 |       "\u001b[1;31mZeroDivisionError\u001b[0m: division by zero"
153 |      ]
154 |     }
155 |    ],
156 |    "source": [
157 |     "x = 10\n",
158 |     "y = 0\n",
159 |     "result = x / y"
160 |    ]
161 |   },
162 |   {
163 |    "cell_type": "code",
164 |    "execution_count": 1,
165 |    "id": "1eb252b2",
166 |    "metadata": {},
167 |    "outputs": [
168 |     {
169 |      "ename": "KeyError",
170 |      "evalue": "'city'",
171 |      "output_type": "error",
172 |      "traceback": [
173 |       "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
174 |       "\u001b[1;31mKeyError\u001b[0m                                  Traceback (most recent call last)",
175 |       "Cell \u001b[1;32mIn[1], line 2\u001b[0m\n\u001b[0;32m      1\u001b[0m my_dict \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mname\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAlice\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mage\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;241m30\u001b[39m}\n\u001b[1;32m----> 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43mmy_dict\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mcity\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m)\n",
176 |       "\u001b[1;31mKeyError\u001b[0m: 'city'"
177 |      ]
178 |     }
179 |    ],
180 |    "source": [
181 |     "my_dict = {\"name\": \"Alice\", \"age\": 30}\n",
182 |     "print(my_dict[\"city\"])"
183 |    ]
184 |   },
185 |   {
186 |    "cell_type": "code",
187 |    "execution_count": 30,
188 |    "id": "b076aeb0",
189 |    "metadata": {},
190 |    "outputs": [
191 |     {
192 |      "ename": "ImportError",
193 |      "evalue": "cannot import name 'load_adigits' from 'sklearn.datasets' (C:\\Users\\redam\\AppData\\Local\\Programs\\Python\\Python310\\lib\\site-packages\\sklearn\\datasets\\__init__.py)",
194 |      "output_type": "error",
195 |      "traceback": [
196 |       "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
197 |       "\u001b[1;31mImportError\u001b[0m                               Traceback (most recent call last)",
198 |       "Cell \u001b[1;32mIn[30], line 2\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;66;03m# load Digits Dataset from sklearn\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01msklearn\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdatasets\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m load_adigits\n\u001b[0;32m      3\u001b[0m digits \u001b[38;5;241m=\u001b[39m load_adigits()\n",
199 |       "\u001b[1;31mImportError\u001b[0m: cannot import name 'load_adigits' from 'sklearn.datasets' (C:\\Users\\redam\\AppData\\Local\\Programs\\Python\\Python310\\lib\\site-packages\\sklearn\\datasets\\__init__.py)"
200 |      ]
201 |     }
202 |    ],
203 |    "source": [
204 |     "# load Digits Dataset from sklearn\n",
205 |     "from sklearn.datasets import load_adigits\n",
206 |     "digits = load_adigits()\n",
207 |     "\n"
208 |    ]
209 |   },
210 |   {
211 |    "cell_type": "code",
212 |    "execution_count": null,
213 |    "id": "bf5758fd",
214 |    "metadata": {},
215 |    "outputs": [],
216 |    "source": []
217 |   }
218 |  ],
219 |  "metadata": {
220 |   "kernelspec": {
221 |    "display_name": "Python 3 (ipykernel)",
222 |    "language": "python",
223 |    "name": "python3"
224 |   },
225 |   "language_info": {
226 |    "codemirror_mode": {
227 |     "name": "ipython",
228 |     "version": 3
229 |    },
230 |    "file_extension": ".py",
231 |    "mimetype": "text/x-python",
232 |    "name": "python",
233 |    "nbconvert_exporter": "python",
234 |    "pygments_lexer": "ipython3",
235 |    "version": "3.10.11"
236 |   }
237 |  },
238 |  "nbformat": 4,
239 |  "nbformat_minor": 5
240 | }
241 | 


--------------------------------------------------------------------------------
/examples/sklearn_digits.ipynb:
--------------------------------------------------------------------------------
 1 | {
 2 |  "cells": [
 3 |   {
 4 |    "cell_type": "code",
 5 |    "execution_count": 1,
 6 |    "id": "1114df89",
 7 |    "metadata": {},
 8 |    "outputs": [],
 9 |    "source": [
10 |     "# load Digits Dataset from sklearn\n"
11 |    ]
12 |   },
13 |   {
14 |    "cell_type": "markdown",
15 |    "id": "bdc6d694",
16 |    "metadata": {},
17 |    "source": [
18 |     "Print to show there are 1797 images (8 by 8 images for a dimensionality of 64)\n",
19 |     "\n",
20 |     "Print to show there are 1797 labels (integers from 0–9)"
21 |    ]
22 |   },
23 |   {
24 |    "cell_type": "code",
25 |    "execution_count": null,
26 |    "id": "c164026e",
27 |    "metadata": {},
28 |    "outputs": [],
29 |    "source": []
30 |   },
31 |   {
32 |    "cell_type": "markdown",
33 |    "id": "f3c71c94",
34 |    "metadata": {},
35 |    "source": [
36 |     "Showing the Images and the Labels (Digits Dataset)\n",
37 |     "\n",
38 |     "This section is really just to show what the images and labels look like. It usually helps to visualize your data to see what you are working with.\n"
39 |    ]
40 |   },
41 |   {
42 |    "cell_type": "code",
43 |    "execution_count": null,
44 |    "id": "20657b9d",
45 |    "metadata": {},
46 |    "outputs": [],
47 |    "source": []
48 |   }
49 |  ],
50 |  "metadata": {
51 |   "kernelspec": {
52 |    "display_name": "Python 3 (ipykernel)",
53 |    "language": "python",
54 |    "name": "python3"
55 |   },
56 |   "language_info": {
57 |    "codemirror_mode": {
58 |     "name": "ipython",
59 |     "version": 3
60 |    },
61 |    "file_extension": ".py",
62 |    "mimetype": "text/x-python",
63 |    "name": "python",
64 |    "nbconvert_exporter": "python",
65 |    "pygments_lexer": "ipython3",
66 |    "version": "3.8.10"
67 |   }
68 |  },
69 |  "nbformat": 4,
70 |  "nbformat_minor": 5
71 | }
72 | 


--------------------------------------------------------------------------------
/examples/starcoder_gpu_usage.ipynb:
--------------------------------------------------------------------------------
 1 | {
 2 |  "cells": [
 3 |   {
 4 |    "cell_type": "code",
 5 |    "execution_count": null,
 6 |    "id": "3aa18388",
 7 |    "metadata": {},
 8 |    "outputs": [],
 9 |    "source": [
10 |     "import pandas as pd\n",
11 |     "import numpy as np\n",
12 |     "import matplotlib.pyplot as plt"
13 |    ]
14 |   },
15 |   {
16 |    "cell_type": "markdown",
17 |    "id": "50ed5565",
18 |    "metadata": {},
19 |    "source": [
20 |     "read the csv usage_df.csv"
21 |    ]
22 |   },
23 |   {
24 |    "cell_type": "code",
25 |    "execution_count": null,
26 |    "id": "0997b28d",
27 |    "metadata": {},
28 |    "outputs": [],
29 |    "source": []
30 |   },
31 |   {
32 |    "cell_type": "markdown",
33 |    "id": "25867be0",
34 |    "metadata": {},
35 |    "source": [
36 |     "plot the daily usage of month 2022-03"
37 |    ]
38 |   },
39 |   {
40 |    "cell_type": "code",
41 |    "execution_count": null,
42 |    "id": "e7fed4a1",
43 |    "metadata": {},
44 |    "outputs": [],
45 |    "source": []
46 |   },
47 |   {
48 |    "cell_type": "markdown",
49 |    "id": "9dd0a765",
50 |    "metadata": {},
51 |    "source": [
52 |     "Calculate the average monthly usage and plot the data"
53 |    ]
54 |   },
55 |   {
56 |    "cell_type": "code",
57 |    "execution_count": null,
58 |    "id": "e0e304b0",
59 |    "metadata": {},
60 |    "outputs": [],
61 |    "source": []
62 |   },
63 |   {
64 |    "cell_type": "code",
65 |    "execution_count": null,
66 |    "id": "e7e4b2e2",
67 |    "metadata": {},
68 |    "outputs": [],
69 |    "source": []
70 |   }
71 |  ],
72 |  "metadata": {
73 |   "kernelspec": {
74 |    "display_name": "Python 3 (ipykernel)",
75 |    "language": "python",
76 |    "name": "python3"
77 |   },
78 |   "language_info": {
79 |    "codemirror_mode": {
80 |     "name": "ipython",
81 |     "version": 3
82 |    },
83 |    "file_extension": ".py",
84 |    "mimetype": "text/x-python",
85 |    "name": "python",
86 |    "nbconvert_exporter": "python",
87 |    "pygments_lexer": "ipython3",
88 |    "version": "3.8.10"
89 |   }
90 |  },
91 |  "nbformat": 4,
92 |  "nbformat_minor": 5
93 | }
94 | 


--------------------------------------------------------------------------------
/icons/flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bigcode-project/jupytercoder/a2e0aafaad4ce276876526b97023d8eee2cc12a7/icons/flow.png


--------------------------------------------------------------------------------
/icons/flow_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bigcode-project/jupytercoder/a2e0aafaad4ce276876526b97023d8eee2cc12a7/icons/flow_128.png


--------------------------------------------------------------------------------
/icons/flow_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bigcode-project/jupytercoder/a2e0aafaad4ce276876526b97023d8eee2cc12a7/icons/flow_256.png


--------------------------------------------------------------------------------
/icons/flow_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bigcode-project/jupytercoder/a2e0aafaad4ce276876526b97023d8eee2cc12a7/icons/flow_64.png


--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "manifest_version": 3,
 3 |   "name": "Jupyter Coder",
 4 |   "version": "0.2.0.0",
 5 |   "description": "Capture data in Jupyter Notebook cells and send to a remote server.",
 6 |   "icons": {
 7 |     "32": "icons/flow_64.png",
 8 |     "48": "icons/flow_128.png",
 9 |     "128": "icons/flow_256.png"
10 |   },
11 |   "permissions": [
12 |     "storage"
13 |   ],
14 |   "action": {
15 |     "default_popup": "options/options.html"
16 |   },
17 |   "background": {
18 |     "service_worker": "background.js"
19 |   },
20 |   "content_scripts": [
21 |     {
22 |       "matches": [
23 |         "*://*.jupyter.org/*",
24 |         "*://localhost:*/*",
25 |         "*://127.0.0.1:*/*"
26 |       ],
27 |       "js": [
28 |         "src/api.js",
29 |         "src/state.js",
30 |         "src/preferences.js",
31 |         "src/utility.js",
32 |         "src/animation.js",
33 |         "src/diff.js",
34 |         "src/content.js"
35 |       ],
36 |       "css": [
37 |         "options/styles.css"
38 |       ],
39 |       "run_at": "document_end"
40 |     }
41 |   ]
42 | }


--------------------------------------------------------------------------------
/options/options.html:
--------------------------------------------------------------------------------
  1 | 
  2 | 
  3 | 
  4 | 
  5 |   
157 | 
158 | 
159 | 
160 | 
161 |   
162 |
163 | Jupyter Coder 164 |
165 |
166 |
BigCode Url:
167 |
168 |
Huggingface Access Token:
169 |
170 |
171 | 172 |
173 |
OpenAI API Key:
174 |
175 | 176 | 181 | 182 |
183 | 184 | 185 |
186 | 187 | 191 | 192 | 193 | 197 |
198 | 199 | 200 | 201 |
202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /options/options.js: -------------------------------------------------------------------------------- 1 | // 获取所有的单选框 2 | const radioButtons = document.querySelectorAll('input[type="radio"]'); 3 | const select = document.querySelector('select'); 4 | 5 | // 获取当前被选中的单选框的值 6 | function getSelectedRadioValue() { 7 | let selectedValue = null; 8 | 9 | radioButtons.forEach(radioButton => { 10 | if (radioButton.checked) { 11 | selectedValue = radioButton.id; 12 | } 13 | }); 14 | 15 | return selectedValue; 16 | } 17 | 18 | 19 | // Save the API key when the Save button is clicked 20 | document.getElementById("save").addEventListener("click", () => { 21 | chrome.storage.sync.set({ 22 | openAIKey: document.getElementById("apiKey").value, 23 | bigcodeServiceUrl: document.getElementById("bigcodeServiceUrl").value, 24 | huggingfaceAccessToken: document.getElementById("huggingfaceAccessToken").value, 25 | modelType: select.value, 26 | checkedMode: getSelectedRadioValue() 27 | }, () => { 28 | alert("API key saved."); 29 | }) 30 | }); 31 | 32 | chrome.storage.sync.get("openAIKey", (data) => { 33 | if (data.openAIKey) { 34 | document.getElementById("apiKey").value = data.openAIKey; 35 | } 36 | }); 37 | 38 | chrome.storage.sync.get("huggingfaceAccessToken", (data) => { 39 | if (data.huggingfaceAccessToken) { 40 | document.getElementById("huggingfaceAccessToken").value = data.huggingfaceAccessToken; 41 | } 42 | }); 43 | 44 | chrome.storage.sync.get("bigcodeServiceUrl", (data) => { 45 | if (data.bigcodeServiceUrl) { 46 | document.getElementById("bigcodeServiceUrl").value = data.bigcodeServiceUrl; 47 | } else { 48 | document.getElementById("bigcodeServiceUrl").value = "https://api-inference.huggingface.co/models/bigcode/starcoderbase/" 49 | } 50 | }); 51 | 52 | chrome.storage.sync.get("modelType", (data) => { 53 | if (data.modelType) { 54 | select.value = data.modelType 55 | } 56 | }) 57 | 58 | chrome.storage.sync.get("checkedMode", (data) => { 59 | if (data.checkedMode && data.checkedMode == "OpenAI") { 60 | document.getElementsByClassName('input-key')[0].classList.toggle("input-hidden") 61 | document.getElementsByClassName('input-key')[1].classList.toggle("input-hidden") 62 | 63 | var optionsElement = document.getElementsByName('options') 64 | if(optionsElement[0].checked){ 65 | optionsElement[0].checked = false 66 | }else{ 67 | optionsElement[0].checked = true 68 | } 69 | if(optionsElement[1].checked){ 70 | optionsElement[1].checked = false 71 | }else{ 72 | optionsElement[1].checked = true 73 | } 74 | } 75 | }) 76 | 77 | // 为每个单选框添加事件监听器 78 | radioButtons.forEach(radioButton => { 79 | radioButton.addEventListener('change', function (event) { 80 | if (event.target.checked) { 81 | document.getElementsByClassName('input-key')[0].classList.toggle("input-hidden") 82 | document.getElementsByClassName('input-key')[1].classList.toggle("input-hidden") 83 | } 84 | }); 85 | }); 86 | 87 | 88 | -------------------------------------------------------------------------------- /options/styles.css: -------------------------------------------------------------------------------- 1 | .greyed-suggestion { 2 | color: #888; 3 | } 4 | -------------------------------------------------------------------------------- /src/animation.js: -------------------------------------------------------------------------------- 1 | const animation = { 2 | /* 3 | Start requested animation 4 | 5 | Params: 6 | activeTextarea: Textarea in active cell 7 | 8 | Returns: list, len == 3 9 | 0. animationInterval: Animation interval, can be cleared using the 'clearInterval' function 10 | 1. animationElement: Animation dom element 11 | 2. activeCellElement: The parent dom of the current cell 12 | 13 | */ 14 | startWaitingAnimation(activeTextarea) { }, 15 | } 16 | 17 | 18 | 19 | // left animation css 20 | const loadCss = ` 21 | .before-content:before { 22 | content: ""; 23 | position: absolute; 24 | top: 5px; 25 | left: 10px; 26 | right: 0; 27 | bottom: 0; 28 | border: 3px solid rgba(0, 0, 0, 0.1); 29 | border-left-color: #000; 30 | border-radius: 50%; 31 | width: 15px; 32 | height: 15px; 33 | animation: spin 1s linear infinite; 34 | } 35 | 36 | @keyframes spin { 37 | to { 38 | transform: rotate(360deg); 39 | } 40 | } 41 | .paused:before { 42 | content: ""; 43 | position: absolute; 44 | top: 5px; 45 | left: 10px; 46 | right: 0; 47 | bottom: 0; 48 | border: 3px solid rgba(0, 0, 0, 0.1); 49 | border-radius: 50%; 50 | width: 15px; 51 | height: 15px; 52 | // animation: spin 1s linear infinite; 53 | border-left-color: red; 54 | } 55 | `; 56 | 57 | 58 | 59 | animation.startWaitingAnimation = (activeTextarea) => { 60 | const activeCellParentElement = activeTextarea.parentElement.parentElement.parentElement; 61 | 62 | // Create a new