From d59780f3897897b8598ce8be5c5e5e3f67cc2710 Mon Sep 17 00:00:00 2001 From: cjq <1401875340@qq.com> Date: Sun, 7 May 2023 20:29:35 +0800 Subject: [PATCH 01/13] add bigCode default url value --- options.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/options.js b/options.js index 87f6290..e323b05 100644 --- a/options.js +++ b/options.js @@ -44,6 +44,8 @@ chrome.storage.sync.get("huggingfaceApiKey", (data) => { chrome.storage.sync.get("otherService", (data) => { if (data.otherService) { document.getElementById("otherServiceUrl").value = data.otherService; + } else { + document.getElementById("otherServiceUrl").value = "https://api-inference.huggingface.co/models/bigcode/starcoder/" } }); From 500dd12c0782e42749bf8fd07bd42bcf4875acd7 Mon Sep 17 00:00:00 2001 From: ran Date: Sun, 7 May 2023 13:18:26 +0800 Subject: [PATCH 02/13] Modify bigcode default url --- options.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options.js b/options.js index e323b05..25f7ce6 100644 --- a/options.js +++ b/options.js @@ -45,7 +45,7 @@ chrome.storage.sync.get("otherService", (data) => { if (data.otherService) { document.getElementById("otherServiceUrl").value = data.otherService; } else { - document.getElementById("otherServiceUrl").value = "https://api-inference.huggingface.co/models/bigcode/starcoder/" + document.getElementById("otherServiceUrl").value = "https://api-inference.huggingface.co/models/bigcode/starcoderbase/" } }); From 72a38b95d09333b5f18e311dd6cd0bbd2347acc8 Mon Sep 17 00:00:00 2001 From: ran Date: Mon, 8 May 2023 17:04:24 +0800 Subject: [PATCH 03/13] add jupyter Event Listener --- content.js | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/content.js b/content.js index 29a9910..c54339a 100644 --- a/content.js +++ b/content.js @@ -404,9 +404,8 @@ const addFillCodeKeyListener = (event) => { }; -// Check if the current page is a Jupyter Notebook -if (document.querySelector('body.notebook_app')) { - + +const montedEventListener = ()=>{ document.addEventListener('keydown', async (event) => { // Check if the Ctrl + Space keys were pressed if (event.ctrlKey && event.code === 'Space') { @@ -419,7 +418,7 @@ if (document.querySelector('body.notebook_app')) { //Obtain the Textarea of the current input box const activeTextarea = document.activeElement; - + activeRequestTextarea = activeTextarea // Obtain the current input box (cell) from the Textarea of the current input box @@ -451,4 +450,34 @@ if (document.querySelector('body.notebook_app')) { } }); document.addEventListener('keydown', addFillCodeKeyListener); -} \ No newline at end of file +} + + +// Check if the current page is a Jupyter Notebook +if (document.querySelector('body.notebook_app')) { + montedEventListener() +} + +// Create a new MutationObserver, This object will listen for changes in elements in the DOM and execute callback functions when changes occur +const observer = new MutationObserver(function(mutations) { + + // In the callback function, use the forEach method to traverse the mutations array and obtain the attribute name attributeName of each mutated object's mutations. + // There is only one mutation in jupyterlab + mutations.forEach(function(mutation) { + + // If the attribute name is 'data jp theme name', + if (mutation.attributeName === "data-jp-theme-name") { + + // use the getAttribute method to obtain the value of the data jp theme name attribute of theelement and store it in the dataJpThemeName variable. + const dataJpThemeName = document.body.getAttribute("data-jp-theme-name"); + if(dataJpThemeName.indexOf("JupyterLab") != -1){ + montedEventListener() + } + + } + + }); +}); + +// Start monitoring attribute changes ofelements +observer.observe(document.body, { attributes: true }); From d8115bce7b11df708bffc12ab06ddce78f7860aa Mon Sep 17 00:00:00 2001 From: ran Date: Mon, 8 May 2023 11:01:47 +0800 Subject: [PATCH 04/13] add jupyterlab Event Listener --- content.js | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/content.js b/content.js index c54339a..65946f8 100644 --- a/content.js +++ b/content.js @@ -245,8 +245,10 @@ function getCellContentTextRequiredForOpenAI(activeCell) { function getCellContentTextRequiredForBigCode(activeCell) { - const cellElements = Array.from(document.querySelectorAll('.cell')); + const cellElements = Array.from(document.querySelectorAll(`.${currctJupyterModel.requiredClassName.cell}`)); + const activeCellIndex = cellElements.findIndex(cell => cell.contains(activeCell)); + // Check if there are at least 3 cells before the active cell let combinedContent = ""; @@ -257,6 +259,7 @@ function getCellContentTextRequiredForBigCode(activeCell) { return null } + TODO: "The following code needs to add 'leftContext' and 'rightContext'" // Iterate through the last 3 cells before the active cell const startIndex = activeCellIndex - 3 < 0 ? 0 : activeCellIndex - 3; @@ -264,11 +267,11 @@ function getCellContentTextRequiredForBigCode(activeCell) { for (let i = startIndex; i <= activeCellIndex; i++) { const cellElement = cellElements[i]; - if (cellElement.classList.contains('code_cell')) { + if (cellElement.classList.contains(currctJupyterModel.requiredClassName.verify)) { const code = extractTextFromCell(cellElement); combinedContent += `${code}`; - const outputElement = cellElement.querySelector('.output_subarea'); + const outputElement = cellElement.querySelector(`.${currctJupyterModel.requiredClassName.output}`); if (outputElement) { if (i !== activeCellIndex) { combinedContent += ``; @@ -309,6 +312,7 @@ function extractTextFromCell(cell) { return content_str; } + function removeJupyterOutput(str) { const jupyterOutput = ''; @@ -423,7 +427,7 @@ const montedEventListener = ()=>{ // Obtain the current input box (cell) from the Textarea of the current input box const activeCell = activeTextarea.parentElement.parentElement - + console.log(activeCell); // Retrieve the content of the active cell const code = await getCellContentText(activeCell); @@ -452,10 +456,34 @@ const montedEventListener = ()=>{ document.addEventListener('keydown', addFillCodeKeyListener); } +// Two options 'lab' and 'notebook' +let currctJupyterModel = {} + +const notebookModel = { + name: "notebook", + requiredClassName:{ + cell:"cell", + verify: "code_cell", + output: "output_subarea" + } +} + +const labModel = { + name: "lab", + requiredClassName:{ + cell:"jp-Notebook-cell", + verify: "jp-CodeCell", + output: "jp-Cell-outputWrapper" + } +} + + // Check if the current page is a Jupyter Notebook if (document.querySelector('body.notebook_app')) { montedEventListener() + jupyterModel = "notebook" + currctJupyterModel = notebookModel } // Create a new MutationObserver, This object will listen for changes in elements in the DOM and execute callback functions when changes occur @@ -472,6 +500,7 @@ const observer = new MutationObserver(function(mutations) { const dataJpThemeName = document.body.getAttribute("data-jp-theme-name"); if(dataJpThemeName.indexOf("JupyterLab") != -1){ montedEventListener() + currctJupyterModel = labModel } } From d32d4693f384002bd7a127839b30600c685d9e4e Mon Sep 17 00:00:00 2001 From: ran Date: Mon, 8 May 2023 11:08:55 +0800 Subject: [PATCH 05/13] renove log --- content.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/content.js b/content.js index 65946f8..75bb910 100644 --- a/content.js +++ b/content.js @@ -89,7 +89,7 @@ async function sendToOtherService(code) { } const prompt = code.replace(/\u200B/g, '') - console.log(JSON.stringify(prompt)); + const response = await fetch(url, { method: "POST", headers: { @@ -427,7 +427,7 @@ const montedEventListener = ()=>{ // Obtain the current input box (cell) from the Textarea of the current input box const activeCell = activeTextarea.parentElement.parentElement - console.log(activeCell); + // Retrieve the content of the active cell const code = await getCellContentText(activeCell); @@ -437,7 +437,7 @@ const montedEventListener = ()=>{ // Start Animation const [animationInterval, animationElement] = startWaitingAnimation(activeCell) isRequestInProgress = true - + const suggestion = await getCodeCompletion(code) if (suggestion) { @@ -482,12 +482,11 @@ const labModel = { // Check if the current page is a Jupyter Notebook if (document.querySelector('body.notebook_app')) { montedEventListener() - jupyterModel = "notebook" currctJupyterModel = notebookModel } // Create a new MutationObserver, This object will listen for changes in elements in the DOM and execute callback functions when changes occur -const observer = new MutationObserver(function(mutations) { +const bodyObserver = new MutationObserver(function(mutations) { // In the callback function, use the forEach method to traverse the mutations array and obtain the attribute name attributeName of each mutated object's mutations. // There is only one mutation in jupyterlab @@ -509,4 +508,4 @@ const observer = new MutationObserver(function(mutations) { }); // Start monitoring attribute changes ofelements -observer.observe(document.body, { attributes: true }); +bodyObserver.observe(document.body, { attributes: true }); From e4de8e353dba0a88e009fd0a6890126c2fb411bf Mon Sep 17 00:00:00 2001 From: ran Date: Mon, 8 May 2023 19:46:25 +0800 Subject: [PATCH 06/13] Separate code and textSeparate code and text --- content.js | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/content.js b/content.js index 75bb910..87c3ea8 100644 --- a/content.js +++ b/content.js @@ -268,7 +268,7 @@ function getCellContentTextRequiredForBigCode(activeCell) { const cellElement = cellElements[i]; if (cellElement.classList.contains(currctJupyterModel.requiredClassName.verify)) { - const code = extractTextFromCell(cellElement); + const code = extractTextFromCodeCell(cellElement); combinedContent += `${code}`; const outputElement = cellElement.querySelector(`.${currctJupyterModel.requiredClassName.output}`); @@ -278,8 +278,8 @@ function getCellContentTextRequiredForBigCode(activeCell) { combinedContent += outputElement.textContent; } } - } else if (cellElement.classList.contains('text_cell')) { - const text = extractTextFromCell(cellElement); + } else if (cellElement.classList.contains(currctJupyterModel.requiredClassName.text)) { + const text = extractTextFromTextCell(cellElement); combinedContent += `${text}`; } } @@ -299,9 +299,23 @@ async function getCellContentText(activeCell){ } - -function extractTextFromCell(cell) { +function extractTextFromCodeCell(cell){ const codeMirrorLines = cell.querySelectorAll('.CodeMirror-code pre'); + + const content = []; + + codeMirrorLines.forEach((line) => { + content.push(line.textContent); + }); + const content_str = content.join('\n'); + + return content_str; +} + + +function extractTextFromTextCell(cell) { + const codeMirrorLines = cell.querySelectorAll(`.${currctJupyterModel.requiredClassName.textOutput}`); + const content = []; codeMirrorLines.forEach((line) => { @@ -313,6 +327,7 @@ function extractTextFromCell(cell) { } + function removeJupyterOutput(str) { const jupyterOutput = ''; @@ -432,7 +447,7 @@ const montedEventListener = ()=>{ const code = await getCellContentText(activeCell); if (!code) return; - + console.log("code", JSON.stringify(code)); if (activeCell) { // Start Animation const [animationInterval, animationElement] = startWaitingAnimation(activeCell) @@ -464,7 +479,9 @@ const notebookModel = { requiredClassName:{ cell:"cell", verify: "code_cell", - output: "output_subarea" + output: "output_subarea", + text: "text_cell", + textOutput: "text_cell_render" } } @@ -473,7 +490,9 @@ const labModel = { requiredClassName:{ cell:"jp-Notebook-cell", verify: "jp-CodeCell", - output: "jp-Cell-outputWrapper" + output: "jp-Cell-outputWrapper", + text: "jp-MarkdownCell", + textOutput: "jp-RenderedMarkdown" } } From 5465aa28789cf2325802bb9bf8557205289ff1b6 Mon Sep 17 00:00:00 2001 From: ran Date: Mon, 8 May 2023 19:52:49 +0800 Subject: [PATCH 07/13] make the code easier to understand --- content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content.js b/content.js index 87c3ea8..846f7ea 100644 --- a/content.js +++ b/content.js @@ -314,7 +314,7 @@ function extractTextFromCodeCell(cell){ function extractTextFromTextCell(cell) { - const codeMirrorLines = cell.querySelectorAll(`.${currctJupyterModel.requiredClassName.textOutput}`); + const codeMirrorLines = cell.querySelectorAll(`.${currctJupyterModel.requiredClassName.textOutput} p`); const content = []; From c9080dbdb6b739d47d22df6709cc3fbfe4525283 Mon Sep 17 00:00:00 2001 From: ran Date: Mon, 8 May 2023 19:54:41 +0800 Subject: [PATCH 08/13] Improve openai --- content.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content.js b/content.js index 846f7ea..6eecd8f 100644 --- a/content.js +++ b/content.js @@ -213,7 +213,7 @@ const getActiveCellPointerCode = (activeCell) => { function getCellContentTextRequiredForOpenAI(activeCell) { - const cellElements = Array.from(document.querySelectorAll('.cell')); + const cellElements = Array.from(document.querySelectorAll(`.${currctJupyterModel.requiredClassName.cell}`)); const activeCellIndex = cellElements.findIndex(cell => cell.contains(activeCell)); // Check if there are at least 3 cells before the active cell let codeContent = ""; @@ -233,8 +233,8 @@ function getCellContentTextRequiredForOpenAI(activeCell) { break }else{ const cellElement = cellElements[i]; - if (cellElement.classList.contains('code_cell')) { - codeContent += extractTextFromCell(cellElement); + if (cellElement.classList.contains(currctJupyterModel.requiredClassName.verify)) { + codeContent += extractTextFromCodeCell(cellElement); } } codeContent += "\n" From 230d317d87049dd2a9ff6b8fac0ee29da0059a2b Mon Sep 17 00:00:00 2001 From: ran Date: Mon, 8 May 2023 19:56:32 +0800 Subject: [PATCH 09/13] Make PR look not so big --- content.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/content.js b/content.js index 6eecd8f..1c8d7ec 100644 --- a/content.js +++ b/content.js @@ -246,9 +246,7 @@ function getCellContentTextRequiredForOpenAI(activeCell) { function getCellContentTextRequiredForBigCode(activeCell) { const cellElements = Array.from(document.querySelectorAll(`.${currctJupyterModel.requiredClassName.cell}`)); - const activeCellIndex = cellElements.findIndex(cell => cell.contains(activeCell)); - // Check if there are at least 3 cells before the active cell let combinedContent = ""; From d29df05fb4552f320706ee3fcfd974fb4fd1a6cc Mon Sep 17 00:00:00 2001 From: ran Date: Mon, 8 May 2023 19:58:31 +0800 Subject: [PATCH 10/13] Formatted the code I wrote --- content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content.js b/content.js index 1c8d7ec..e7ec46c 100644 --- a/content.js +++ b/content.js @@ -422,7 +422,7 @@ const addFillCodeKeyListener = (event) => { -const montedEventListener = ()=>{ +const montedEventListener = () => { document.addEventListener('keydown', async (event) => { // Check if the Ctrl + Space keys were pressed if (event.ctrlKey && event.code === 'Space') { From b1c612c08ec141047041bfc2c04bd5e1232ba125 Mon Sep 17 00:00:00 2001 From: ran Date: Mon, 8 May 2023 20:08:12 +0800 Subject: [PATCH 11/13] remove log --- content.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/content.js b/content.js index e7ec46c..076e46b 100644 --- a/content.js +++ b/content.js @@ -66,6 +66,7 @@ async function sendToOpenAI(prompt) { } let suggestion = data.choices && data.choices[0] && data.choices[0].text; + // Remove invisible characters suggestion = suggestion.replace(/\u200B/g, ''); @@ -445,14 +446,14 @@ const montedEventListener = () => { const code = await getCellContentText(activeCell); if (!code) return; - console.log("code", JSON.stringify(code)); + if (activeCell) { // Start Animation const [animationInterval, animationElement] = startWaitingAnimation(activeCell) isRequestInProgress = true const suggestion = await getCodeCompletion(code) - + if (suggestion) { clearInterval(animationInterval) isRequestSuccessful = true From 84933454767f19e959291631525fc33c5a410811 Mon Sep 17 00:00:00 2001 From: ran Date: Tue, 9 May 2023 16:09:59 +0800 Subject: [PATCH 12/13] update ui --- content.js | 175 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 117 insertions(+), 58 deletions(-) diff --git a/content.js b/content.js index 076e46b..05623e8 100644 --- a/content.js +++ b/content.js @@ -337,10 +337,56 @@ function removeJupyterOutput(str) { return str; } +// left animation css +const loadCss = ` + .before-content:before { + content: ""; + position: absolute; + top: 5px; + left: 10px; + right: 0; + bottom: 0; + border: 3px solid rgba(0, 0, 0, 0.1); + border-left-color: #000; + border-radius: 50%; + width: 15px; + height: 15px; + animation: spin 1s linear infinite; + } + + @keyframes spin { + to { + transform: rotate(360deg); + } + } + .paused:before { + content: ""; + position: absolute; + top: 5px; + left: 10px; + right: 0; + bottom: 0; + border: 3px solid rgba(0, 0, 0, 0.1); + border-radius: 50%; + width: 15px; + height: 15px; + // animation: spin 1s linear infinite; + border-left-color: red; + } +`; + // 开始等待动画,有30s等待时间,如果等待时间过了,出现“error”字体,返回两个值如下,接收:"const [animationInterval, animationElement] = startWaitingAnimation(activeCall)" // 1. animationInterval(interval, 动画计时器),可使用clearInterval(animationInterval)消除动画, 每次请求完毕必须要关掉 // 2. animationElement (dom, 动画字体节点), animationElement.innerHTML = xxx 来赋值 const startWaitingAnimation = (activeCell) => { + const inputElement = activeCell.parentElement.parentElement.parentElement; + + // 创建新的