Skip to content

Add Mood Tracker extension#17428

Merged
raycastbot merged 4 commits intoraycast:mainfrom
vpukhanov:ext/mood
Mar 8, 2025
Merged

Add Mood Tracker extension#17428
raycastbot merged 4 commits intoraycast:mainfrom
vpukhanov:ext/mood

Conversation

@vpukhanov
Copy link
Contributor

@vpukhanov vpukhanov commented Feb 28, 2025

Description

Mood Tracker extension helps users monitor their emotional well-being by providing a simple and efficient way to record, track, and analyze your moods over time.

It includes actions that let user record their mood, view and search the list of recordings and view glanceable charts of mood trend and distribution over a specific timeframe.

It also includes a tool that lets Raycast AI work with the data and provide additional insights (I found this especially fun).

Screenshots

mood-1
mood-3
mood-2
mood-4

Checklist

- docs: update metadata
- refactor: remove unused code
- feat: add moods ai tool
- chore: rename dashboard -> charts
- refactor: add a moods keys array
- refactor: run prettier
- refactor: extract svg charts into a separate module
- refactor: extract data functions
- feat: implement dashboard view
- feat: implement mood search
- feat: implement mood recording
- feat: initial commit
@raycastbot
Copy link
Collaborator

Congratulations on your new Raycast extension! 🚀

Due to our current reduced availability, the initial review may take up to 10-15 business days

Once the PR is approved and merged, the extension will be available on our Store.

@raycastbot raycastbot added the new extension Label for PRs with new extensions label Feb 28, 2025
@vpukhanov vpukhanov changed the title Add mood extension Add Mood Tracker extension Feb 28, 2025
@pernielsentikaer pernielsentikaer self-assigned this Mar 5, 2025
@pernielsentikaer
Copy link
Collaborator

@greptileai can you help check this

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR adds a comprehensive Mood Tracker extension that enables users to record, search, and visualize their emotional well-being with AI-powered insights.

💡 (1/5) You can manually trigger the bot by mentioning @greptileai in a comment!

13 file(s) reviewed, 13 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines +18 to +19
const chartWidth = width - margin.right;
const chartHeight = height - margin.top - margin.bottom;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: margin.left not accounted for in chartWidth calculation, could cause alignment issues

Suggested change
const chartWidth = width - margin.right;
const chartHeight = height - margin.top - margin.bottom;
const chartWidth = width - margin.right - margin.left;
const chartHeight = height - margin.top - margin.bottom;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This chart actually doesn't have a left margin. But I've made an additional pass on margins and now actually removed most of them, only adding the ones necessary for the labels


// Calculate points for sparkline
const points = values.map((value, index) => {
const x = (index / (values.length - 1 || 1)) * chartWidth;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: division by zero possible if values.length is 1 (|| 1 only helps if length is 0)

Suggested change
const x = (index / (values.length - 1 || 1)) * chartWidth;
const x = (index / Math.max(1, values.length - 1)) * chartWidth;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, actually if length is 1, then length - 1 is 0 that gets replaced with || 1. But Math.max is more readable, I've adopted it. Thanks

Comment on lines +171 to +176
return svgToBase64(`
<svg width="${width}" height="${height + 30}" xmlns="http://www.w3.org/2000/svg">
${xAxis}
${bars}
</svg>
`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: height + 30 adjustment not accounted for in y-coordinate calculations

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was strictly for the labels, but I agree it's confusing, removed

Comment on lines +43 to +45
if (fs.existsSync(ENTRIES_FILE)) {
const data = await fs.promises.readFile(ENTRIES_FILE, "utf-8");
let entries = JSON.parse(data) as MoodEntry[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: fs operations should be wrapped in try/catch to handle potential file system errors gracefully

Suggested change
if (fs.existsSync(ENTRIES_FILE)) {
const data = await fs.promises.readFile(ENTRIES_FILE, "utf-8");
let entries = JSON.parse(data) as MoodEntry[];
if (fs.existsSync(ENTRIES_FILE)) {
try {
const data = await fs.promises.readFile(ENTRIES_FILE, "utf-8");
let entries = JSON.parse(data) as MoodEntry[];
} catch (error) {
console.error('Failed to load entries:', error);
return [];
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually wrap these calls at the place of use, where more context can be provided to the user. Everywhere loadEntries/saveEntries is called, they are wrapped in try/catch

/**
* Returns the list of user's moods with timestamp, notes and tags
*/
export default async function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Function should have a descriptive name like getMoods instead of being anonymous

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was actually generated by Raycast when I added a new tool using the "Manage Extensions" action. Might need to edit the template if that's a problem 😄 Fixed it manually here, though I don't see the need for a descriptive name here personally.

Comment on lines +13 to +21
try {
const newEntry: MoodEntry = {
id: crypto.randomUUID(),
mood: values.mood,
tags: values.tags,
notes: values.notes,
date: values.date || new Date(),
};

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: loadEntries() could throw an error but is outside the try block. Move the loadEntries() call inside the try block to properly catch any file system errors

Suggested change
try {
const newEntry: MoodEntry = {
id: crypto.randomUUID(),
mood: values.mood,
tags: values.tags,
notes: values.notes,
date: values.date || new Date(),
};
try {
const entries = await loadEntries();
const newEntry: MoodEntry = {
id: crypto.randomUUID(),
mood: values.mood,
tags: values.tags,
notes: values.notes,
date: values.date || new Date(),
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a hallucination, loadEntries already is inside this try/catch block

Comment on lines +33 to +35
// Generate charts
const sparklineSvg = generateSparkline(filteredEntries);
const distributionSvg = generateMoodDistribution(filteredEntries);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Chart generation on every render could impact performance with large datasets. Consider using useMemo for sparklineSvg and distributionSvg

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@vpukhanov
Copy link
Contributor Author

@pernielsentikaer done with the initial batch of comments except for #17428 (comment) because I'm honestly not sure what is being suggested

Copy link
Collaborator

@pernielsentikaer pernielsentikaer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi 👋

Looks good to me, approved 🔥

@raycastbot raycastbot merged commit dcb9960 into raycast:main Mar 8, 2025
2 checks passed
@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2025

Published to the Raycast Store:
https://raycast.com/vyacheslav/mood

@raycastbot
Copy link
Collaborator

🎉 🎉 🎉

We've rewarded your Raycast account with some credits. You will soon be able to exchange them for some swag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new extension Label for PRs with new extensions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants