diff --git a/index.html b/index.html index ab7c72f..9863c9d 100644 --- a/index.html +++ b/index.html @@ -20,6 +20,7 @@ + diff --git a/lib/quoteUtils.js b/lib/quoteUtils.js new file mode 100644 index 0000000..af007a8 --- /dev/null +++ b/lib/quoteUtils.js @@ -0,0 +1,138 @@ +// TODO(me) Just a copypaste of pieces of taskUtils to get this working. Needs cleanup, and might not merge settings correctly + +/* + +Example quotes file: + +# Settings + +- backgroundColor: var(--grey-trans) +- fontSize: 1.5vh +- borderRadius: 0.2em +- width: 20em +- padding: 1em + +--- + +> I love deadlines. I love the whooshing noise they make as they go by + +Douglas Adams + + + +*/ + +function textToQuoteObject(text) { + const lines = text.split("\n"); + let obj = {}; + obj._valid = true; + obj.lines = []; + let settings = false; + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + if (line == "# Settings") { + settings = true; + obj._valid = true; + obj.settings = {}; + continue; + } + if (line.startsWith("- ") && settings) { + const cleaned = line.replace("- ", "").trim(); + + const [prop, val] = cleaned.split(":"); + obj.settings[prop.trim()] = val.trim(); + continue; + } + + console.log(line); + if (line.startsWith("> ")) { + obj.lines.push(line.replace(/^> /, "")); + } + if (line.length === 0) { + continue; + } + obj.author = line; + } + return obj; +} + +function quotesFromMarkdown(paths, cb, basepath) { + const promises = []; + + if (typeof paths === "string") { + paths = [paths]; + } + + paths.forEach((path) => { + promises.push( + fetch(path) + .then((response) => response.text()) + .then((markdown) => { + let quotes = []; + + const blocks = markdown.split(/---/); + for (let i = 0; i < blocks.length; i++) { + const block = blocks[i]; + let obj = textToQuoteObject(block); + if (basepath) { + obj = textToQuoteObject(block); + } + + if (!obj._valid) { + tasks.push({ + text: `Error loading quote ${i} from file ${path}`, + error: true, + }); + continue; + } + quotes.push(obj); + } + return quotes; + }), + ); + }); + Promise.all(promises) + .then((results) => { + // Flatten the array of arrays into a single array of tasks + const allQuotes = results.flat(); + window._quotes = allQuotes; + cb(allQuotes); + }) + .catch((error) => console.error("Error loading markdown file:", error)); +} + +function addQuotesToDiv(_quotes, targetDivId) { + const quotes = shuffleArray(_quotes); + const d = () => document.createElement("DIV"); + const targetDiv = document.getElementById(targetDivId); + if (!targetDiv) { + console.error("Target div not found:", targetDivId); + return; + } + for (let i = 0; i < quotes.length; i++) { + const quote = quotes[i]; + if (quote.settings) { + for (const key in quote.settings) { + targetDiv.style[key] = quote.settings[key]; + } + continue; + } + const div = d(); + const color = colors[i % colors.length]; + div.style.color = `var(${color})`; + div.classList.add("quote-wrapper"); + const q = d(); + q.classList.add("quote"); + const a = d(); + a.classList.add("author"); + for (const line of quote.lines) { + const p = document.createElement("p"); + p.textContent = line; + q.appendChild(p); + } + a.textContent = quote.author; + div.appendChild(q); + div.appendChild(a); + targetDiv.appendChild(div); + } +} diff --git a/lib/taskUtils.js b/lib/taskUtils.js index f9fb21b..91c404d 100644 --- a/lib/taskUtils.js +++ b/lib/taskUtils.js @@ -139,6 +139,11 @@ function textToTaskObject(text, hPos = 0, filepath) { obj.done = true; cleaned = cleaned.slice(3); } + if (cleaned.endsWith("(someday)") || cleaned.endsWith("(Someday)")) { + obj.someday = true; + obj.prio = -1000; + cleaned = cleaned.replace(/\s+\(.omeday\)/, ""); + } const linkMatch = cleaned.match(linkPlusRegex); if (linkMatch) { obj.text = linkMatch[1] + (linkMatch[3] ?? ""); @@ -153,7 +158,7 @@ function textToTaskObject(text, hPos = 0, filepath) { obj.done = true; continue; } - if (line.startsWith("prio:")) { + if (line.startsWith("prio:") && !obj.someday) { obj.prio = parseInt(line.replace("prio:", "")); continue; } @@ -284,6 +289,7 @@ function addTasksToDiv(_tasks, targetDivId) { return; } let addedDoneSeparator = false; + let someday = false; targetDiv.addEventListener("mouseover", () => toTop(targetDiv)); let projects = []; targetDiv.innerHTML = ""; @@ -359,6 +365,10 @@ function addTasksToDiv(_tasks, targetDivId) { 100 - fuzz }%, var(--light) ${fuzz}%)`; } + if (task.someday && !task.done) { + taskWrapper.classList.add("someday", "hide"); + someday = true; // Global catch for "there is someday" + } for (let i = 0; i < projects.length; i++) { const _p = projects[i]; let pproj = _p; @@ -507,6 +517,33 @@ function addTasksToDiv(_tasks, targetDivId) { } wrapperNode.appendChild(taskWrapper); } + if (someday) { + // I could just use count + const somedays = tasks.filter((t) => t.someday && !t.done); + const count = somedays.length; + console.log(`Someday: ${count}`); + const taskWrapper = d(); + taskWrapper.classList.add("taskRow"); + taskWrapper.classList.add("someday-header"); + const taskText = d(); + taskText.classList.add("taskText"); + taskWrapper.appendChild(taskText); + taskText.textContent = `Someday (${count})`; + taskText.style.color = `var(--task-default)`; + const firstSomeday = wrapperNode.querySelector(".taskRow.someday"); + console.log(firstSomeday); + const hr = document.createElement("HR"); + hr.style.borderColor = `var(--task-default)`; + hr.classList.add("dim"); + wrapperNode.insertBefore(hr, firstSomeday); + wrapperNode.insertBefore(taskWrapper, firstSomeday); + taskText.addEventListener("click", (e) => { + e.preventDefault(); + const somedays = document.querySelectorAll(".taskRow.someday"); + Array.from(somedays).map((s) => s.classList.toggle("hide")); + }); + taskText.addEventListener("contextmenu", (e) => e.preventDefault()); + } try { chrome.storage.local.get(["visitedLinks"], (data) => { const visitedLinks = data.visitedLinks || {}; diff --git a/style.css b/style.css index 02750e8..89a6a9f 100644 --- a/style.css +++ b/style.css @@ -227,10 +227,23 @@ span[data-title]:hover::after { filter: saturate(1.2) brightness(1.2); } +hr.dim { + filter: grayscale(0.7) opacity(0.5); +} + .taskRow.dim { filter: grayscale(0.7) opacity(0.5); } +.taskRow.someday { + filter: grayscale(0.1) opacity(0.8); + font-size: 80% !important; +} + +.taskRow.someday.hide { + display: none; +} + .taskRow[data-title][data-prio]:hover::after { content: "prio:" attr(data-prio) "\A" attr(data-title); /* Combine content */ white-space: pre; /* Preserve newline character */ @@ -280,3 +293,12 @@ span[data-title]:hover::after { color: var(--light-grey-trans) !important; text-shadow: none !important; } + +.quote { +} + +.author { + font-size: 90%; + filter: grayscale(0.7) opacity(0.8); + float: right; +} diff --git a/tests/test_task_parsing.js b/tests/test_task_parsing.js index 6b53838..9836b30 100644 --- a/tests/test_task_parsing.js +++ b/tests/test_task_parsing.js @@ -122,6 +122,27 @@ foo`; chai.expect(parsed.color).to.eql("ultraviolet"); chai.expect(parsed.extraColor).to.eql("maroon"); }); + it("someday should be handled", function () { + const t = `# Something to do (someday) + prio: 20 + - msg1 + - [l1](foo.com) + - [l2](bar.com) + extracolor: maroon + color: ultraviolet + - msg2 +foo`; + const parsed = textToTaskObject(t); + console.log(parsed); + chai.expect(parsed.text).to.eql("Something to do"); + chai.expect(parsed.prio).to.eql(-1000); + chai.expect(parsed.someday).to.be.true; + chai.expect(parsed.links[0]).to.eql(["l1", "foo.com"]); + chai.expect(parsed.links[1]).to.eql(["l2", "bar.com"]); + chai.expect(parsed.msg).to.eql(["msg1", "msg2"]); + chai.expect(parsed.color).to.eql("ultraviolet"); + chai.expect(parsed.extraColor).to.eql("maroon"); + }); }); describe("Settings list handling", function () {