diff --git a/background.js b/background.js index d573fa7..678f1e4 100644 --- a/background.js +++ b/background.js @@ -1,25 +1,160 @@ -// add context menu on select -function setupContextMenu(){ +/** Helpful type definitions + * @typedef {Object} Note + * @property {string|number} noteID - Unique ID identifying the note + * @property {string} title - Title of the note + * @property {string} content - Content of the note + * @property {Date} createdDate - The date the note was created + * @property {number} folderID - unique ID of the folder + * + * @typedef {Object} Folder + * @property {string} folderName + * @property {number} folderID + * + * @typedef AppData + * @property {Note[]} notes + * @property {number[]} folders + * @property {{theme: number}} settings + */ + +/** @type AppData - Stores the app's state */ +const appData = { + notes: [], + folders: [], + settings: { + theme: "light" + } +} + + +// appData.settings.theme + +const saveStorage = async () => chrome.storage.local.set(appData); +const getStorage = async () => chrome.storage.local.get(appData); + +chrome.runtime.onInstalled.addListener(async () => { + // setup chrome context menus chrome.contextMenus.create({ id: "addnote", title: "Add to Harbor", contexts: ["selection"] }) -} -chrome.runtime.onInstalled.addListener(() => { setupContextMenu() }) + + // initialize storage + const initAppData = await getStorage(); + Object.assign(appData, initAppData); + chrome.storage.local.set(appData); +}); // handle context menu click chrome.contextMenus.onClicked.addListener((info, tab) => { - console.log(info, tab); if (info.menuItemId === "addnote") { - console.log(`Adding the note "${info.selectionText}"`); - - // we don't need a response, don't bother waiting for one - chrome.runtime.sendMessage({content: info.selectionText}); + processCommand({ + command: "addNote", + data: { /** @type Note */ + title: "", + content: info.selectionText, + createdDate: Date.now(), + folderID: -1 + } + }); } -}) +}); // open panel onclick chrome.sidePanel .setPanelBehavior({ openPanelOnActionClick: true }) - .catch((error) => console.error(error)) \ No newline at end of file + .catch((error) => console.error(error)) + +/** Usage + * chrome.runtime.sendMessage({command: "getData"}) + * chrome.runtime.sendMessage({command: "addNote", data: {title: "test", content: "hello world"}}) + **/ +chrome.runtime.onMessage.addListener(processCommand); + +/** + * The core event loop for the extension + * @param {{command: string, data?: Object}} message + * @param {chrome.runtime.MessageSender} sender + * @param {(response?: any) => void} sendResponse + */ +function processCommand(message, sender, sendResponse) { + if (message === null || message.command === null) return; + const command = message.command; + const data = message.data; + + switch (command) { + case "getData": { + sendResponse(appData); + break; + } + case "getNotes": { + sendResponse(appData.notes); + break; + } + case "getStructure": { + sendResponse(appData.folders); + break; + } + case "getTheme": { + sendResponse(appData.settings.theme); + break; + } + + case "setTheme": { + appData.settings.theme = data.theme; + chrome.runtime.sendMessage({command: "setThemeUI", data: {theme: data.theme}}); + saveStorage(); + break; + } + + case "addNote": { + const { title, content, createdDate, folderID } = data; + // you can replace the noteID with whatever you want, as long as it's a unique string/number + let noteObject = { + noteID: (Math.random()+"").slice(2) + (Math.random()+"").slice(2), + title: title || "", + content: content || "", + createdDate: createdDate || Date.now(), + folderID: folderID || -1 + } + + if (noteObject.title === "" && noteObject.content === "") { + console.error("WARNING, TRYING TO ADD EMPTY NOTE!", noteObject); + return; + } + + appData.notes.push(noteObject); + chrome.runtime.sendMessage({command: "addNoteUI", data: noteObject}); + saveStorage(); + break; + } + + case "deleteNote": { + const noteID = data.noteID; + + let deletedNoteIndex = appData.notes.findIndex(note => note.noteID === noteID); + console.log(`deleting note index ${deletedNoteIndex}`); + if (deletedNoteIndex !== -1) { + let noteObject = appData.notes.splice(deletedNoteIndex, 1)[0]; + chrome.runtime.sendMessage({command: "deleteNoteUI", data: noteObject}); + saveStorage(); + } else { + console.error(`Trying to delete note ${noteID}, which can't be found`); + } + + break; + } + + case "deleteAllNotes": { + appData.notes = []; + chrome.runtime.sendMessage({command: "deleteAllNotesUI"}); + saveStorage(); + break; + } + + default: { + sendResponse("I DONT KNOW WHAT YOU WANT ME TO DO"); + break; + } + } +} \ No newline at end of file diff --git a/js/_main_.js b/js/_main_.js new file mode 100644 index 0000000..13bd237 --- /dev/null +++ b/js/_main_.js @@ -0,0 +1,25 @@ +(async _ => { + // notes are stored as an object + // key: Date.now() + // value: {content: string, tags: string[], title: string} + // this lets us sort the notes by date, and delete by some ID + let notes = {}; + + await loadSettings(); + await loadNotes(); + //insertTag(); + //formatBar.append(createFormatBar()); + + add.addEventListener("click", _ => { addNote(""); }); + document.addEventListener("DOMContentLoaded", _ => { reloadNoteHTML(); loadFolders(); }); + document.addEventListener("visibilitychange", _ => { saveNotesOrder(); saveFolders(); }); + + // context menu --> add new note + chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + const content = request.content; + + // make that new message if it's non-empty + if (content) addNote(content); + }); +})() + diff --git a/js/formatBar.js b/js/formatBar.js index 96638a9..46c8176 100644 --- a/js/formatBar.js +++ b/js/formatBar.js @@ -2,7 +2,7 @@ * Generates "bottom bar" below note boxes to hold text formating buttons * @returns {object} bottomBar */ -function createFormatBar() { +export default function createFormatBar() { const bottomBar = document.createElement("div"); bottomBar.className = "bottom-bar"; diff --git a/js/notes.js b/js/notes.js index 96831df..78a6cec 100644 --- a/js/notes.js +++ b/js/notes.js @@ -14,43 +14,46 @@ function resizeTextarea(textarea) { // this lets us sort the notes by date, and delete by some ID let notes = {}; -// a bunch of helper functions in case we need them later -// tbh we don't really need them but it's nicer to type -function loadNotes() { - const notesText = localStorage.getItem("notesData") || "{}"; - notes = JSON.parse(notesText); -} -function saveNotes() { - localStorage.setItem("notesData", JSON.stringify(notes)); -} -function reloadNoteHTML() { - // delete all the current notes - const currentNotes = Array.from(document.getElementsByClassName("note")); - for (let i = 0; i < currentNotes.length; i++) { - currentNotes[i].remove(); - } - - // add them all back from notes[] - Object.entries(notes).reverse().forEach(([id, {title, content, tags}]) => { - addNoteHTML(title, content, tags, id); - }); -} -function deleteNote(id) { - delete notes[id]; - saveNotes(); -} -function deleteAllNotes() { - notes = {}; - reloadNoteHTML(); - saveNotes(); - reloadFolders(); -} + // a bunch of helper functions in case we need them later + // tbh we don't really need them but it's nicer to type + async loadNotes() { + const { notesData } = await chrome.storage.local.get("notesData"); + data = notesData; + }, + + async saveNotes() { + console.log("should be saved") + chrome.storage.local.set({notesData: notes}); + }, + + reloadNoteHTML() { + // delete all the current notes + const currentNotes = Array.from(document.getElementsByClassName("note")); + for (let i = 0; i < currentNotes.length; i++) { + currentNotes[i].remove(); + } -/** - * this function only creates the note in the notes[] array, then calls addNoteHTML - * @param {string} text - textual/body content of note - * @param {object} insertAfter - the note that precedes the new note you're trying to add - */ + // add them all back from notes[] + Object.entries(notes).reverse().forEach(([id, {title, content, tags}]) => { + addNoteHTML(title, content, tags, id); + }); + }, + async deleteNote(id) { + delete notes[id]; + await saveNotes(); + }, + async deleteAllNotes() { + notes = {}; + reloadNoteHTML(); + await saveNotes(); + reloadFolders(); + }, + + /** + * this function only creates the note in the notes[] array, then calls addNoteHTML + * @param {string} text - textual/body content of note + * @param {object} insertAfter - the note that precedes the new note you're trying to add + */ function eraseNote() { titleInput.value = ""; @@ -63,86 +66,86 @@ function addNote(text, insertAfter) { infoInput.value = ""; // empty out the textbox titleInput.value = ""; - // stop if no text is provided - if (title === "" && content === "") return; + // stop if no text is provided + if (title === "" && content === "") return; - const id = Date.now(); - const tags = []; + const id = Date.now(); + const tags = []; - notes[id] = { title, content, tags }; + notes[id] = { title, content, tags }; - //Move new note to top - const notesArray = Object.entries(notes); - if (notesArray.length > 0) { - newNote = notesArray.pop() - notesArray.unshift(newNote); - - const sortedNotes = {}; - notesArray.forEach(([id, note]) => { - sortedNotes[id] = note; - }); - notes = sortedNotes; - } + //Move new note to top + const notesArray = Object.entries(notes); + if (notesArray.length > 0) { + newNote = notesArray.pop() + notesArray.unshift(newNote); - saveNotes(); - reloadNoteHTML(); - console.log(tags); -} - -/** - * Generates the actual HTML element in the DOM - * don't call directly unless you're reloading - * @param {string} title - title of a note - * @param {string} text - textual/body content of a note - * @param {string[]} tags - list containing all tags of a given note - * parameter id = id - * @param {object} insertAfter - the note that precedes the new note you're trying to add - */ -function addNoteHTML(title, text, tags, id, insertAfter = null) { - if (!id) { - console.log("no ID provided!!!"); - } - // create note elements, then add event listeners - const note = document.createElement("div"); - note.className = "note"; - note.id = id; - note.draggable = true; - - const deleteButton = document.createElement("button"); - deleteButton.className = "del"; - deleteButton.textContent = "X"; - deleteButton.style.display = "none"; - deleteButton.addEventListener("click", function (event) { - event.stopPropagation(); - deleteNote(id); - note.remove(); - customMenu.style.display = "none"; - - // remove overlay - let ove = document.getElementsByClassName("overlay"); - if (ove.length !== 0) document.body.removeChild(ove[0]); - }); - note.appendChild(deleteButton); - - addDraggingEvents(note); - addContextMenuToNote(note); - - note.addEventListener("mouseover", function () { - deleteButton.style.display = "block"; - }); - - note.addEventListener("mouseout", function () { + const sortedNotes = {}; + notesArray.forEach(([id, note]) => { + sortedNotes[id] = note; + }); + notes = sortedNotes; + } + + await saveNotes(); + reloadNoteHTML(); + console.log(tags); + }, + + /** + * Generates the actual HTML element in the DOM + * don't call directly unless you're reloading + * @param {string} title - title of a note + * @param {string} text - textual/body content of a note + * @param {string[]} tags - list containing all tags of a given note + * parameter id = id + * @param {object} insertAfter - the note that precedes the new note you're trying to add + */ + addNoteHTML(title, text, tags, id, insertAfter = null) { + if (!id) { + console.log("no ID provided!!!"); + } + // create note elements, then add event listeners + const note = document.createElement("div"); + note.className = "note"; + note.id = id; + note.draggable = true; + + const deleteButton = document.createElement("button"); + deleteButton.className = "del"; + deleteButton.textContent = "X"; deleteButton.style.display = "none"; - }); + deleteButton.addEventListener("click", function (event) { + event.stopPropagation(); + deleteNote(id); + note.remove(); + customMenu.style.display = "none"; + + // remove overlay + let ove = document.getElementsByClassName("overlay"); + if (ove.length !== 0) document.body.removeChild(ove[0]); + }); + note.appendChild(deleteButton); - note.addEventListener("click", function (event) { - // if the user clicks on a link inside the note, don't change into edit mode - if (event.target.nodeName === "A") return; + addDraggingEvents(note); + addContextMenuToNote(note); - if (!this.classList.contains("overlay-created")) { - const overlay = document.createElement("div"); - overlay.className = "overlay"; - document.body.appendChild(overlay); + note.addEventListener("mouseover", function () { + deleteButton.style.display = "block"; + }); + + note.addEventListener("mouseout", function () { + deleteButton.style.display = "none"; + }); + + note.addEventListener("click", function (event) { + // if the user clicks on a link inside the note, don't change into edit mode + if (event.target.nodeName === "A") return; + + if (!this.classList.contains("overlay-created")) { + const overlay = document.createElement("div"); + overlay.className = "overlay"; + document.body.appendChild(overlay); // show only noteContent const noteTitle = note.getElementsByClassName("note-title")[0]; @@ -167,21 +170,21 @@ function addNoteHTML(title, text, tags, id, insertAfter = null) { note.style.zIndex = null; note.draggable = true; - // update noteDisplay, persist to notes - notes[note.id].title = noteTitle.innerText; - notes[note.id].content = noteContent.value; - //TODO: persist tags as well - noteDisplay.innerHTML = DOMPurify.sanitize(marked.parse(noteContent.value)); + // update noteDisplay, persist to notes + notes[note.id].title = noteTitle.innerText; + notes[note.id].content = noteContent.value; + //TODO: persist tags as well + noteDisplay.innerHTML = DOMPurify.sanitize(marked.parse(noteContent.value)); - // only show noteDisplay - noteContent.classList.add("displayNone"); - noteDisplay.classList.remove("displayNone"); - }); + // only show noteDisplay + noteContent.classList.add("displayNone"); + noteDisplay.classList.remove("displayNone"); + }); - this.classList.add("overlay-created"); - this.style.zIndex = "999"; - } - }); + this.classList.add("overlay-created"); + this.style.zIndex = "999"; + } + }); const noteTitle = document.createElement("div"); noteTitle.contentEditable = "plaintext-only"; @@ -195,25 +198,25 @@ function addNoteHTML(title, text, tags, id, insertAfter = null) { noteDisplay.className = "note-display body"; noteDisplay.innerHTML = DOMPurify.sanitize(marked.parse(text)); - note.appendChild(noteTitle); - note.appendChild(noteContent); - note.appendChild(noteDisplay); + note.appendChild(noteTitle); + note.appendChild(noteContent); + note.appendChild(noteDisplay); - const tagBar = document.createElement("div"); - tagBar.className = "tag-bar"; + const tagBar = document.createElement("div"); + tagBar.className = "tag-bar"; - if(tags) { - tags.forEach((tag) => { - const tagElement = document.createElement("div"); - tagElement.className = "note-tag"; - tagElement.textContent = tag; + if(tags) { + tags.forEach((tag) => { + const tagElement = document.createElement("div"); + tagElement.className = "note-tag"; + tagElement.textContent = tag; - tagBar.appendChild(tagElement); - }); - } + tagBar.appendChild(tagElement); + }); + } - const bottomBar = createFormatBar(); + const bottomBar = createFormatBar(); const timeText = document.createElement("div"); timeText.className = "time-text"; @@ -230,94 +233,95 @@ function addNoteHTML(title, text, tags, id, insertAfter = null) { bottomDiv.appendChild(timeText); note.appendChild(bottomDiv); - if (insertAfter && insertAfter.nextElementSibling) { - container.insertBefore(note, insertAfter.nextElementSibling); - } else { - container.prepend(note); - } -} - -function saveNotesOrder() { - const newNotesOrder = {}; - const noteElements = Array.from(container.getElementsByClassName("note")); + if (insertAfter && insertAfter.nextElementSibling) { + container.insertBefore(note, insertAfter.nextElementSibling); + } else { + container.prepend(note); + } + }, - noteElements.forEach(noteElement => { - const id = noteElement.id; - newNotesOrder[id] = notes[id]; - }); + async saveNotesOrder() { + const newNotesOrder = {}; + const noteElements = Array.from(container.getElementsByClassName("note")); - notes = newNotesOrder; - saveNotes(); -} + noteElements.forEach(noteElement => { + const id = noteElement.id; + newNotesOrder[id] = notes[id]; + }); -infoInput.addEventListener("keydown", evt => { - if (evt.ctrlKey && evt.key === "Enter") { - evt.preventDefault(); - addNote(""); // that was easy - } -}); + notes = newNotesOrder; + await saveNotes(); + }, -function addContextMenuToNote(note) { - console.log(note); - note.addEventListener("contextmenu", function(event) { - event.preventDefault(); + async addContextMenuToNote(note) { + console.log(note); + note.addEventListener("contextmenu", function(event) { + event.preventDefault(); - customMenu.style.display = "block"; - customMenu.style.left = `${event.clientX}px`; - customMenu.style.top = `${event.clientY}px`; + customMenu.style.display = "block"; + customMenu.style.left = `${event.clientX}px`; + customMenu.style.top = `${event.clientY}px`; - document.getElementById("rem").addEventListener("click", function() { - let tagBar = note.querySelector('.tag-bar'); - while (tagBar.firstChild) { - tagBar.removeChild(tagBar.firstChild); - } - notes[note.id].tags = []; - saveNotes(); + document.getElementById("rem").addEventListener("click", async () => { + let tagBar = note.querySelector('.tag-bar'); + while (tagBar.firstChild) { + tagBar.removeChild(tagBar.firstChild); + } + notes[note.id].tags = []; + await saveNotes(); - customMenu.style.display = "none"; - }); + customMenu.style.display = "none"; + }); - document.getElementById("rem").addEventListener("mouseover", function() { - tagMenu.style.display = "none"; - }); - - document.getElementById("addtofolder").addEventListener("mouseover", function(event) { - const tagInputs = document.querySelectorAll('.tag-input'); - - tagMenu.innerHTML = ''; - - tagInputs.forEach(input => { - const menuItem = document.createElement('div'); - menuItem.className = "menu-item"; - menuItem.textContent = input.textContent; - - menuItem.addEventListener("click", () => { - let tagBar = note.querySelector('.tag-bar'); - const tagElement = document.createElement("div"); - tagElement.className = "note-tag"; - tagElement.textContent = menuItem.textContent; - - while (tagBar.firstChild) { - tagBar.removeChild(tagBar.firstChild); - } - tagBar.appendChild(tagElement); - - notes[note.id].tags = []; - notes[note.id].tags.push(menuItem.textContent); - saveNotes(); + document.getElementById("rem").addEventListener("mouseover", function() { + tagMenu.style.display = "none"; + }); + + document.getElementById("addtofolder").addEventListener("mouseover", function(event) { + const tagInputs = document.querySelectorAll('.tag-input'); + + tagMenu.innerHTML = ''; + + tagInputs.forEach(input => { + const menuItem = document.createElement('div'); + menuItem.className = "menu-item"; + menuItem.textContent = input.textContent; + + menuItem.addEventListener("click", async () => { + let tagBar = note.querySelector('.tag-bar'); + const tagElement = document.createElement("div"); + tagElement.className = "note-tag"; + tagElement.textContent = menuItem.textContent; + + while (tagBar.firstChild) { + tagBar.removeChild(tagBar.firstChild); + } + tagBar.appendChild(tagElement); + + notes[note.id].tags = []; + notes[note.id].tags.push(menuItem.textContent); + await saveNotes(); + }); + + tagMenu.appendChild(menuItem); }); - - tagMenu.appendChild(menuItem); + + const customMenuRect = customMenu.getBoundingClientRect(); + tagMenu.style.left = `${customMenuRect.right + 10}px`; + tagMenu.style.top = `${customMenuRect.top}px`; + tagMenu.style.display = "block"; }); - - const customMenuRect = customMenu.getBoundingClientRect(); - tagMenu.style.left = `${customMenuRect.right + 10}px`; - tagMenu.style.top = `${customMenuRect.top}px`; - tagMenu.style.display = "block"; }); - }); + } } +infoInput.addEventListener("keydown", evt => { + if (evt.ctrlKey && evt.key === "Enter") { + evt.preventDefault(); + addNote(""); // that was easy + } +}); + const customMenu = document.createElement("div"); customMenu.className = "custom-context-menu"; diff --git a/js/settings.js b/js/settings.js index 6ef1dde..50e87ca 100644 --- a/js/settings.js +++ b/js/settings.js @@ -6,7 +6,7 @@ const defaultSettings = { Object.assign(settings, defaultSettings); -function loadSettings() { +async function loadSettings() { const settingsObject = JSON.parse(localStorage.getItem("settings")) || {}; const keys = Object.keys(settingsObject); console.log("keys!!", keys); @@ -76,7 +76,7 @@ function saveSettings() { } } -function sortNotesByTag() { +async function sortNotesByTag() { const notesArray = Object.entries(notes); notesArray.sort(([, noteA], [, noteB]) => { @@ -112,10 +112,10 @@ function sortNotesByTag() { notes = sortedNotes; reloadNoteHTML(); - saveNotes(); + await saveNotes(); } -function sortNotesByDate() { +async function sortNotesByDate() { const notesArray = Object.entries(notes); notesArray.sort(([idA], [idB]) => idA - idB); @@ -126,7 +126,7 @@ function sortNotesByDate() { notes = sortedNotes; reloadNoteHTML(); - saveNotes(); + await saveNotes(); } // Get references to elements diff --git a/js/tags.js b/js/tags.js index f9bf4ab..5f11312 100644 --- a/js/tags.js +++ b/js/tags.js @@ -292,7 +292,7 @@ function insertTag(folderName) { notes[draggedNoteId].tags = []; notes[draggedNoteId].tags.push(tagText); - saveNotes(); + await saveNotes(); } } diff --git a/manifest.json b/manifest.json index f48ced1..cba89db 100644 --- a/manifest.json +++ b/manifest.json @@ -7,7 +7,7 @@ "service_worker": "background.js" }, "side_panel":{ - "default_path": "sidepanel.html" + "default_path": "./pages/main.html" }, "icons": { "64": "img/NH_logo_zoom.png", diff --git a/newjs/_main_.js b/newjs/_main_.js new file mode 100644 index 0000000..4f51e1c --- /dev/null +++ b/newjs/_main_.js @@ -0,0 +1,333 @@ +let notes = []; +let folders = []; +let currentFolder = -1; + +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + console.log(request); + + const { command, data } = request; + + if (command === "UpdateUI") { + reloadNoteHTML(); + } else if (command === "addNoteUI") { + let noteObject = data; + addNoteHTML(noteObject) + } else if (command === "deleteNoteUI") { + let noteObject = data; + let target = document.getElementById(noteObject.noteID); + + if (target) target.remove(); + } +}); + + + +add.addEventListener("click", _ => addNote()); +settingsButton.addEventListener("click", _ => { window.location.href = "settings.html" }); +document.addEventListener("DOMContentLoaded", _ => reloadNoteHTML()); +document.addEventListener("visibilitychange", _ => reloadNoteHTML()); + +function reloadData(callback) { + return chrome.runtime.sendMessage({command: "getData"}, (data) => { + notes = data.notes; + folders = data.folders; + callback(); + }); +} + + +const infoInput = document.getElementById("info"); +const titleInput = document.getElementById("title"); +const noteEditor = document.getElementById("noteEditor"); + +const closeButton = document.getElementById("noteEditor_close"); +closeButton.addEventListener("click", _ => { noteEditor.close(); }); + + +function deleteNote(id) { + console.log(`deleting note ${id}`) + chrome.runtime.sendMessage({command: "deleteNote", data: {noteID: id}}); +} +function deleteAllNotes() { + chrome.runtime.sendMessage({command: "deleteAllNotes"}); +} + +function addNote() { + const title = titleInput.value || ""; + const content = infoInput.innerText || ""; + infoInput.innerText = ""; // empty out the textbox + titleInput.value = ""; + + // stop if no text is provided + if (title === "" && content === "") return; + + const noteObject = { title, content, folderID: -1 } + chrome.runtime.sendMessage({command: "addNote", data: noteObject}); +} + +// call on DOM reload and page init +function reloadNoteHTML() { + chrome.runtime.sendMessage({command: "getNotes"}, (notes) => { + // delete all the current notes + Array.from(document.getElementsByClassName("note")).forEach(v => v.remove()); + + // add them all back from notes[] + notes.forEach(noteObject => { + addNoteHTML(noteObject); + }); + }); +} + +/** + * Generates the actual HTML element in the DOM + * don't call directly unless you're reloading + * @param {Note} noteObject - The note we're trying to add + * @param {object} insertAfter - the note that precedes the new note you're trying to add + */ +function addNoteHTML(noteObject, insertAfter) { + let { noteID, title, content, createdDate, folderID } = noteObject; + + if (!noteID) console.log("no ID provided!!!"); + + // create note elements, then add event listeners + const note = document.createElement("div"); + note.className = "note"; + note.id = noteID; + note.draggable = true; + + const deleteButton = document.createElement("button"); + deleteButton.className = "del"; + deleteButton.textContent = "X"; + deleteButton.addEventListener("click", evt => { + evt.stopPropagation(); + deleteNote(noteID); + customMenu.style.display = "none"; + }); + note.appendChild(deleteButton); + + note.addEventListener("mouseover", () => { + deleteButton.style.display = "block"; + }); + note.addEventListener("mouseout", () => { + // deleteButton.style.display = "none"; + }); + note.addEventListener("click", function (event) { + // if the user clicks on a link inside the note, don't change into edit mode + if (event.target.nodeName === "A") return; + + noteEditor.showModal(); + + document.getElementById("noteEditor_title").value = title; + document.getElementById("noteEditor_info").innerText = content; + + // show only noteContent + const noteTitle = note.getElementsByClassName("note-title")[0]; + const noteContent = note.getElementsByClassName("note-content")[0]; + const noteDisplay = note.getElementsByClassName("note-display")[0]; + noteContent.classList.remove("displayNone"); + noteDisplay.classList.add("displayNone"); + + + noteEditor.addEventListener("click", evt => { + + }) + // Disable dragging if note in focused mode + overlay.addEventListener("click", function () { + // remove overlay + document.body.removeChild(overlay); + note.classList.remove("overlay-created"); + note.style.zIndex = null; + note.draggable = true; + + // update noteDisplay, persist to notes + notes[note.id].title = noteTitle.innerText; + notes[note.id].content = noteContent.value; + //TODO: persist tags as well + noteDisplay.innerHTML = DOMPurify.sanitize(marked.parse(noteContent.value)); + + // only show noteDisplay + noteContent.classList.add("displayNone"); + noteDisplay.classList.remove("displayNone"); + }); + + this.classList.add("overlay-created"); + this.style.zIndex = "999"; + }); + + const noteTitle = document.createElement("div"); + noteTitle.className = "note-title"; + noteTitle.innerText = title; + const noteContent = document.createElement("div"); + noteContent.className = "note-content displayNone body"; + noteContent.value = content; + const noteDisplay = document.createElement("div"); + noteDisplay.className = "note-display body"; + noteDisplay.innerHTML = DOMPurify.sanitize(marked.parse(content)); + + note.appendChild(noteTitle); + note.appendChild(noteContent); + note.appendChild(noteDisplay); + + // const tagBar = document.createElement("div"); + // tagBar.className = "tag-bar"; + + // if(tags) { + // tags.forEach((tag) => { + // const tagElement = document.createElement("div"); + // tagElement.className = "note-tag"; + // tagElement.textContent = tag; + + // tagBar.appendChild(tagElement); + // }); + // } + + // note.appendChild(tagBar); + + // const bottomBar = createFormatBar(); + + const timeText = document.createElement("div"); + timeText.className = "time-text"; + timeText.style = "justify-content: right"; + const noteCreatedTime = new Date(+createdDate); + timeText.textContent = `${noteCreatedTime.toLocaleString([], { + timeStyle: "short", + dateStyle: "short" + })}`; + const bottomDiv = document.createElement("div"); + bottomDiv.className = "bottomDiv"; + // bottomDiv.appendChild(bottomBar); + bottomDiv.appendChild(timeText); + note.appendChild(bottomDiv); + + if (insertAfter && insertAfter.nextElementSibling) { + container.insertBefore(note, insertAfter.nextElementSibling); + } else { + container.prepend(note); + } +} + +async function saveNotesOrder() { + const newNotesOrder = {}; + const noteElements = Array.from(container.getElementsByClassName("note")); + + noteElements.forEach(noteElement => { + const id = noteElement.id; + newNotesOrder[id] = notes[id]; + }); + + notes = newNotesOrder; + await saveNotes(); +} + +async function addContextMenuToNote(note) { + note.addEventListener("contextmenu", function(event) { + event.preventDefault(); + + customMenu.style.display = "block"; + customMenu.style.left = `${event.clientX}px`; + customMenu.style.top = `${event.clientY}px`; + + document.getElementById("rem").addEventListener("click", async () => { + let tagBar = note.querySelector('.tag-bar'); + while (tagBar.firstChild) { + tagBar.removeChild(tagBar.firstChild); + } + notes[note.id].tags = []; + await saveNotes(); + + customMenu.style.display = "none"; + }); + + document.getElementById("rem").addEventListener("mouseover", function() { + tagMenu.style.display = "none"; + }); + + document.getElementById("addtofolder").addEventListener("mouseover", function(event) { + const tagInputs = document.querySelectorAll('.tag-input'); + + tagMenu.innerHTML = ''; + + tagInputs.forEach(input => { + const menuItem = document.createElement('div'); + menuItem.className = "menu-item"; + menuItem.textContent = input.textContent; + + menuItem.addEventListener("click", async () => { + let tagBar = note.querySelector('.tag-bar'); + const tagElement = document.createElement("div"); + tagElement.className = "note-tag"; + tagElement.textContent = menuItem.textContent; + + while (tagBar.firstChild) { + tagBar.removeChild(tagBar.firstChild); + } + tagBar.appendChild(tagElement); + + notes[note.id].tags = []; + notes[note.id].tags.push(menuItem.textContent); + await saveNotes(); + }); + + tagMenu.appendChild(menuItem); + }); + + const customMenuRect = customMenu.getBoundingClientRect(); + tagMenu.style.left = `${customMenuRect.right + 10}px`; + tagMenu.style.top = `${customMenuRect.top}px`; + tagMenu.style.display = "block"; + }); + }); +} + + + + + + + + + + + + +titleInput.addEventListener("keydown", evt => { + if (evt.ctrlKey && evt.key === "Enter") { + evt.preventDefault(); + addNote(""); // that was easy + } +}); +infoInput.addEventListener("keydown", evt => { + if (evt.ctrlKey && evt.key === "Enter") { + evt.preventDefault(); + addNote(""); // that was easy + } +}); + +const customMenu = document.createElement("div"); +customMenu.className = "custom-context-menu"; + +customMenu.innerHTML = ` + + +`; + +document.body.appendChild(customMenu); + +const tagMenu = document.createElement('div'); +tagMenu.className = "custom-context-menu"; +tagMenu.style.display = "none"; + +tagMenu.addEventListener("mouseleave", () => { + tagMenu.style.display = "none"; +}); + +document.body.appendChild(tagMenu); + +document.addEventListener("click", function () { + customMenu.style.display = "none"; + tagMenu.style.display = "none"; +}); + +document.addEventListener("contextmenu", () => { + tagMenu.style.display = "none"; +}); \ No newline at end of file diff --git a/newjs/_settings_.js b/newjs/_settings_.js new file mode 100644 index 0000000..cb9dc90 --- /dev/null +++ b/newjs/_settings_.js @@ -0,0 +1,90 @@ +// Get references to elements +const settingsMenu = document.getElementById("settingsMenu"); +const sortMenu = document.getElementById("sortMenu"); + +// Comfirm menu items +const deleteConfirmModal = document.getElementById("deleteConfirmModal"); +const confirmDeleteNotes = document.getElementById("confirmDeleteNotes"); +const cancelDeleteNotes = document.getElementById("cancelDeleteNotes"); + +const resetConfirmModal = document.getElementById("resetConfirmModal"); +const confirmResetNotes = document.getElementById("confirmResetNotes"); +const cancelResetNotes = document.getElementById("cancelResetNotes"); + +// Download notes +const downloadButton = document.getElementById("downloadButton"); + +//delete confirm stuff +const delall = document.getElementById("delall"); +delall.addEventListener("click", () => { + deleteConfirmModal.showModal(); +}); + +confirmDeleteNotes.addEventListener("click", () => { + deleteAllNotes(); + deleteConfirmModal.close(); +}); + +cancelDeleteNotes.addEventListener("click", () => { + deleteConfirmModal.close(); +}); + +deleteConfirmModal.addEventListener("click", function(event) { + const modalContent = document.getElementById("delall"); + if (!modalContent.contains(event.target)) { + deleteConfirmModal.close(); + } +}); + +//reset confirm stuff +const resetSettings = document.getElementById("resetSettings"); +resetSettings.addEventListener("click", () => { + resetConfirmModal.showModal(); +}); + +confirmResetNotes.addEventListener("click", function() { + Object.assign(settings, defaultSettings); + saveSettings(); + loadSettings(); + resetConfirmModal.close(); +}); + +cancelResetNotes.addEventListener("click", () => { + resetConfirmModal.close(); +}); + +resetConfirmModal.addEventListener("click", function(event) { + const modalContent = document.getElementById("resetSettings"); + if (!modalContent.contains(event.target)) { + resetConfirmModal.close(); + } +}); + +chrome.runtime.sendMessage({command: "getTheme"}, theme => { + themeDropdown.value = theme; + themeDropdown.addEventListener("change", evt => { + const selectedTheme = evt.target.value; + console.log(`${selectedTheme} theme selected`); + chrome.runtime.sendMessage({command: "setTheme", data: {theme: selectedTheme}}); + }); +}); + +downloadButton.addEventListener("click", () => { + const folders = JSON.parse(localStorage.getItem("folders") || "[]"); + const data = { + folders: folders, + notes: notes + }; + const blob = new Blob([JSON.stringify(data, null, 2)], { + type: "application/json" + }); + + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = "notes.json"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); +}); \ No newline at end of file diff --git a/newjs/events.js b/newjs/events.js new file mode 100644 index 0000000..28ca363 --- /dev/null +++ b/newjs/events.js @@ -0,0 +1,21 @@ +const events = { + get: { + getData: "", + getNotes: "", + getStructure: "" + }, + + update: { + addNote: "", + updateNote: "", + deleteNote: "", + deleteAllNotes: "" + }, + + ui: { + addNoteUI: "", + deleteNoteUI: "", + deleteAllNotesUI: "", + UpdateUI: "" + } +} \ No newline at end of file diff --git a/newjs/themes.js b/newjs/themes.js new file mode 100644 index 0000000..9c0a9d6 --- /dev/null +++ b/newjs/themes.js @@ -0,0 +1,129 @@ +const unimplementedColor = "#F00BA7"; +const themes = { + legacy: { + text: "#000000", + placeholder: "#747474", + background: "#97BCC7", + foreground: "#F2F1EF", + codeblocks: "#CFCFCF", + hover: "#135473", + click: "#053D57", + border: "#053D57", + button: "#006884", + buttonText: "#F2F1EF", + format: "#D9D9D9", + formatText: "#000000", + formatHover: "#C4C4C4", + formatClick: "#B0B0B0", + submenuHover: "#D9D9D9", + submenuClick: "#C4C4C4" + }, + light: { + text: "#000000", + placeholder: "#A0A0A0", + background: "#FFFFFF", + foreground: "#FFFFFF", + codeblocks: "#E0E0E0", + hover: "#DFDFDF", + click: "#C7C7C7", + border: "#C8C8C8", + button: "#006884", + buttonText: "#F2F1EF", + format: "#EDEDED", + formatText: "#000000", + formatHover: "#D9D9D9", + formatClick: "#C4C4C4", + submenuHover: "#DFDFDF", + submenuClick: "#CCCCCC" + }, + dark: { + text: "#E6E6E6", + placeholder: "#A0A0A0", + background: "#181818", + foreground: "#2A2A2A", + codeblocks: "#3A3A3A", + hover: "#444444", + click: "#333333", + border: "#383838", + button: "#505050", + buttonText: "#E0E0E0", + format: "#505050", + formatText: "#E0E0E0", + formatHover: "#464646", + formatClick: "#3C3C3C", + submenuHover: "#404040", + submenuClick: "#363636" + }, + matcha: { + text: "#5A4632", + placeholder: "#84715B", + background: "#EDE3C9", + foreground: "#FFF8E5", + codeblocks: "#DAC3A3", + hover: "#8A9A5B", + click: "#7A8B4B", + border: "#A98467", + button: "#A0B762", + buttonText: "#FFF8E5", + format: "#A0B762", + formatText: "#FFF8E5", + formatHover: "#8FA456", + formatClick: "#7F934D", + submenuHover: "#EDE3C9", + submenuClick: "#D6CBAF" + }, + nebula: { + text: "#e2dbf0", + placeholder: "#A693B0", + background: "#121022", + foreground: "#282143", + codeblocks: "#4A3B6A", + hover: "#66001d", + click: "#4d0016", + border: "#5B4B8A", + button: "#99002b", + buttonText: "#DDD1E3", + format: "#99002b", + formatText: "#DDD1E3", + formatHover: "#870026", + formatClick: "#7A001F", + submenuHover: "#5B4B8A", + submenuClick: "#4F4178" + } +} + +// initialize to correct theme on load +chrome.runtime.sendMessage({command: "getTheme"}, theme => loadTheme(theme)); + +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + const { command, data } = request; + + if (command === "setThemeUI") { + console.log(`Loading theme ${data.theme}`); + loadTheme(data.theme); + } +}); + +function loadTheme(themeName) { + const des = document.body.style; + let selectedTheme = themes[themeName]; + des.setProperty("--theme-text", selectedTheme.text || unimplementedColor) + des.setProperty("--theme-placeholder", selectedTheme.placeholder || unimplementedColor); + des.setProperty("--theme-background", selectedTheme.background || unimplementedColor); + des.setProperty("--theme-foreground", selectedTheme.foreground || unimplementedColor); + des.setProperty("--theme-codeblocks", selectedTheme.codeblocks || unimplementedColor); + des.setProperty("--theme-hover", selectedTheme.hover || unimplementedColor); + des.setProperty("--theme-click", selectedTheme.click || unimplementedColor); + des.setProperty("--theme-border", selectedTheme.border || unimplementedColor); + des.setProperty("--theme-button", selectedTheme.button || unimplementedColor); + des.setProperty("--theme-buttonText", selectedTheme.buttonText || unimplementedColor); + des.setProperty("--theme-format", selectedTheme.format || unimplementedColor); + des.setProperty("--theme-formatText", selectedTheme.formatText || unimplementedColor); + des.setProperty("--theme-formatHover", selectedTheme.formatHover || unimplementedColor); + des.setProperty("--theme-formatClick", selectedTheme.formatClick || unimplementedColor); + des.setProperty("--theme-submenuHover", selectedTheme.submenuHover || unimplementedColor); + des.setProperty("--theme-submenuClick", selectedTheme.submenuClick || unimplementedColor); +} + + + diff --git a/pages/main.css b/pages/main.css new file mode 100644 index 0000000..5fadec1 --- /dev/null +++ b/pages/main.css @@ -0,0 +1,216 @@ +*, *::before, *::after { + box-sizing: border-box; +} + +#draftArea, #noteEditor[open] { + display: flex; + flex-wrap: wrap; + justify-content: end; + gap: 5px; + padding: 5px; + background-color: var(--theme-foreground); + justify-content: end; +} + +#noteEditor { + border-color: black; +} + +.title { + font-weight: bold; +} + +.body { + border-radius: 0px; +} + +.cuteBorder { + border: 2px solid; + border-color: var(--theme-border); + border-radius: 10px; +} + +.body { + min-height: 50px; +} + +[contenteditable="plaintext-only"] { + padding: 5px; + user-select: all; +} + +[contenteditable="plaintext-only"]:empty:before{ + content:attr(placeholder); + color:gray; + font-style:italic; +} + +#container { + display: flex; + gap: 10px; + margin-top: 10px; + flex-wrap: wrap; +} + +.del { + position: absolute; + top: -7px; + right: -7px; + border-radius: 50%; + border: 0px; + display: none; + text-align: center; + width: 25px; + height: 25px; + background-color: var(--theme-button); + color: var(--theme-buttonText); + background-color: var(--theme-button); + color: var(--theme-buttonText); + box-shadow: 0px 3px 4px 0px rgba(0, 0, 0, 0.3); +} + +.del:hover { + background-color: var(--theme-hover); +} + +.del:active { + background-color: var(--theme-click); +} + +.note { + width: 100%; + padding: 10px; + background-color: var(--theme-foreground); + position: relative; + border-radius: 10px; + height: 100%; + outline: 2px solid var(--theme-border); + box-shadow: 0px 3px 4px 0px rgba(0, 0, 0, 0.3); + color: var(--theme-text); + transition: transform .2s ease-in-out; +} + +.note:hover { + transform: scale(1.01); +} + +.note-title { + border-radius: 10px 10px 0px 0px; + color: var(--theme-text); + margin-bottom: 5px; + font-weight: bold; + overflow-x: hidden; +} + +#settingsButton { + text-align: center; + vertical-align: middle; + width: 34px; + height: 34px; + font-size: 20px; + border-radius: 10px; + border: none; +} + +.flex { + display: flex; +} + +.row { + display: flex; + flex-direction: row; + gap: 10px; + margin-bottom: 10px; + align-items: center; +} + +.column { + display: flex; + flex-direction: column; + gap: 10px; +} + + + +.none { + display: none !important; +} + +.topButton { + border-radius: 5px; + border: none; + padding: 4px; +} + +#add { + background-color: var(--theme-button); + color: var(--theme-buttonText); +} + +#erase { + background-color: var(--theme-format); + color: var(--theme-formatText); +} + + + +.overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 2; + /* Make sure it's on top */ +} + +.topInput { + min-width: 100px; + padding: 5px; + width: 100%; + background-color: var(--theme-foreground); + color: var(--theme-text); + border: none; +} +.topInput::placeholder { + color: var(--theme-placeholder); +} +.topInput:focus { + border: 1px solid red; +} + +.time-text { + color: var(--theme-text); + text-align: end; + width: 100%; + margin-right: 10px; +} + + + +#search { + flex: 1; + border-radius: 10px; + height: 34px; + border: 2px solid; + border-color: var(--theme-border); + /*box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.3);*/ +} + +#search:hover { + background-color: var(--theme-submenuHover); +} + +#search:active { + background-color: var(--theme-submenuClick); +} + +#search:focus { + border: 2px solid var(--theme-border); + outline: 0px; +} + +#search::-webkit-calendar-picker-indicator { + display: none !important; +} \ No newline at end of file diff --git a/pages/main.html b/pages/main.html new file mode 100644 index 0000000..b721689 --- /dev/null +++ b/pages/main.html @@ -0,0 +1,77 @@ + + + + + + + + + + + +
+ + + + + + + +
+ + +
+ +
+ + +
+ + +
+ + + + +
+
+ + + +
+ + +
+
+ + +
+
+
+ + +
+ + +
+ + + +
+ + +
+ + +
+ + + + + + diff --git a/pages/main2.css b/pages/main2.css new file mode 100644 index 0000000..44a61b0 --- /dev/null +++ b/pages/main2.css @@ -0,0 +1,619 @@ +.title { + font-size: 17px; + font-weight: bold; + color: black; +} + +.body { + font-size: 15px; +} + + + +.icon { + width: 24px; + height: 24px; + + /* Set up the PNG as a mask */ + -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/img/close.png'); + mask-image: url('chrome-extension://__MSG_@@extension_id__/img/close.png'); + + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + + -webkit-mask-size: cover; + mask-size: cover; + + background-color: var(--theme-text); /* Color changes with theme */ +} + + + +nav { + background-color: orange; +} + + + +.topContainerSearch { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 10px; + margin-bottom: 10px; +} + +.topContainer { + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: space-between; + gap: 10px; + margin-bottom: 10px; + position: relative; + position: relative; +} + +#container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + align-items: center; + position: relative; + width: 100%; + gap: 10px; + padding-top: 12px; +} + +#dAdd { + margin-top: 10px; + padding: 10px; + border: none; + border-radius: 5px; + margin-left: auto; + margin-right: auto; + width: fit-content; + display: none; +} + + + +.note-title:focus { + outline: none; + color: var(--theme-text) +} + +.note.dragging { + opacity: 0.5; + box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2); +} + +.note-content { + height: auto; + max-height: 500px; + height: auto; + width: 100%; + background-color: var(--theme-foreground); + border: none; + padding: 5px; + overflow-x: scroll; + overflow-y: scroll; + white-space: pre-wrap; + color: var(--theme-text); +} + +.note-content:focus { + outline: none; + color: var(--theme-text); +} + +.note-content:link { + color: var(--button-text); + color: var(--button-text); +} + +.note-display { + height: auto; + max-height: 100px; + height: auto; + max-height: 100px; + max-width: 100%; + background-color: var(--theme-foreground); + color: var(--theme-text); + border: none; + padding: 5px; + overflow-x: scroll; + overflow-y: clip; + white-space: pre-wrap; +} + +.deleteButton { + color: var(--theme-buttonText) +} + + + + + + + +.inputContainer { + display: flex; + flex: 1; + flex-direction: column; + border-radius: 10px; + border: 2px solid; + border-color: var(--theme-border); + background-color: var(--theme-foreground); + color: var(--theme-text); + height: 150px; + height: 150px; + transition: transform .2s ease-in-out; +} + +.inputContainer:hover { + transform: scale(1.01); +} + +.bottomDiv { + display: flex; + align-items: center; +} + +.time-text { + font-size: 13px; +} +.bottom-bar { + margin: 5px; + padding: 5px; + background-color: var(--theme-format); + border-radius: 5px; + height: 10px; + width: 55px; + display: flex; + justify-content: center; + align-items: center; +} + +.bottom-bar button { + background-color: var(--theme-format); + color: var(--theme-formatText); + border: 0px; +} + +.bottom-bar button:hover { + background-color: var(--theme-formatHover); + color: var(--theme-formatText); + border: 0px; +} + +.bottom-bar button:active { + background-color: var(--theme-formatClick); + color: var(--theme-formatText); + border: 0px; +} + +#title { + font-weight: bold; + border-radius: 5px 5px 0px 0px; + margin: 5px 5px 0px 5px; + border: none; +} +#title:focus { + outline: none; +} + +#info { + border-radius: 0px 0px 10px 10px; + border: 0px; + color: var(--theme-text); + min-height: 74px; + resize: none; +} +#info:focus { + outline: none; +} + +.topButton { + border-radius: 10px; + background-color: var(--theme-button); + color: var(--theme-buttonText); + background-color: var(--theme-button); + color: var(--theme-buttonText); + border: 0px; +} + +.topButton:hover { + background-color: var(--theme-hover); + cursor: pointer; +} + +.topButton:active { + background-color: var(--theme-click); +} + +#openSettings { + height: 34px; +} + +.closeButton { + background-color: var(--theme-button); + color: var(--theme-buttonText); + background-color: var(--theme-button); + color: var(--theme-buttonText); + border: none; + width: 30px; + height: 30px; + border-radius: 20px; +} + +.closeButton:hover { + cursor: pointer; + background-color: var(--theme-hover); +} + +.closeButton:active { + background-color: var(--theme-click); +} + +#add { + height: 30px; + width: 75px; + z-index: 1; + position: absolute; + right: 10px; + bottom: 10px; + background-color: var(--theme-button); + color: var(--theme-buttonText); +} + +#add:hover { +background-color: var(--theme-hover); +} + +#add:active { + background-color: var(--theme-click) +} + +#erase { + height: 30px; + width: 65px; + z-index: 1; + position: absolute; + right: 95px; + bottom: 10px; +} + +.shadow { + box-shadow: 0px 3px 4px 0px rgba(0, 0, 0, 0.3); +} + +#tagRow { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 4px; +} + +#folderRow { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 4px; +} + +#addTagButton { + width: 30px; + height: 30px; + font-size: 20px; + align-items: center; +} + +#tagDropdown { + width: 50px; + height: 30px; + align-items: center; +} + +#downloadButton { + width: 40px; + text-align: center; +} + +/* Wrapper for new tag */ +.new-tag { + background-color: var(--theme-button); + color: var(--theme-placeholder); + padding: 2px 8px; + border-radius: 10px; + align-items: center; + display: flex; + font-size: 12px; +} + +.new-tag:focus { + border: none; +} + +.new-tag:hover { + background-color: var(--theme-hover); +} + +.new-tag:active { + background-color: var(--theme-click); +} + +.tag { + height: 30px; + background-color: var(--theme-button); + color: var(--theme-buttonText); + padding: 0px 8px; + border-radius: 10px; + align-items: center; + position: relative; + display: flex; + align-items: center; + font-size: 12px; +} + +.tag:hover { + background-color: var(--theme-hover); + cursor: pointer; +} +.tag:active { + background-color: var(--theme-click); +} + +.tag:focus { + border: none; + background-color: var(--theme-hover); + color: var(--theme-buttonText); + cursor: text; +} + +.tag-input { + background-color: transparent; + color: var(--theme-buttonText); + border: none; + outline: none; + padding: 0; + margin: 0; + flex-grow: 1; + margin-right: 7px; +} + +.tag-input:empty:before { + content: "Add Folder Name..."; + background-color: transparent; + color: var(--theme-placeholder); +} + +.del-tag { + border: none; + border-radius: 50%; + width: 17px; + height: 17px; + cursor: pointer; + font-size: 12px; + display: flex; + user-select: none; + background-color: transparent; + color: var(--theme-buttonText); + align-items: center; +} + +.tag-bar { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding-left: 5px; + gap: 5px; +} + + +.bottom-bar { + display: flex; + padding-left: 5px; +} + +.note-tag { + background-color: var(--theme-button); + color: var(--theme-buttonText); + height: 20px; + padding: 0px 8px; + border-radius: 5px; + align-items: center; + position: relative; + display: flex; + align-items: center; + font-size: 12px; +} + +.custom-context-menu { + position: absolute; + display: none; + z-index: 1; + background-color: #ffffff; + color: #000000; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + padding: 4px 0; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + font-family: Arial, sans-serif; + font-size: 12px; +} + +.custom-context-menu .menu-item { + padding: 8px 16px; + cursor: pointer; + line-height: 1.5; + border-radius: 2px; +} + +.custom-context-menu .menu-item:hover { + background-color: #f5f5f5; +} + +/* Hide menus by default */ +.settings-menu { + position: absolute; + background: var(--theme-foreground); + border: 2px solid var(--theme-border); + border-radius: 8px; + padding: 5px; + width: 150px; + display: none; + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); + z-index: 1; +} + +/* Style for dropdown items */ +.menu-item { + padding: 8px; + color: var(--theme-text); + font-family: Arial, sans-serif; + font-size: 16px; + cursor: pointer; + border-radius: 5px; +} + +/* Hover effect for dropdown */ +.menu-item:hover { + background: var(--theme-submenuHover); +} + +/* Click effect for dropdown */ +.menu-item:active { + background: var(--theme-submenuClick); +} + +.settings-modal { + width: 70%; + min-width: 300px; + border-radius: 10px; + border: 2px solid var(--theme-border); + background-color: var(--theme-foreground); +} + +.confirmModal { + width: 50%; + min-width: 200px; +} + +#settingsTitle { + font-size: 20px; + color: var(--theme-text); +} + +.settingsHeader { + font-size: 17px; + font-weight: bold; + color: var(--theme-text); + margin-bottom: 3px; +} + +.settingsText { + font-size: 14px; + color: var(--theme-text); + margin-bottom: 3px; + display: flex; + width: 85%; +} + + + +#themeDropdown { + width: 140px; + height: 35px; + border-radius: 10px; + margin-top: 5px; + background-color: var(--theme-submenuHover); + border: none; + color: var(--theme-text); + padding-left: 5px; + outline: none; +} + +.settingsTop { + display: flex; + justify-content: space-between; +} + +.settingsDiv { + margin-top: 10px; +} + +.dropdown { + position: relative; + width: 30px; + height: 30px; + align-items: center; + font-family: Arial, sans-serif; + font-size: 12px; +} + +.dropdown-content { + display: flex; + justify-content: center; + width: 100%; + padding-top: 4px; + align-items: center; +} + +#dropdown-arrow { + font-size: 12px; + align-items: center; + margin-top: 4px; +} + +.dropdown-menu { + position: absolute; + top: 100%; + min-width: max-content; + background: var(--theme-foreground); + border-radius: 10px; + z-index: 1; + border: 2px solid var(--theme-border); + padding: 5px; + font-family: Arial, sans-serif; + font-size: 12px; +} + +.dropdown-item { + padding: 10px; + cursor: pointer; + color: var(--theme-text); + border-radius: 6px; +} + +.dropdown-item:hover { + background: var(--theme-submenuHover); +} + +.dropdown-item:active { + background: var(--theme-submenuClick); +} + +.hidden { + display: none; +} + +#errorMessage { + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + background-color: var(--theme-format); + color: var(--theme-formatText); + /*background-color: #323232; + color: white; */ + padding: 10px 20px; + border-radius: 5px; + display: none; + z-index: 9999; + font-family: sans-serif; + box-shadow: 0 2px 8px rgba(0,0,0,0.3); +} \ No newline at end of file diff --git a/pages/settings.html b/pages/settings.html new file mode 100644 index 0000000..205f6e1 --- /dev/null +++ b/pages/settings.html @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sidepanel.html b/pages/sidepanel.html similarity index 95% rename from sidepanel.html rename to pages/sidepanel.html index b9b7e38..66df9db 100644 --- a/sidepanel.html +++ b/pages/sidepanel.html @@ -1,8 +1,9 @@ + - - - + + + @@ -132,15 +133,17 @@ - - + + + + + diff --git a/style.css b/pages/style.css similarity index 95% rename from style.css rename to pages/style.css index 7c213e4..99264ba 100644 --- a/style.css +++ b/pages/style.css @@ -1,22 +1,22 @@ :root { - --theme-text: #000000; - --theme-placeholder: #747474; /* for use in placeholder text */ - --theme-background: #97BCC7; - --theme-foreground: #F2F1EF; - --theme-codeblocks: #CFCFCF; - --theme-hover: #135473; - --theme-click: #053D57; - --theme-border: #053D57; - --theme-button: #006884; /* for use in certain buttons */ - --theme-buttonText: #F2F1EF; - --theme-button: #006884; /* for use in certain buttons */ - --theme-buttonText: #F2F1EF; - --theme-format: #D9D9D9; - --theme-formatText: #000000; - --theme-formatHover: #C4C4C4; - --theme-formatClick: #B0B0B0; - --theme-submenuHover: #D9D9D9; - --theme-submenuClick: #C4C4C4; + --theme-text: inherit; + --theme-placeholder: inherit; /* for use in placeholder text */ + --theme-background: inherit; + --theme-foreground: inherit; + --theme-codeblocks: inherit; + --theme-hover: inherit; + --theme-click: inherit; + --theme-border: inherit; + --theme-button: inherit; /* for use in certain buttons */ + --theme-buttonText: inherit; + --theme-button: inherit; /* for use in certain buttons */ + --theme-buttonText: inherit; + --theme-format: inherit; + --theme-formatText: inherit; + --theme-formatHover: inherit; + --theme-formatClick: inherit; + --theme-submenuHover: inherit; + --theme-submenuClick: inherit; } a:link { diff --git a/pages/theme.css b/pages/theme.css new file mode 100644 index 0000000..1598114 --- /dev/null +++ b/pages/theme.css @@ -0,0 +1,117 @@ +:root { + --theme-text: #000000; + --theme-placeholder: #747474; /* for use in placeholder text */ + --theme-background: #97BCC7; + --theme-foreground: #F2F1EF; + --theme-codeblocks: #CFCFCF; + --theme-hover: #135473; + --theme-click: #053D57; + --theme-border: #053D57; + --theme-button: #006884; /* for use in certain buttons */ + --theme-buttonText: #F2F1EF; + --theme-format: #D9D9D9; + --theme-formatText: #000000; + --theme-formatHover: #C4C4C4; + --theme-formatClick: #B0B0B0; + --theme-submenuHover: #D9D9D9; + --theme-submenuClick: #C4C4C4; +} + +a:link { + color: var(--theme-button-text); + color: var(--theme-button-text); +} + +a:hover { + color: var(--theme-hover); +} + +a:active { + color: var(--theme-click); +} + +body { + background-color: var(--theme-background); + overflow: hidden; + margin: 10px; + overflow-y: scroll; + overflow-x: hidden; + font-family: Arial, Helvetica, sans-serif; +} + +textarea, input, button, select { + font-family: inherit; +} + +label { + font-size: medium; +} + +h1, h2, h3, h4, h5, h6, p { + margin: 0; + margin: 0; + overflow-wrap: break-word; +} + +/* Codeblocks */ +pre { + background-color: var(--theme-codeblocks); + margin: 0px; + padding: 5px; + border: solid 1px var(--theme-border); +} + +/* width */ +::-webkit-scrollbar { + width: 2px; + height: 2%; + color: 006884; +} + +/* Track */ +::-webkit-scrollbar-track { + background: #f1f1f1; +} + +/* Handle */ +::-webkit-scrollbar-thumb { + background: #888; +} + +/* Handle on hover */ +::-webkit-scrollbar-thumb:hover { + background: #555; +} + +::backdrop { + background: gray; + opacity: 0.50; +} + + +.primaryButton { + background-color: var(--theme-button); + color: var(--theme-buttonText); + padding: 10px; +} + +.secondaryButton { + background-color: var(--theme-format); + color: var(--theme-formatText); + padding: 10px; +} + +.settingsIcon { + background-color: var(--theme-button); + color: var(--theme-buttonText); +} + + + + +.flex { + display: flex +} +.displayNone { + display: none; +} \ No newline at end of file