β The Study Nook
β The Study Nook
A gentle reminder
Progress counts, even when it happens one small step at a time.
πΏ Start here
ποΈ Planning
label: πΏ New Assignment
icon: plus-circle
style: primary
id: new-assignment
hidden: false
actions:
- type: templaterCreateNote
templateFile: "study nook/School/Templates/Assignment Template.md"
folderPath: "study nook/School/Assignments"
fileName: "New Assignment"
openNote: true
label: π New Weekly Plan
icon: calendar-plus
style: primary
id: new-weekly-plan
hidden: false
actions:
- type: templaterCreateNote
templateFile: "study nook/School/Templates/Weekly Plan Template.md"
folderPath: "study nook/School"
fileName: "New Weekly Plan"
openNote: true
label: πΈ Schedule Work Session
icon: calendar-clock
style: primary
id: schedule-work-session
hidden: false
actions:
- type: runTemplaterFile
templateFile: "study nook/School/Templates/Schedule Work Session.md"
// πΏ Find the current weekly plan
const today = dv.date("today");
const monday = today.minus({
days: today.weekday - 1
});
const plans = dv.pages('"study nook/School/Weekly Plans"')
.where(p => {
if (!p.week_start) return false;
const date = dv.date(p.week_start);
return date &&
date.toISODate() === monday.toISODate();
});
if (plans.length > 0) {
const plan = plans.first();
dv.paragraph(
"πΏ " +
"[[" + plan.file.path +
"|Open this week's plan]]"
);
} else {
dv.paragraph(
"π± No weekly plan created yet for this week!"
);
}
πͺ΄ What Should I Work On?
A little guidance for when starting feels like the hardest part. πΈ
// =============================================
// πΏ STUDY NOOK: WHAT SHOULD I WORK ON?
// =============================================
const today = dv.date("today").startOf("day");
const todayISO = today.toISODate();
// πΈ Find all active assignments
const assignments = dv.pages(
'"study nook/School/Assignments"'
);
// Convert date properties consistently
function toDate(value) {
if (!value) return null;
try {
const parsed = dv.date(value);
return parsed && parsed.isValid
? parsed.startOf("day")
: null;
} catch {
return null;
}
}
// Read work_days, whether a single date or list
function getWorkDays(page) {
if (!page.work_days) return [];
const values = Array.isArray(page.work_days)
? page.work_days
: (typeof page.work_days.array === "function"
? page.work_days.array()
: [page.work_days]);
return values
.map(value => {
const date = toDate(value);
return date ? date.toISODate() : null;
})
.filter(Boolean);
}
// Check whether an assignment is completed
function isComplete(page) {
const status = String(page.status ?? "")
.trim()
.toLowerCase();
return ["done", "completed", "complete"].includes(status)
|| page.completed === true;
}
// πΏ Calculate recommendation scores
function analyze(page) {
const due = toDate(page.due);
const daysUntilDue = due
? Math.round(due.diff(today, "days").days)
: null;
const status = String(page.status ?? "")
.trim()
.toLowerCase();
const priority = String(page.priority ?? "Medium")
.trim()
.toLowerCase();
const workDays = getWorkDays(page);
const scheduledToday = workDays.includes(todayISO);
let score = 0;
let reason = "Ready when you are";
// Deadline urgency
if (daysUntilDue !== null) {
if (daysUntilDue < 0) {
score += 100;
reason = "β° Overdue";
} else if (daysUntilDue === 0) {
score += 85;
reason = "π₯ Due today";
} else if (daysUntilDue === 1) {
score += 65;
reason = "πΈ Due tomorrow";
} else if (daysUntilDue === 2) {
score += 45;
reason = "π
Due in 2 days";
} else if (daysUntilDue <= 7) {
score += 20;
reason = "π Due this week";
}
}
// Scheduled work takes priority
if (scheduledToday) {
score += 55;
if (daysUntilDue === null || daysUntilDue > 2) {
reason = "πΏ Scheduled for today";
}
}
// Assignment priority
if (priority === "high") score += 25;
if (priority === "medium") score += 10;
// Encourage finishing started assignments
if (status === "in progress") score += 15;
return {
page,
score,
reason,
priority,
daysUntilDue,
scheduledToday,
status
};
}
// β Select unfinished assignments
const ranked = assignments
.where(page => !isComplete(page))
.array()
.map(analyze)
.sort((a, b) => {
if (b.score !== a.score) return b.score - a.score;
return (a.daysUntilDue ?? Infinity)
- (b.daysUntilDue ?? Infinity);
});
// π± Give a gentle five-minute starting step
function firstStep(item) {
const name = item.page.file.name.toLowerCase();
const className = String(item.page.class ?? "")
.toLowerCase();
const combined = name + " " + className;
if (/static|engr 210/.test(combined)) {
return "Open the next problem and draw the free-body diagram.";
}
if (/calc|math/.test(combined)) {
return "Open the assignment, identify the next problem, and write down the formula or concept you need.";
}
if (/statistic|probability/.test(combined)) {
return "Read the next problem and identify the given values and what you need to find.";
}
if (/matlab|me 273|coding/.test(combined)) {
return "Open MATLAB and your assignment. Choose one small section to work on.";
}
if (/me 287|design|solidworks|dino/.test(combined)) {
return "Open your project files and choose one small design or documentation task.";
}
return "Open the assignment and identify the smallest task you can complete in five minutes.";
}
// πΈ DISPLAY RECOMMENDATIONS
if (ranked.length === 0) {
dv.paragraph(
"πΈ Everything is finished! Enjoy a well-earned break."
);
} else {
const best = ranked[0];
dv.header(3, "β¨ Your recommended next task");
dv.paragraph(best.page.file.link);
dv.paragraph(
"**" + best.reason + "**" +
" Β· " +
best.priority.charAt(0).toUpperCase() +
best.priority.slice(1) +
" priority"
);
dv.paragraph(
"πΏ **Your first five minutes:** " +
firstStep(best)
);
if (ranked.length > 1) {
dv.header(3, "π· Other options");
dv.table(
["Assignment", "Why it's here"],
ranked.slice(1, 3).map(item => [
item.page.file.link,
item.reason
])
);
}
}
β What Do You Have Energy For?
A little progress is still progress. Choose what feels manageable today. πΏ
// ==========================================
// πΏ STUDY NOOK: COZY ENERGY SELECTOR
// ==========================================
// πΈ Read existing assignments
const assignments = dv.pages(
'"study nook/School/Assignments"'
);
const today = moment().startOf("day");
// π± Date helper
function parseDate(value) {
if (!value) return null;
const raw = typeof value.toISODate === "function"
? value.toISODate()
: String(value);
const date = moment(
raw,
["YYYY-MM-DD", "MM/DD/YY", "MM/DD/YYYY"],
true
);
return date.isValid() ? date : null;
}
// π· Completion helper
function isComplete(page) {
const status = String(page.status ?? "")
.trim().toLowerCase();
return ["done", "completed", "complete"]
.includes(status) || page.completed === true;
}
// β³ Read estimates in minutes
function getEstimate(page) {
const value = page.estimate;
if (value === undefined || value === null) {
return null;
}
const n = Number(value);
return Number.isFinite(n) && n > 0 ? n : null;
}
// π
Check scheduled workdays
function scheduledToday(page) {
if (!page.work_days) return false;
const values = typeof page.work_days.array === "function"
? page.work_days.array()
: Array.isArray(page.work_days)
? page.work_days
: [page.work_days];
return values.some(value => {
const date = parseDate(value);
return date &&
date.format("YYYY-MM-DD") ===
today.format("YYYY-MM-DD");
});
}
// πΏ Give every assignment an urgency score
function score(page) {
let points = 0;
const due = parseDate(page.due);
if (due) {
const days = due.startOf("day").diff(today, "days");
if (days < 0) points += 100;
else if (days === 0) points += 85;
else if (days === 1) points += 65;
else if (days <= 3) points += 40;
else if (days <= 7) points += 20;
}
if (scheduledToday(page)) points += 55;
const priority = String(page.priority ?? "")
.toLowerCase();
if (priority === "high") points += 25;
if (priority === "medium") points += 10;
if (String(page.status ?? "").toLowerCase()
=== "in progress") {
points += 15;
}
return points;
}
// π All unfinished assignments
const active = assignments
.where(p => !isComplete(p))
.array()
.sort((a, b) => score(b) - score(a));
// ==========================================
// πΈ INTERACTIVE MENU
// ==========================================
const root = dv.container.createDiv();
root.style.padding = "16px";
root.style.border = "1px solid var(--background-modifier-border)";
root.style.borderRadius = "12px";
const menu = root.createDiv();
menu.style.display = "flex";
menu.style.flexWrap = "wrap";
menu.style.gap = "8px";
menu.style.marginBottom = "16px";
const result = root.createDiv();
let currentMode = "15";
let currentIndex = 0;
// π± Recommend a five-minute step
function firstStep(page) {
const name = (
page.file.name + " " +
String(page.class ?? "")
).toLowerCase();
if (/statistic|probability/.test(name)) {
return "Read one problem and write down the given information.";
}
if (/static|engr 210/.test(name)) {
return "Open one problem and sketch its free-body diagram.";
}
if (/calc|math/.test(name)) {
return "Read the next problem and identify the formula you'll need.";
}
if (/matlab|me 273|coding/.test(name)) {
return "Open MATLAB and identify one small piece of code to work on.";
}
if (/dino|solidworks|design|me 287/.test(name)) {
return "Open your project and choose one tiny design or documentation task.";
}
return "Open the assignment and identify the smallest next action.";
}
// β Filter by available time
function choicesFor(mode) {
if (mode === "5") {
return active;
}
if (mode === "15") {
// Prefer smaller assignments when available.
const short = active.filter(p => {
const estimate = getEstimate(p);
return estimate !== null && estimate <= 30;
});
return short.length ? short : active;
}
// Focus mode: prefer more substantial assignments.
const long = active.filter(p => {
const estimate = getEstimate(p);
return estimate !== null && estimate >= 45;
});
return long.length ? long : active;
}
// π· Create a mode button
function addMode(label, mode) {
const button = menu.createEl("button", {
text: label
});
button.style.cursor = "pointer";
button.addEventListener("click", () => {
currentMode = mode;
currentIndex = 0;
render();
});
return {button, mode};
}
const buttons = [
addMode("π± Just 5 minutes", "5"),
addMode("β I have 15 minutes", "15"),
addMode("π Ready to focus", "focus")
];
// ==========================================
// π―οΈ DISPLAY SELECTED TASK
// ==========================================
function render() {
result.replaceChildren();
buttons.forEach(({button, mode}) => {
const selected = mode === currentMode;
button.style.background = selected
? "var(--interactive-accent)"
: "var(--background-secondary)";
button.style.color = selected
? "var(--text-on-accent)"
: "var(--text-normal)";
});
const options = choicesFor(currentMode);
if (!options.length) {
result.createEl("p", {
text: "πΈ No unfinished assignments! Enjoy your break."
});
return;
}
currentIndex = currentIndex % options.length;
const page = options[currentIndex];
const heading = currentMode === "5"
? "π± Your tiny first step"
: currentMode === "15"
? "β Your mini study session"
: "π Your focus session";
result.createEl("h4", {text: heading});
const link = result.createEl("a", {
text: page.file.name
});
link.style.cursor = "pointer";
link.style.fontWeight = "600";
link.addEventListener("click", async () => {
const file = app.vault.getAbstractFileByPath(
page.file.path
);
if (file) {
await app.workspace.getLeaf(false).openFile(file);
}
});
const estimate = getEstimate(page);
const due = parseDate(page.due);
let details = [];
if (estimate !== null) {
details.push("β³ " + estimate + " min estimated");
}
if (due) {
details.push("π
Due " + due.format("MMM D"));
}
if (scheduledToday(page)) {
details.push("πΏ Scheduled today");
}
result.createEl("p", {
text: details.join(" Β· ") || "π· Ready when you are"
});
let guidance = "";
if (currentMode === "5") {
guidance = firstStep(page);
} else if (currentMode === "15") {
guidance =
"Set a 15-minute timer. " +
firstStep(page) +
" Keep going on that one small part until your timer ends.";
} else {
guidance =
"Give this assignment a focused work block. " +
firstStep(page) +
" Then continue through the next manageable section.";
}
const guidanceBox = result.createDiv();
guidanceBox.style.padding = "12px";
guidanceBox.style.background =
"var(--background-secondary)";
guidanceBox.style.borderRadius = "8px";
guidanceBox.style.marginBottom = "12px";
guidanceBox.createEl("strong", {
text: "πΏ Your next step"
});
guidanceBox.createEl("p", {
text: guidance
});
// πΈ Pick an alternative
if (options.length > 1) {
const another = result.createEl("button", {
text: "πΈ Pick Something Else"
});
another.addEventListener("click", () => {
currentIndex = (currentIndex + 1) % options.length;
render();
});
}
result.createEl("p", {
text: "π―οΈ A little progress is still progress."
}).style.color = "var(--text-muted)";
}
// Start with the cozy 15-minute option
render();
πͺ· This Week at a Glance
// πΏ CURRENT WEEK
const today = dv.date("today");
const monday = today.minus({ days: today.weekday - 1 });
const sunday = monday.plus({ days: 6 });
dv.paragraph(
"ποΈ " +
monday.toFormat("MMMM d") +
" β " +
sunday.toFormat("MMMM d, yyyy")
);
// πΈ FIND THIS WEEK'S PLAN
const plans = dv.pages('"study nook/School/Weekly Plans"')
.where(p => {
if (!p.week_start) return false;
const date = dv.date(p.week_start);
return date &&
date.toISODate() === monday.toISODate();
});
const currentPlan = plans.first();
// π± THREE PRIORITIES
dv.header(3, "π± Three priorities");
if (!currentPlan) {
dv.paragraph("π± No weekly plan created for this week yet.");
} else {
const content = await dv.io.load(currentPlan.file.path);
let priorities = [];
if (content) {
const lines = content.split(/\r?\n/);
let inPriorities = false;
for (const line of lines) {
const heading = line.match(/^(#{1,6})\s+(.+)$/);
if (heading) {
// Recognizes headings regardless of their emoji
const headingText = heading[2]
.replace(/^.*?(?=Three priorities)/i, "")
.replace(/#+\s*$/, "")
.trim()
.toLowerCase();
if (headingText === "three priorities") {
inPriorities = true;
continue;
}
if (inPriorities) break;
}
if (inPriorities) {
const item = line.match(/^\s*\d+[.)]\s+(.+)$/);
if (item && item[1].trim()) {
priorities.push(item[1].trim());
}
}
}
}
if (priorities.length) {
dv.list(priorities.slice(0, 3));
} else {
dv.paragraph("π· No priorities added to this week's plan yet.");
}
}
// π ASSIGNMENTS DUE THIS WEEK
const assignments = dv.pages('"study nook/School/Assignments"')
.where(a => a.due)
.where(a => {
const due = dv.date(a.due);
return due &&
due >= monday &&
due < sunday.plus({ days: 1 });
})
.sort(a => a.due, "asc");
function isComplete(a) {
const status = String(a.status ?? "")
.trim()
.toLowerCase();
return ["done", "completed", "complete"].includes(status) ||
a.completed === true;
}
const completed = assignments.where(a => isComplete(a)).length;
const total = assignments.length;
const percent = total
? Math.round((completed / total) * 100)
: 0;
// π ASSIGNMENT PROGRESS
dv.header(3, "π Assignment progress");
dv.paragraph(
`**${completed} of ${total} completed (${percent}%)**`
);
const bar = dv.el("div", "");
bar.style.width = "100%";
bar.style.height = "16px";
bar.style.background = "#d1cbb9";
bar.style.borderRadius = "10px";
bar.style.overflow = "hidden";
const fill = bar.createEl("div");
fill.style.width = percent + "%";
fill.style.height = "100%";
fill.style.background = "#81977d";
fill.style.borderRadius = "10px";
// β° DEADLINES THIS WEEK
dv.header(3, "β° Deadlines this week");
if (total === 0) {
dv.paragraph("πΏ No assignments due this week!");
} else {
dv.table(
["Assignment", "Due", "Status"],
assignments.map(a => [
a.file.link,
dv.date(a.due).toFormat("MMM d"),
isComplete(a)
? "β
Done"
: (a.status ?? "Not started")
])
);
}
π―οΈ Today's focus
- Must-do: write one clear next action here
- Study: choose one concept to practice
- Small win: do a 10-minute starter task
π Due today
TABLE WITHOUT ID
file.link AS "Assignment",
class AS "Class",
priority AS "Priority",
status AS "Status",
round(estimate / 60, 1) AS "Hours"
FROM "study nook/School/Assignments"
WHERE type = "assignment"
AND due = date(today)
AND status != "Done"
SORT priority ASC
β³ Due in the next 7 days
TABLE WITHOUT ID
file.link AS "Assignment",
class AS "Class",
due AS "Due",
priority AS "Priority",
status AS "Status"
FROM "study nook/School/Assignments"
WHERE type = "assignment"
AND due >= date(today)
AND due <= date(today) + dur(7 days)
AND status != "Done"
SORT due ASC
π± Progress by class
TABLE WITHOUT ID
key AS "Class",
length(rows) AS "Assignments",
length(filter(rows, (r) => r.status = "Done")) AS "Done",
length(filter(rows, (r) => r.status != "Done")) AS "Remaining",
round(100 * length(filter(rows, (r) => r.status = "Done")) / length(rows), 0) + "%" AS "Complete"
FROM "study nook/School/Assignments"
WHERE type = "assignment"
GROUP BY class
SORT key ASC
π All upcoming assignments
TABLE WITHOUT ID
file.link AS "Assignment",
class AS "Class",
due AS "Due",
priority AS "Priority",
status AS "Status",
round(estimate / 60, 1) AS "Hours"
FROM "study nook/School/Assignments"
SORT due ASC
π΅οΈPast due Assignments
β° Past Due Assignments
πΏ Let's catch up on anything that slipped past its deadline.
TABLE WITHOUT ID
file.link AS "Assignment",
class AS "Class",
due AS "Due",
status AS "Status"
FROM "study nook/School/Assignments"
WHERE due
AND date(due) < date(today)
AND lower(string(status)) != "done"
AND lower(string(status)) != "completed"
AND lower(string(status)) != "complete"
AND completed != true
SORT due ASC
πCompleted Assignments
β Completed Assignments
πΏ All finished homework, safely tucked away!
TABLE WITHOUT ID
file.link AS "Assignment",
due AS "Due",
status AS "Status"
FROM "study nook/School/Assignments" OR "study nook/School/Done"
WHERE lower(string(status)) = "done"
OR lower(string(status)) = "completed"
OR lower(string(status)) = "complete"
OR completed = true
SORT due DESC
πͺ» Weekly reset
// ======================================
// πΈ STUDY NOOK: SUNDAY RESET
// ======================================
// πΏ Find this week and next week
const today = dv.date("today");
const monday = today.minus({
days: today.weekday - 1
});
const sunday = monday.plus({ days: 6 });
const nextMonday = monday.plus({ days: 7 });
const nextSunday = nextMonday.plus({ days: 6 });
dv.paragraph(
"β **Week of " +
monday.toFormat("MMMM d") +
" β " +
sunday.toFormat("MMMM d, yyyy") +
"**"
);
dv.paragraph(
"*A gentle moment to celebrate your progress and prepare for what comes next.*"
);
// π± Get assignment information
const assignments = dv.pages(
'"study nook/School/Assignments"'
);
// Convert dates safely
function dateKey(value) {
if (!value) return "";
try {
const parsed = dv.date(value);
return parsed ? parsed.toISODate() : "";
} catch {
return "";
}
}
// Check completion
function isComplete(page) {
const status = String(page.status ?? "")
.trim()
.toLowerCase();
return [
"done",
"complete",
"completed"
].includes(status) || page.completed === true;
}
const start = monday.toISODate();
const end = sunday.toISODate();
// ======================================
// πΈ CELEBRATE YOUR PROGRESS
// ======================================
dv.header(3, "πΈ Celebrate your progress");
// Assignments due this week that are done
const finished = assignments
.where(p => {
const due = dateKey(p.due);
return isComplete(p) &&
due >= start && due <= end;
})
.sort(p => p.due, "asc");
if (finished.length > 0) {
dv.paragraph(
"πΏ You completed **" +
finished.length +
"** assignment(s) due this week!"
);
dv.list(
finished.map(p => p.file.link)
);
} else {
dv.paragraph(
"π· No completed assignments due this week are recorded yet. Every little step still counts!"
);
}
// ======================================
// π THINGS TO CARRY FORWARD
// ======================================
dv.header(3, "π Things to carry forward");
// Unfinished assignments due by Sunday
const unfinished = assignments
.where(p => {
const due = dateKey(p.due);
return due &&
due <= end &&
!isComplete(p);
})
.sort(p => p.due, "asc");
if (unfinished.length > 0) {
dv.paragraph(
"These assignments are still unfinished and may need attention:"
);
dv.table(
["Assignment", "Due", "Status"],
unfinished.slice(0, 8).map(p => [
p.file.link,
dv.date(p.due).toFormat("MMM d"),
p.status ?? "Not started"
])
);
if (unfinished.length > 8) {
dv.paragraph(
"πΏ " + (unfinished.length - 8) +
" more unfinished assignments. See your Past Due section for the full list."
);
}
} else {
dv.paragraph(
"πΈ Nothing unfinished with a deadline through this Sunday. Beautiful!"
);
}
// ======================================
// π± PREPARE FOR NEXT WEEK
// ======================================
dv.header(3, "π± Prepare for next week");
dv.paragraph(
"π
**" +
nextMonday.toFormat("MMMM d") +
" β " +
nextSunday.toFormat("MMMM d, yyyy") +
"**"
);
// Find next week's plan
const plans = dv.pages(
'"study nook/School/Weekly Plans"'
);
const nextPlan = plans.find(p =>
dateKey(p.week_start ?? p.week) ===
nextMonday.toISODate()
);
if (nextPlan) {
dv.paragraph(
"πΏ Your next weekly plan is ready!"
);
dv.paragraph(nextPlan.file.link);
} else {
dv.paragraph(
"π· No plan for next week yet. Use your **New Weekly Plan** button above to create one!"
);
}
// ======================================
// β GENTLE RESET CHECKLIST
// ======================================
dv.header(3, "β Your Sunday ritual");
dv.paragraph(
"πΈ **1. Celebrate:** Look at what you finished."
);
dv.paragraph(
"π **2. Carry over:** Decide which unfinished tasks need attention."
);
dv.paragraph(
"π **3. Plan:** Open next week's plan and choose three priorities."
);
dv.paragraph(
"π
**4. Schedule:** Give your homework realistic workdays."
);
dv.paragraph(
"πͺ· **5. Rest:** Close your planner and enjoy your evening."
);
dv.paragraph(
"*You don't have to finish everything to have made progress this week. π*"
);
π§Ί Unfinished tasks
Tasks plugin query; task dates use Obsidian Tasks syntax, e.g. π
2026-10-26.
not done
path includes School
sort by due