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 () {