diff --git a/.gitignore b/.gitignore index 3b925b8..3e834f4 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,6 @@ Model_training/AnswerAwareQG/fine_tuned_t5_tokenizer_aaqg_2 backend/models backend/tokenizers backend/sample_input.py -extension/pdfjs-3.9.179-dist \ No newline at end of file +extension/pdfjs-3.9.179-dist +backend/credentials.json +backend/token.json \ No newline at end of file diff --git a/backend/__pycache__/extract_keywords.cpython-310.pyc b/backend/__pycache__/extract_keywords.cpython-310.pyc new file mode 100644 index 0000000..1bfa4b3 Binary files /dev/null and b/backend/__pycache__/extract_keywords.cpython-310.pyc differ diff --git a/backend/__pycache__/find_sentances.cpython-310.pyc b/backend/__pycache__/find_sentances.cpython-310.pyc new file mode 100644 index 0000000..f317f52 Binary files /dev/null and b/backend/__pycache__/find_sentances.cpython-310.pyc differ diff --git a/backend/server.py b/backend/server.py index 0d4abe0..3ac70ff 100644 --- a/backend/server.py +++ b/backend/server.py @@ -1,114 +1,192 @@ import http.server +import json import socketserver -import urllib.parse import torch +from models.modelC.distractor_generator import DistractorGenerator from transformers import T5ForConditionalGeneration, T5Tokenizer, pipeline -from transformers import AutoTokenizer, AutoModelForSeq2SeqLM -import json -IP='127.0.0.1' -PORT=8000 +import webbrowser -def summarize(text): - summarizer=pipeline('summarization') - return summarizer(text,max_length=110)[0]['summary_text'] +from apiclient import discovery +from httplib2 import Http +from oauth2client import client, file, tools + +IP = "127.0.0.1" +PORT = 8000 -def generate_question(context,answer,model_path, tokenizer_path): +def summarize(text): + summarizer = pipeline("summarization") + return summarizer(text, max_length=110)[0]["summary_text"] + +def generate_question(context, answer, model_path, tokenizer_path): model = T5ForConditionalGeneration.from_pretrained(model_path) tokenizer = T5Tokenizer.from_pretrained(tokenizer_path) - device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model.to(device) - input_text=f'answer: {answer} context: {context}' + input_text = f"answer: {answer} context: {context}" - inputs=tokenizer.encode_plus( + inputs = tokenizer.encode_plus( input_text, - padding='max_length', + padding="max_length", truncation=True, max_length=512, - return_tensors='pt' + return_tensors="pt", ) - input_ids=inputs['input_ids'].to(device) - attention_mask=inputs['attention_mask'].to(device) + input_ids = inputs["input_ids"].to(device) + attention_mask = inputs["attention_mask"].to(device) with torch.no_grad(): - output=model.generate( - input_ids=input_ids, - attention_mask=attention_mask, - max_length=32 + output = model.generate( + input_ids=input_ids, attention_mask=attention_mask, max_length=32 ) generated_question = tokenizer.decode(output[0], skip_special_tokens=True) return generated_question -def generate_keyphrases(abstract, model_path,tokenizer_path): - device= torch.device('cuda' if torch.cuda.is_available() else 'cpu') + +def generate_keyphrases(abstract, model_path, tokenizer_path): + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = T5ForConditionalGeneration.from_pretrained(model_path) tokenizer = T5Tokenizer.from_pretrained(tokenizer_path) model.to(device) # tokenizer.to(device) - input_text=f'detect keyword: abstract: {abstract}' - input_ids=tokenizer.encode(input_text, truncation=True,padding='max_length',max_length=512,return_tensors='pt').to(device) - output=model.generate(input_ids) - keyphrases= tokenizer.decode(output[0],skip_special_tokens=True).split(',') - return [x.strip() for x in keyphrases if x != ''] - -def generate_qa(text): - - # text_summary=summarize(text) - text_summary=text - - - modelA, modelB='./models/modelA','./models/modelB' - # tokenizerA, tokenizerB= './tokenizers/tokenizerA', './tokenizers/tokenizerB' - tokenizerA, tokenizerB= 't5-base', 't5-base' - - answers=generate_keyphrases(text_summary, modelA, tokenizerA) - - qa={} - for answer in answers: - question= generate_question(text_summary, answer, modelB, tokenizerB) - qa[question]=answer - - return qa - - - + input_text = f"detect keyword: abstract: {abstract}" + input_ids = tokenizer.encode( + input_text, + truncation=True, + padding="max_length", + max_length=512, + return_tensors="pt", + ).to(device) + output = model.generate(input_ids) + keyphrases = tokenizer.decode(output[0], skip_special_tokens=True).split(",") + return [x.strip() for x in keyphrases if x != ""] + + +def generate_qa(self, text, question_type): + modelA, modelB = "./models/modelA", "./models/modelB" + tokenizerA, tokenizerB = "t5-base", "t5-base" + if question_type == "text": + text_summary = text + answers = generate_keyphrases(text_summary, modelA, tokenizerA) + qa = {} + for answer in answers: + question = generate_question(text_summary, answer, modelB, tokenizerB) + qa[question] = answer + + return qa + if question_type == "form": + text_summary = text + answers = generate_keyphrases(text_summary, modelA, tokenizerA) + qa = {} + for answer in answers: + question = generate_question(text_summary, answer, modelB, tokenizerB) + qa[question] = answer + SCOPES = "https://www.googleapis.com/auth/forms.body" + DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" + + store = file.Storage("token.json") + creds = None + if not creds or creds.invalid: + flow = client.flow_from_clientsecrets("credentials.json", SCOPES) + creds = tools.run_flow(flow, store) + + form_service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, + ) + NEW_FORM = { + "info": { + "title": "EduAid form", + } + } + requests_list = [] + + for index, (question, answer) in enumerate(qa.items()): + request = { + "createItem": { + "item": { + "title": question, + "questionItem": { + "question": { + "required": True, + "textQuestion": {}, + } + }, + }, + "location": {"index": index}, + } + } + requests_list.append(request) + + NEW_QUESTION = {"requests": requests_list} + + result = form_service.forms().create(body=NEW_FORM).execute() + question_setting = ( + form_service.forms() + .batchUpdate(formId=result["formId"], body=NEW_QUESTION) + .execute() + ) + edit_url = result["responderUri"] + qa["edit_url"] = edit_url + webbrowser.open_new_tab( + "https://docs.google.com/forms/d/" + result["formId"] + "/edit" + ) + return qa class QARequestHandler(http.server.BaseHTTPRequestHandler): - def do_POST(self): + def do_OPTIONS(self): + self.send_response(200) + self.send_header("Access-Control-Allow-Origin", "*") + self.send_header("Access-Control-Allow-Methods", "POST, OPTIONS") + self.send_header("Access-Control-Allow-Headers", "Content-Type") + self.send_header("Content-Length", "0") + self.end_headers() + def do_POST(self): self.send_response(200) self.send_header("Content-type", "text/plain") self.end_headers() - content_length=int(self.headers["Content-Length"]) - post_data=self.rfile.read(content_length).decode('utf-8') - - # parsed_data=urllib.parse.parse_qs(post_data) + content_length = int(self.headers["Content-Length"]) + post_data = self.rfile.read(content_length).decode("utf-8") parsed_data = json.loads(post_data) + if self.path == "/": + input_text = parsed_data.get("input_text") + question_type = self.headers.get("Question-Type", "text") + qa = generate_qa(self, input_text, question_type) - input_text=parsed_data.get('input_text') + self.wfile.write(json.dumps(qa).encode("utf-8")) + self.wfile.flush() - qa=generate_qa(input_text) +class CustomRequestHandler(QARequestHandler): + def __init__(self, *args, **kwargs): + self.distractor_generator = kwargs.pop("distractor_generator") + super().__init__(*args, **kwargs) - self.wfile.write(json.dumps(qa).encode("utf-8")) - self.wfile.flush() - def main(): - with socketserver.TCPServer((IP, PORT), QARequestHandler) as server: - print(f'Server started at http://{IP}:{PORT}') + distractor_generator = DistractorGenerator() + with socketserver.TCPServer( + (IP, PORT), + lambda x, y, z: CustomRequestHandler( + x, y, z, distractor_generator=distractor_generator + ), + ) as server: + print(f"Server started at http://{IP}:{PORT}") server.serve_forever() -if __name__=="__main__": - main() - \ No newline at end of file +if __name__ == "__main__": + main() diff --git a/extension/html/text_input.html b/extension/html/text_input.html index f62c2da..56a1a8b 100644 --- a/extension/html/text_input.html +++ b/extension/html/text_input.html @@ -2,8 +2,6 @@ EduAid: Text Input - @@ -24,11 +22,10 @@

Generate QnA

- +
- diff --git a/extension/js/question_generation.js b/extension/js/question_generation.js index 58bdd94..4f80563 100644 --- a/extension/js/question_generation.js +++ b/extension/js/question_generation.js @@ -1,56 +1,61 @@ -document.addEventListener("DOMContentLoaded", function(){ - const saveButton= document.getElementById("save-button"); - const backButton= document.getElementById("back-button"); - const viewQuestionsButton = document.getElementById("view-questions-button"); - const qaPairs=JSON.parse(localStorage.getItem("qaPairs")); - const modalClose= document.querySelector("[data-close-modal]"); - const modal=document.querySelector("[data-modal]"); - - - viewQuestionsButton.addEventListener("click", function(){ - const modalQuestionList = document.getElementById("modal-question-list"); - modalQuestionList.innerHTML = ""; // Clear previous content - - for (const [question, answer] of Object.entries(qaPairs)) { - const questionElement = document.createElement("li"); - questionElement.textContent = `Question: ${question}, Answer: ${answer}`; - modalQuestionList.appendChild(questionElement) +document.addEventListener("DOMContentLoaded", function () { + const saveButton = document.getElementById("save-button"); + const backButton = document.getElementById("back-button"); + const viewQuestionsButton = document.getElementById("view-questions-button"); + const qaPairs = JSON.parse(localStorage.getItem("qaPairs")); + const modalClose = document.querySelector("[data-close-modal]"); + const modal = document.querySelector("[data-modal]"); + + viewQuestionsButton.addEventListener("click", function () { + const modalQuestionList = document.getElementById("modal-question-list"); + modalQuestionList.innerHTML = ""; + + for (const [question, answer] of Object.entries(qaPairs)) { + const questionElement = document.createElement("li"); + if (question.includes("Options:")) { + const options = question.split("Options: ")[1].split(", "); + const formattedOptions = options.map( + (opt, index) => `${String.fromCharCode(97 + index)}) ${opt}` + ); + questionElement.textContent = `Question: ${ + question.split(" Options:")[0] + }\n${formattedOptions.join("\n")}`; + } else { + questionElement.textContent = `Question: ${question}\n\nAnswer: ${answer}\n`; } - modal.showModal(); - }); - modalClose.addEventListener("click", function(){ - modal.close(); - }); - saveButton.addEventListener("click", async function(){ - let textContent= "EduAid Generated QnA:\n\n"; + modalQuestionList.appendChild(questionElement); + } + modal.showModal(); + }); - for (const [question,answer] of Object.entries(qaPairs)){ - textContent+= `Question: ${question}\nAnswer: ${answer}\n\n`; - } - const blob = new Blob([textContent], { type: "text/plain" }); - - // Create a URL for the Blob - const blobUrl = URL.createObjectURL(blob); - - // Create a temporary element to trigger the download - const downloadLink = document.createElement("a"); - downloadLink.href = blobUrl; - downloadLink.download = "questions_and_answers.txt"; - downloadLink.style.display = "none"; - - // Append the element to the document - document.body.appendChild(downloadLink); - - // Simulate a click on the link to trigger the download - downloadLink.click(); - - // Clean up: remove the temporary element and revoke the Blob URL - document.body.removeChild(downloadLink); - URL.revokeObjectURL(blobUrl); - }); - - backButton.addEventListener("click", function(){ - window.location.href="../html/text_input.html" - }); -}); \ No newline at end of file + modalClose.addEventListener("click", function () { + modal.close(); + }); + saveButton.addEventListener("click", async function () { + let textContent = "EduAid Generated QnA:\n\n"; + + for (const [question, answer] of Object.entries(qaPairs)) { + textContent += `Question: ${question}\nAnswer: ${answer}\n\n`; + } + const blob = new Blob([textContent], { type: "text/plain" }); + + const blobUrl = URL.createObjectURL(blob); + + const downloadLink = document.createElement("a"); + downloadLink.href = blobUrl; + downloadLink.download = "questions_and_answers.txt"; + downloadLink.style.display = "none"; + + document.body.appendChild(downloadLink); + + downloadLink.click(); + + document.body.removeChild(downloadLink); + URL.revokeObjectURL(blobUrl); + }); + + backButton.addEventListener("click", function () { + window.location.href = "../html/text_input.html"; + }); +}); diff --git a/extension/js/text_input.js b/extension/js/text_input.js index bf65bb9..6489fe6 100644 --- a/extension/js/text_input.js +++ b/extension/js/text_input.js @@ -1,79 +1,93 @@ document.addEventListener("DOMContentLoaded", function () { - const nextButton = document.getElementById("next-button"); - const backButton = document.getElementById("back-button"); - const textInput = document.getElementById("text-input"); - const fileInput = document.getElementById("file-upload"); - const loadingScreen = document.getElementById("loading-screen"); - - - fileInput.addEventListener("change", async function () { - const file = fileInput.files[0]; - if (file) { - const fileReader = new FileReader(); - fileReader.onload = async function (event) { - const pdfData = new Uint8Array(event.target.result); - const pdf = await pdfjsLib.getDocument({ data: pdfData }).promise; - let pdfText = ""; - - for (let i = 1; i <= pdf.numPages; i++) { - const page = await pdf.getPage(i); - const pageText = await page.getTextContent(); - const pageStrings = pageText.items.map(item => item.str); - pdfText += pageStrings.join(" "); - } - - textInput.value = pdfText; - }; - fileReader.readAsArrayBuffer(file); - } - }); + const nextButton = document.getElementById("next-button"); + const mcqButton = document.getElementById("mcq-button"); + const backButton = document.getElementById("back-button"); + const textInput = document.getElementById("text-input"); + const fileInput = document.getElementById("file-upload"); + const createForm = document.getElementById("google-form-button"); + const loadingScreen = document.getElementById("loading-screen"); - nextButton.addEventListener("click", async function () { - loadingScreen.style.display = "flex" - const inputText = textInput.value; - - if (inputText.trim() === "" && fileInput.files.length > 0) { - const file = fileInput.files[0]; - const fileReader = new FileReader(); - fileReader.onload = async function (event) { - const uploadedPdfData = new Uint8Array(event.target.result); - await sendToBackend(uploadedPdfData,"pdf"); - }; - fileReader.readAsArrayBuffer(file); - } else if (inputText.trim() !== "") { - await sendToBackend(inputText,"text"); - } else { - alert("Please enter text or upload a PDF file."); - loadingScreen.style.display = "none"; - } - }); - - backButton.addEventListener("click", function () { - window.location.href = "../html/index.html"; + fileInput.addEventListener("change", async function () { + const file = fileInput.files[0]; + if (file) { + const fileReader = new FileReader(); + fileReader.onload = async function (event) { + const pdfData = new Uint8Array(event.target.result); + const pdf = await pdfjsLib.getDocument({ data: pdfData }).promise; + let pdfText = ""; + + for (let i = 1; i <= pdf.numPages; i++) { + const page = await pdf.getPage(i); + const pageText = await page.getTextContent(); + const pageStrings = pageText.items.map((item) => item.str); + pdfText += pageStrings.join(" "); + } + + textInput.value = pdfText; + }; + fileReader.readAsArrayBuffer(file); + } + }); + + nextButton.addEventListener("click", async function () { + await generateQuestion("text"); + }); + + createForm.addEventListener("click", async function () { + await generateQuestion("form"); + }); + + backButton.addEventListener("click", function () { + window.location.href = "../html/index.html"; + }); + document + .getElementById("generate-quiz-button") + .addEventListener("click", function () { + window.location.href = "../html/openai.html"; }); - - async function sendToBackend(data, dataType) { - let formData; - let contentType; - formData = JSON.stringify({ 'input_text': data }); - contentType = "application/json; charset=UTF-8"; - - const response = await fetch("http://127.0.0.1:8000", { - method: "POST", - body: formData, - headers: { - "Content-Type": contentType, - }, - }); - - if (response.ok) { - const responseData = await response.json(); - // console.log("Response data:\n"+responseData); - localStorage.setItem("qaPairs", JSON.stringify(responseData)); - window.location.href = "../html/question_generation.html"; - } else { - console.error("Backend request failed."); - } + + async function generateQuestion(questionType) { + loadingScreen.style.display = "flex"; + const inputText = textInput.value; + + if (inputText.trim() === "" && fileInput.files.length > 0) { + const file = fileInput.files[0]; + const fileReader = new FileReader(); + fileReader.onload = async function (event) { + const uploadedPdfData = new Uint8Array(event.target.result); + await sendToBackend(uploadedPdfData, "pdf", questionType); + }; + fileReader.readAsArrayBuffer(file); + } else if (inputText.trim() !== "") { + await sendToBackend(inputText, "text", questionType); + } else { + alert("Please enter text or upload a PDF file."); loadingScreen.style.display = "none"; } - }); \ No newline at end of file + } + + async function sendToBackend(data, dataType, questionType) { + let formData; + let contentType; + formData = JSON.stringify({ input_text: data }); + contentType = "application/json"; + + const response = await fetch("http://127.0.0.1:8000/", { + method: "POST", + body: formData, + headers: { + "Content-Type": contentType, + "Question-Type": questionType, + }, + }); + + if (response.ok) { + const responseData = await response.json(); + localStorage.setItem("qaPairs", JSON.stringify(responseData)); + window.location.href = "../html/question_generation.html"; + } else { + console.error("Backend request failed."); + } + loadingScreen.style.display = "none"; + } +}); diff --git a/extension/manifest.json b/extension/manifest.json index dabae1a..a8ea075 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -4,7 +4,7 @@ "version": "1.0", "description": "Generate quizzes with AI-powered questions.", "permissions": ["activeTab", "storage"], - "host_permissions":["http://127.0.0.1:8000/*"], + "host_permissions":["http://localhost:8000/*","http://127.0.0.1:8000/*","http://localhost:8000/"], "icons": { "16": "./assets/aossie_logo.png" }, @@ -33,4 +33,4 @@ "matches": [""] } ] -} +} \ No newline at end of file diff --git a/extension/styles/index.css b/extension/styles/index.css index 8c0bae4..5eae184 100644 --- a/extension/styles/index.css +++ b/extension/styles/index.css @@ -1,8 +1,4 @@ - - - body{ - /* background-color: rgb(18, 89, 231); */ background-color: #FBAB7E; background-image: linear-gradient(62deg, #FBAB7E 0%, #F7CE68 100%); font-family: 'Inter'; @@ -29,7 +25,6 @@ h1 { flex-direction: column; align-items: center; justify-content: center; - /* height: calc(101vh - 102px); Adjust this height value as needed */ } h2 { @@ -49,8 +44,6 @@ p{ --black: #000; } - -/* Style the button */ button { background: linear-gradient(to right, var(--yellow) 0%, var(--green) 50%, var(--yellow) 100%); background-size: 500%; @@ -59,9 +52,6 @@ button { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); color: var(--black); cursor: pointer; - /* font: 1.5em Raleway, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; */ height: 1.5rem; letter-spacing: 0.05em; outline: none; @@ -71,16 +61,14 @@ button { -ms-user-select: none; user-select: none; width: 7rem; - transition: background-position 0.7s ease; /* Add transition for background position */ + transition: background-position 0.7s ease; margin-bottom: 1px; } -/* Style the button on hover */ button:hover { background-position: 100%; /* Move the background gradient on hover */ } -/* Add animation for button hover effect */ @keyframes gradient { 0% { background-position: 0% 50%; diff --git a/extension/styles/question_generation.css b/extension/styles/question_generation.css index 605639c..d5c8b95 100644 --- a/extension/styles/question_generation.css +++ b/extension/styles/question_generation.css @@ -1,5 +1,4 @@ body{ - /* background-color: rgb(18, 89, 231); */ background-color: #FBAB7E; background-image: linear-gradient(62deg, #FBAB7E 0%, #F7CE68 100%); font-family: 'Inter'; @@ -26,7 +25,6 @@ h1 { flex-direction: column; align-items: center; justify-content: center; - /* height: calc(101vh - 102px); Adjust this height value as needed */ } h2 { @@ -63,9 +61,6 @@ button { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); color: var(--black); cursor: pointer; - /* font: 1.5em Raleway, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; */ height: 1.5rem; letter-spacing: 0.05em; outline: none; @@ -75,15 +70,14 @@ button { -ms-user-select: none; user-select: none; width: 3rem; - transition: background-position 0.7s ease; /* Add transition for background position */ + transition: background-position 0.7s ease; margin-bottom: 1px; } button:hover { - background-position: 100%; /* Move the background gradient on hover */ + background-position: 100%; } - /* Add animation for button hover effect */ @keyframes gradient { 0% { background-position: 0% 50%; diff --git a/extension/styles/text_input.css b/extension/styles/text_input.css index 19e1412..ed7934c 100644 --- a/extension/styles/text_input.css +++ b/extension/styles/text_input.css @@ -1,49 +1,77 @@ -body{ - /* background-color: rgb(18, 89, 231); */ - background-color: #FBAB7E; - background-image: linear-gradient(62deg, #FBAB7E 0%, #F7CE68 100%); - font-family: 'Inter'; - font-weight: 400; /* Regular */ +body { + background-color: #fbab7e; + background-image: linear-gradient(62deg, #fbab7e 0%, #f7ce68 100%); + font-family: "Inter"; + font-weight: 400; /* Regular */ } -header{ - display: flex; - align-items: center; - padding: 10px 20px; - -} -img{ - width: 32px; - height: 32px; - margin-right: 10px; - +header { + display: flex; + align-items: center; + padding: 10px 20px; +} +img { + width: 32px; + height: 32px; + margin-right: 10px; } h1 { - font-size: 24px; - } - main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - /* height: calc(101vh - 102px); Adjust this height value as needed */ - } + font-size: 24px; +} +main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} - h2 { - font-size: 28px; - margin-bottom: 10px; - } -p{ - margin-left: 2px; - margin-right: 2px; - padding-left: 1px; - padding-right: 1px; - text-align: left; -} -#text-input{ - height: 30px; +h2 { + font-size: 28px; + margin-bottom: 10px; +} +p { + margin-left: 2px; + margin-right: 2px; + padding-left: 1px; + padding-right: 1px; + text-align: left; +} +#text-input { + height: 150px; width: 150px; margin-bottom: 10px; + resize: vertical; + overflow: auto; +} + +#google-form-button { + display: inline-block; + background: linear-gradient( + to right, + var(--yellow) 0%, + var(--green) 50%, + var(--yellow) 100% + ); + background-size: 500%; + border: none; + border-radius: 2rem; + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + color: var(--black); + cursor: pointer; + font: 1em Inter, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + height: 2rem; + letter-spacing: 0.05em; + outline: none; + -webkit-tap-highlight-color: transparent; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 8rem; + transition: background-position 0.7s ease; + margin-top: 10px; } :root { --yellow: #e8f222; @@ -51,21 +79,20 @@ p{ --black: #000; } - -/* Style the button */ - button { display: inline-block; - background: linear-gradient(to right, var(--yellow) 0%, var(--green) 50%, var(--yellow) 100%); + background: linear-gradient( + to right, + var(--yellow) 0%, + var(--green) 50%, + var(--yellow) 100% + ); background-size: 500%; border: none; border-radius: 2rem; box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); color: var(--black); cursor: pointer; - /* font: 1.5em Raleway, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; */ height: 1.5rem; letter-spacing: 0.05em; outline: none; @@ -77,20 +104,24 @@ button { width: 3rem; transition: background-position 0.7s ease; /* Add transition for background position */ margin-bottom: 1px; - } label { display: inline-block; - background: linear-gradient(to right, var(--yellow) 0%, var(--green) 50%, var(--yellow) 100%); + background: linear-gradient( + to right, + var(--yellow) 0%, + var(--green) 50%, + var(--yellow) 100% + ); background-size: 500%; border: none; border-radius: 1rem; box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); color: var(--black); cursor: pointer; - font: 1em Inter, sans-serif; + font: 1em Inter, sans-serif; -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + -moz-osx-font-smoothing: grayscale; height: 1rem; letter-spacing: 0.05em; outline: none; @@ -100,21 +131,20 @@ label { -ms-user-select: none; user-select: none; width: 7rem; - transition: background-position 0.7s ease; + transition: background-position 0.7s ease; margin-bottom: 10px; - } -#upload-label{ +#upload-label { text-align: center; align-items: center; margin-top: 2px; } -/* Style the button on hover */ -label:hover, button:hover { - background-position: 100%; /* Move the background gradient on hover */ + +label:hover, +button:hover { + background-position: 100%; } -/* Add animation for button hover effect */ @keyframes gradient { 0% { background-position: 0% 50%; @@ -134,7 +164,6 @@ label:hover, button:hover { z-index: 9999; justify-content: center; align-items: center; - } .loading-spinner { @@ -149,6 +178,10 @@ label:hover, button:hover { } @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } }